/*
 *	DDNS v3 Client
 *
 *		Author:		Alan Yates <alany@ay.com.au>
 *		Version:	$Id: ctx.c,v 1.1.1.1 2002/07/19 11:47:20 alany Exp $
 */
#include "ctx.h"
#include "sockio.h"
#include "auth.h"
#ifdef WIN32
#include <windows.h>
#else
#include <unistd.h>
#endif
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#ifdef _DEBUG
	#define DEBUG(x) x
#else
	#define DEBUG(x)
#endif /* _DEBUG */

static int
ctxsend(struct ddns3_ctx *c, char *cmd) { 
	int ret;

	ret = sprintf(c->buf, "%s\r\n", cmd);
	ret = ddns3_sockio_write(c->sock, c->buf, ret);
	if(ret < 0) return -1;
	DEBUG( printf(">>> %s", c->buf); )
	return 0;
}

static int
recvline(struct ddns3_ctx *c) {
	int ret;

	/* FIXME: we expect atomic line reception, fairly evil stuff */
	ret = ddns3_sockio_read(c->sock, c->buf, DDNS3_BUF);
	if(ret < 0) return -1;
	c->buf[ret] = 0;
	DEBUG( printf("<<< %s", c->buf); )
	if(!strncmp(c->buf, "+OK",  3)) return T_OK;
	if(!strncmp(c->buf, "-ERR", 4)) return T_ERR;
	if(!strncmp(c->buf, "@RET", 4)) return T_RET;
	if(!strncmp(c->buf, "DDNS", 4)) return T_DDNS;
	if(!strncmp(&(c->buf[ret-3]), ".\r\n", 3)) return T_DOT;
	return -1;
}

static int
recvlist(struct ddns3_ctx *c) {
	int ret, i = 0;
	char *p1, *p2, **lp = 0;
	char handle[128], ip[16];

	if(c->list) {
		for(i = 0; c->list[i]; i++) free(c->list[i]);
		free(c->list);
		c->list = 0;
	}

	/* FIXME: we excpect atomic list reception, evil! */
	ret = recvline(c);
	if(ret != T_DOT) return -1;
	c->buf[strlen(c->buf) - 3] = 0;

	/* parse the list we get back, yuck */
	i = 0;
	p2 = c->buf;
	lp = (char **) realloc(lp, (i + 1) * sizeof(char *));
	while((p1 = strtok(p2, "\n"))) {
		i += 2;
		lp = (char **) realloc(lp, (i + 1) * sizeof(char *));
		sscanf(p1, "%s%s", handle, ip);
		lp[i-2] = strdup(handle);
		lp[i-1] = strdup(ip);
		lp[i] = 0;
		p2 = 0;
	}
	c->list = lp;

	return 0;
}



int
ddns3_ctx_new(struct ddns3_ctx **c, char *host, int port) {

	/* allocate context */
	*c = (struct ddns3_ctx *) calloc(1, sizeof(struct ddns3_ctx));
	if(!(*c)) {
		perror("calloc()");
		return -1;
	}

	/* set URL */
	sprintf((*c)->buf, "%s:%hd", host, (short) port);
	(*c)->url = (char *) strdup((*c)->buf);

	/* initialize sockets */
	ddns3_sockio_init();

	return 0;
}

int
ddns3_ctx_del(struct ddns3_ctx **c) {
	int i = 0;
	char *p;

	/* just in case */
	ddns3_sockio_close((*c)->sock);

	if((*c)->url) free((*c)->url);
	if((*c)->hello) free((*c)->hello);

	if((*c)->list) {
		for(p = (*c)->list[i]; p; p = (*c)->list[++i]) 
			free(p);
		free((*c)->list);
	}

	free(*c);
	*c = 0;

	/* clean up socket library */
	ddns3_sockio_cleanup();

	return 0;
}

int
ddns3_ctx_connect(struct ddns3_ctx *c) {
	int ret;
	
	/* connect to server */
	DEBUG( printf("connect(%s)... ", c->url); )
	ret = ddns3_sockio_connect(c->url);
	if(ret < 0) return -1;
	c->sock = ret;
	DEBUG( printf("OK\n"); )
	
	/* grab opening header */
	ret = recvline(c);
	if(ret != T_DDNS) return -1;
	c->hello = strdup(c->buf);

	return 0;
}

int
ddns3_ctx_disconnect(struct ddns3_ctx *c) {
	int ret;
	
	/* QUIT */
	ret = ctxsend(c, "QUIT");
	if(ret < 0) return -1;

	/* reply */
	ret = recvline(c);
	if(ret != T_OK) return -1;

	/* close socket */
	ddns3_sockio_close(c->sock);
	return 0;
}

int
ddns3_ctx_login(struct ddns3_ctx *c, char *auth, char *user, char *passwd) {
	int ret;
	char buf[4096];

	/* build challenge command */
	ret = ddns3_auth_makechallenge(c, auth, user, passwd);
	if(ret < 0) return -1;

	/* LOGIN */
	strcpy(buf, c->buf);
	ret = ctxsend(c, buf);
	if(ret < 0) return -1;

	/* reply */
	ret = recvline(c);
	if(ret != T_OK) return -1;

	return 0;
}

int
ddns3_ctx_logout(struct ddns3_ctx *c) {
	int ret;
	
	/* LOGOUT */
	ret = ctxsend(c, "LOGOUT");
	if(ret < 0) return -1;

	/* reply */
	ret = recvline(c);
	if(ret != T_OK) return -1;

	return 0;
}

int
ddns3_ctx_list(struct ddns3_ctx *c) {
	int ret;
	
	/* LIST */
	ret = ctxsend(c, "LIST");
	if(ret < 0) return -1;

	/* get returned value */
	ret = recvline(c);
	if(ret != T_RET) return -1;

	ret = recvlist(c);
	if(ret < 0) return -1;

	return 0;
}

int
ddns3_ctx_set(struct ddns3_ctx *c, char *handle, char *ip) {
	int ret;
	char buf[4096];
	
	/* SET */
	ret = sprintf(buf, "SET %s %s", handle, ip);
	ret = ctxsend(c, buf);
	if(ret < 0) return -1;

	/* reply */
	ret = recvline(c);
	if(ret != T_OK) return -1;

	return 0;
}

int
ddns3_ctx_guess(struct ddns3_ctx *c, char *handle, int type) {
	int ret, i = 0;
	char buf[4096], ipbuf[32], *p, *q;
	
	/* SET */
	switch(type) {
		case GUESS_LOCAL:
			ret = ddns3_sockio_getlocalip(c->sock, ipbuf, 32);
			break;
		case GUESS_REMOTE:
			if(!(p = strstr(c->hello, "sourceIP{"))) return -1;
			if(!(q = strstr(p, "}"))) return -1;
			for(p += 9; p < q; p++) ipbuf[i++] = *p;
			ipbuf[i] = 0;
			break;
		default:
			return -1;
	}

	ret = sprintf(buf, "SET %s %s", handle, ipbuf);
	ret = ctxsend(c, buf);
	if(ret < 0) return -1;

	/* reply */
	ret = recvline(c);
	if(ret != T_OK) return -1;

	return 0;
}
