/*
** iksemel (XML Parser for Jabber)
** Copyright (C) 2000-2001 Gurer Ozen <palpa@jabber.org>
**
** This code is free software; you can redistribute it and/or
** modify it under the terms of GNU Lesser General Public License.
**
** network and file io
*/

#include "common.h"
#include "iksemel.h"

#include <proto/exec.h>
#ifdef __MORPHOS__
#include <ppcinline/socket.h>
#else
#include <inline/socket.h>
#endif
#include <netdb.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <amitcp/socketbasetags.h>
#include <errno.h>

extern struct Library *SocketBase;

struct iksconn
{
	char *server;
	char *buf;
	int state;
	long sock;
};


int iks_connect_tcp(iksparser *prs, char *server, int port)
{
	struct hostent *host;
	struct sockaddr_in sin;
	struct iksconn *j;
	long tmp = 1;

	if(!prs) return(0);
	if(prs->conn)
	{
		iks_disconnect(prs);
		return 0;
	}
	if(!server) return(0);

	j = malloc(sizeof(struct iksconn));
	if(!j) return(0);
	memset(j, 0, sizeof(struct iksconn));
	j->sock = -1;
	prs->conn = j;

	j->buf = malloc(4096);
	if(!j->buf)
	{
		iks_disconnect(prs);
		return 0;
	}

	j->server = strdup(server);
	if(!j->server)
	{
		iks_disconnect(prs);
		return 0;
	}

	if(port == 0) port = IKS_JABBER_PORT;

	host = gethostbyname(server);
	if(!host)
	{
		iks_disconnect(prs);
		return 0;
	}
	memcpy(&sin.sin_addr, host->h_addr, host->h_length);
	sin.sin_family = host->h_addrtype;
	sin.sin_port = port;
	sin.sin_len = sizeof(sin);

	j->sock = socket(host->h_addrtype, SOCK_STREAM, 0);
	if(j->sock < 0)
	{
		iks_disconnect(prs);
		return 0;
	}
	IoctlSocket(j->sock, FIOASYNC, (char *)&tmp);
	IoctlSocket(j->sock, FIONBIO, (char *)&tmp);

	tmp = connect(j->sock, (struct sockaddr *)&sin, sizeof(struct sockaddr_in));
	if(tmp == -1 && Errno() != EINPROGRESS)
	{
		iks_disconnect(prs);
		return 0;
	}

	return 1;
}


char *iks_myip(iksparser *prs)
{
	static char buf[20];
	u_char *ip;
	struct sockaddr_in sin;
	long len;

	if(!prs || !prs->conn || prs->conn->sock == -1)
		return "localhost";

	len = sizeof(sin);
	getsockname(prs->conn->sock, (struct sockaddr *)&sin, &len);
	ip = (char *)&sin.sin_addr;

	sprintf(buf, "%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]);
	return buf;
}


int iks_recv(iksparser *prs, int timeout)
{
	struct iksconn *j;
	struct fd_set fds;
	struct timeval tv;

	if(!prs || !prs->conn) return(0);

	j = prs->conn;

	tv.tv_sec = 0;
	tv.tv_usec = 0;

	switch(j->state)
	{
	case 0:
		FD_ZERO(&fds);
		FD_SET(j->sock, &fds);
		if(WaitSelect(j->sock + 1, NULL, &fds, NULL, &tv, 0) == 1)
		{
			long tmp = 0;
			IoctlSocket(j->sock, FIONBIO, (char *)&tmp);
			j->state = 1;
			{
				iks *x;
				char *a, *b;
				if(!iks_send_raw(prs, "<?xml version='1.0'?>")) return(0);
				x = iks_make_header(j->server, IKS_NS_CLIENT);
				a = iks_str_print(iks_string(x->p, x));
				b = strstr(a, "/>");
				*b = '>';
				b++;
				*b = '\0';
				if(!iks_send_raw(prs, a)) return(0);
				iks_delete(x);
			}
		}
		if(j->state == 0) break;

	case 1:
		FD_ZERO(&fds);
		FD_SET(j->sock, &fds);
		tv.tv_sec = timeout;
		/* FIX ME: timeout == -1 blocking */
		while(WaitSelect(j->sock + 1, &fds, NULL, NULL, &tv, 0) > 0)
		{
			int len;
			len = recv(j->sock, j->buf, 4095, 0);
			if(len > 0)
			{
				j->buf[len] = '\0';
				if(prs->jLog) CALL_JLOG(j->buf, IKS_DIR_IN);
				iks_parse(prs, j->buf, len, 0);
			}
			else if(len <= 0)
			{
				return 0;
			}
			FD_ZERO(&fds);
			FD_SET(j->sock, &fds);
		}
	}

	return 1;
}


int iks_send(iksparser *prs, iks *x)
{
	ikstr *s;

	if(!prs || !prs->conn || !x) return(0);

	s = iks_string(x->p, x);
	return iks_send_raw(prs, iks_str_print(s));
}


int iks_send_raw(iksparser *prs, char *data)
{
	if(!prs || !prs->conn || !data) return(0);

	if(send(prs->conn->sock, data, strlen(data), 0) == -1) return(0);
	if(prs->jLog) CALL_JLOG(data, IKS_DIR_OUT);
	return 1;
}


void iks_disconnect(iksparser *prs)
{
	struct iksconn *j;

	if(!prs || !prs->conn) return;

	j = prs->conn;
	if(j->sock >= 0) CloseSocket(j->sock);
	if(j->server) free(j->server);
	if(j->buf) free(j->buf);
	free(j);
	prs->conn = NULL;
}


iks *iks_load(char *fname)
{
	iksparser *prs;
	iks *x = NULL;
	char *buf;
	FILE *f;
	int len, done = 0;

	if(!fname) return(NULL);

	buf = malloc(2048);
	if(buf)
	{
		prs = iks_dom_new(&x);
		if(prs)
		{
			f = fopen(fname, "r");
			if(f)
			{
				while(!done)
				{
					len = fread(buf, 1, 2048, f);
					if(len < 2048) done = 1;
					iks_parse(prs, buf, len, done);
				}
				fclose(f);
			}
			iks_parser_delete(prs);
		}
		free(buf);
	}

	return x;
}


int iks_save(char *fname, iks *x)
{
	FILE *f;
	ikstr *s;
	char *data;
	int ret;

	s = iks_string(NULL, x);
	if(!s) return(0);
	data = iks_str_print(s);
	if(!data) return(0);

	f = fopen(fname, "w");
	if(!f)
	{
		iks_str_delete(s);
		return(0);
	}

	ret = fputs(data, f);
	iks_str_delete(s);
	fclose(f);

	if(ret != 0) return(0);
	return 1;
}
