/*
** 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.
**
** jabber utilities
*/

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

/*****  jabber id  *****/

iksid *iks_id_new(ikspool *p, char *src)
{
	char *tmp;
	iksid *id;

	if(!src) return(NULL);
	if(!p) p = iks_pool_new(512);
	if(!p) return(NULL);

	id = iks_pool_alloc(p, sizeof(iksid));
	if(!id) return(NULL);
	memset(id, 0, sizeof(iksid));
	id->p = p;

	tmp = strchr(src, ':');
	if(tmp) src = ++tmp;

	tmp = strchr(src, '@');
	if(tmp)
	{
		*tmp++ = '\0';
		id->user = iks_pool_strdup(p, src);
		src = tmp;
	}

	tmp = strchr(src, '/');
	if(tmp)
	{
		*tmp++ = '\0';
		id->resource = iks_pool_strdup(p, tmp);
	}

	id->server = iks_pool_strdup(p, src);

	/* FIX ME: implement jid checks here */

	return id;
}


void iks_id_set(iksid *id, char *src, int item)
{
	/* FIX ME */

	id->full = NULL;
}


int iks_id_cmp(iksid *a, iksid *b)
{
	return iks_id_cmpx(a, b, IKS_ID_RESOURCE | IKS_ID_USER | IKS_ID_SERVER);
}


int iks_id_cmpx(iksid *a, iksid *b, int parts)
{
	int diff = 0;

	if(!a || !b) return(IKS_ID_RESOURCE | IKS_ID_USER | IKS_ID_SERVER);

	if(parts & IKS_ID_RESOURCE && !(!a->resource && !b->resource) && iks_strcmp(a->resource, b->resource) != 0)
		diff += IKS_ID_RESOURCE;

	if(parts & IKS_ID_USER && !(!a->user && !b->user) && iks_strcasecmp(a->user, b->user) != 0)
		diff += IKS_ID_USER;

	if(parts & IKS_ID_SERVER && !(!a->server && !b->server) && iks_strcmp(a->server, b->server) != 0)
		diff += IKS_ID_SERVER;

	return diff;
}


char *iks_id_print(iksid *id)
{
	ikstr *s;

	if(!id) return(NULL);

	if(id->full) return(id->full);

	s = iks_str_new(id->p);
	if(!s) return(NULL);

	if(id->user)
	{
		iks_str_add(s, id->user);
		iks_str_add(s, "@");
	}
	iks_str_add(s, id->server);
	if(id->resource)
	{
		iks_str_add(s, "/");
		iks_str_add(s, id->resource);
	}

	id->full = iks_str_print(s);
	return id->full;
}


char *iks_id_printx(iksid *id, int parts)
{
	switch(parts)
	{
	case IKS_ID_USER | IKS_ID_SERVER:
	{
		ikstr *s;
		if(id->partial) return(id->partial);
		s = iks_str_new(id->p);
		if(!s) return(NULL);
		if(id->user)
		{
			iks_str_add(s, id->user);
			iks_str_add(s, "@");
		}
		iks_str_add(s, id->server);
		id->partial = iks_str_print(s);
		return id->partial;
	}

	case IKS_ID_USER:
		return id->user;

	case IKS_ID_SERVER:
		return id->server;

	case IKS_ID_RESOURCE:
		return id->resource;

	case IKS_ID_USER | IKS_ID_SERVER | IKS_ID_RESOURCE:
		return iks_id_print(id);
	}

	return NULL;
}


/*****  iks packet  *****/

ikspak *iks_packet(iks *x)
{
	ikspak *pak;
	ikspool *p;
	char *tmp;

	if(!x) return(NULL);

	p = x->p;
	pak = iks_pool_alloc(p, sizeof(ikspak));
	if(!pak) return(NULL);
	memset(pak, 0, sizeof(ikspak));
	pak->p = p;
	pak->x = x;

	tmp = iks_find_attrib(x, "to");
	if(tmp) pak->to = iks_id_new(p, tmp);
	tmp = iks_find_attrib(x, "from");
	if(tmp) pak->from = iks_id_new(p, tmp);

	tmp = iks_find_attrib(x, "type");
	pak->subtype = IKS_TYPE_NONE;

	if(strcmp(x->name, "message") == 0)
	{
		pak->type = IKS_PAK_MESSAGE;
		if(tmp)
		{
			if(strcmp(tmp, "chat") == 0)
				pak->subtype = IKS_TYPE_CHAT;
			else if(strcmp(tmp, "groupchat") == 0)
				pak->subtype = IKS_TYPE_GROUPCHAT;
			else if(strcmp(tmp, "headline") == 0)
				pak->subtype = IKS_TYPE_HEADLINE;
			else if(strcmp(tmp, "error") == 0)
				pak->subtype = IKS_TYPE_ERROR;
			else
				pak->subtype = IKS_TYPE_UNKNOWN;
		}
	}

	else if(strcmp(x->name, "presence") == 0)
	{
		pak->type = IKS_PAK_S10N;
		if(tmp)
		{
			if(strcmp(tmp, "unavailable") == 0)
			{
				pak->type = IKS_PAK_PRESENCE;
				pak->subtype = IKS_TYPE_UNAVAILABLE;
				pak->show = IKS_SHOW_UNAVAILABLE;
			}
			else if(strcmp(tmp, "probe") == 0)
			{
				pak->type = IKS_PAK_PRESENCE;
				pak->subtype = IKS_TYPE_PROBE;
			}
			else if(strcmp(tmp, "subscribe") == 0)
				pak->subtype = IKS_TYPE_SUBSCRIBE;
			else if(strcmp(tmp, "subscribed") == 0)
				pak->subtype = IKS_TYPE_SUBSCRIBED;
			else if(strcmp(tmp, "unsubscribe") == 0)
				pak->subtype = IKS_TYPE_UNSUBSCRIBE;
			else if(strcmp(tmp, "unsubscribed") == 0)
				pak->subtype = IKS_TYPE_UNSUBSCRIBED;
			else if(strcmp(tmp, "error") == 0)
				pak->subtype = IKS_TYPE_ERROR;
			else
				pak->subtype = IKS_TYPE_UNKNOWN;
		}
		else
		{
			pak->type = IKS_PAK_PRESENCE;
			pak->subtype = IKS_TYPE_AVAILABLE;
			tmp = iks_find_cdata(x, "show");
			pak->show = IKS_SHOW_AVAILABLE;
			if(tmp)
			{
				if(strcmp(tmp, "chat") == 0)
					pak->show = IKS_SHOW_CHAT;
				else if(strcmp(tmp, "away") == 0)
					pak->show = IKS_SHOW_AWAY;
				else if(strcmp(tmp, "xa") == 0)
					pak->show = IKS_SHOW_XA;
				else if(strcmp(tmp, "dnd") == 0)
					pak->show = IKS_SHOW_DND;
			}
		}
	}

	else if(strcmp(x->name, "iq") == 0)
	{
		pak->type = IKS_PAK_IQ;
		if(tmp)
		{
			if(strcmp(tmp, "get") == 0)
				pak->subtype = IKS_TYPE_GET;
			else if(strcmp(tmp, "set") == 0)
				pak->subtype = IKS_TYPE_SET;
			else if(strcmp(tmp, "result") == 0)
				pak->subtype = IKS_TYPE_RESULT;
			else if(strcmp(tmp, "error") == 0)
				pak->subtype = IKS_TYPE_ERROR;
			else
				pak->subtype = IKS_TYPE_UNKNOWN;
		}
		pak->query = iks_child(x);
		pak->ns = iks_find_attrib(pak->query, "xmlns");
	}

	else if(strcmp(x->name, "stream:stream") == 0)
	{
		pak->type = IKS_PAK_STREAM;
	}

	else if(strcmp(x->name, "stream:error") == 0)
	{
		pak->type = IKS_PAK_ERROR;
	}

	return pak;
}


/*****  node creation utils  *****/

iks *iks_make_header(char *to, char *xmlns)
{
	iks *x;

	x = iks_new("stream:stream");
	iks_insert_attrib(x, "xmlns:stream", "http://etherx.jabber.org/streams");
	iks_insert_attrib(x, "xmlns", xmlns);
	iks_insert_attrib(x, "to", to);

	return x;
}


iks *iks_make_auth(iksid *id, char *pass, char *sid)
{
	iks *x, *y;

	x = iks_new("iq");
	iks_insert_attrib(x, "type", "set");
	y = iks_insert(x, "query");
	iks_insert_attrib(y, "xmlns", IKS_NS_AUTH);

	iks_insert_cdata(iks_insert(y, "username"), id->user, -1);
	iks_insert_cdata(iks_insert(y, "resource"), id->resource, -1);

	if(sid)
	{
		char buf[41];
		iksha *sha;
		sha = iks_sha_new();
		iks_sha_hash(sha, sid, strlen(sid), 0);
		iks_sha_hash(sha, pass, strlen(pass), 1);
		iks_sha_print(sha, buf);
		free(sha);
		iks_insert_cdata(iks_insert(y, "digest"), buf, 40);
	}
	else
	{
		iks_insert_cdata(iks_insert(y, "password"), pass, -1);
	}

	return x;
}


iks *iks_make_msg(enum iksubtype type, char *to, char *body, char *thread)
{
	iks *x;
	char *t = NULL;

	x = iks_new("message");
	switch(type)
	{
	case IKS_TYPE_CHAT: t = "chat"; break;
	case IKS_TYPE_GROUPCHAT: t = "groupchat"; break;
	case IKS_TYPE_HEADLINE: t = "headline"; break;
	default: break;
	}
	if(t) iks_insert_attrib(x, "type", t);
	if(to) iks_insert_attrib(x, "to", to);
	if(body) iks_insert_cdata(iks_insert(x, "body"), body, -1);
	if(thread) iks_insert_cdata(iks_insert(x, "thread"), thread, -1);

	return x;
}


iks *iks_make_pres(enum iksubtype type, enum ikshowtype show, char *to, char *status)
{
	iks *x;
	char *t = NULL;

	x = iks_new("presence");
	switch(type)
	{
	case IKS_TYPE_SUBSCRIBE: t = "subscribe"; break;
	case IKS_TYPE_SUBSCRIBED: t = "subscribed"; break;
	case IKS_TYPE_UNSUBSCRIBE: t = "unsubscribe"; break;
	case IKS_TYPE_UNSUBSCRIBED: t = "unsubscribed"; break;
	case IKS_TYPE_PROBE: t = "probe"; break;
	case IKS_TYPE_UNAVAILABLE: t = "unavailable"; break;
	default: break;
	}
	if(t) iks_insert_attrib(x, "type", t);
	if(to) iks_insert_attrib(x, "to", to);
	t = NULL;
	switch(show)
	{
	case IKS_SHOW_CHAT: t = "chat"; break;
	case IKS_SHOW_AWAY: t = "away"; break;
	case IKS_SHOW_XA: t = "xa"; break;
	case IKS_SHOW_DND: t = "dnd"; break;
	default: break;
	}
	if(t) iks_insert_cdata(iks_insert(x, "show"), t, -1);
	if(status) iks_insert_cdata(iks_insert(x, "status"), status, -1);

	return x;
}


iks *iks_make_iq(enum iksubtype type, const char *xmlns)
{
	iks *x;
	char *t = NULL;

	x = iks_new("iq");
	switch(type)
	{
	case IKS_TYPE_GET: t = "get"; break;
	case IKS_TYPE_SET: t = "set"; break;
	case IKS_TYPE_RESULT: t = "result"; break;
	case IKS_TYPE_ERROR: t = "error"; break;
	default: break;
	}
	if(t) iks_insert_attrib(x, "type", t);
	iks_insert_attrib(iks_insert(x, "query"), "xmlns", xmlns);

	return x;
}
