view plugins/yay/libyahoo.c @ 1054:0b0b4cb53c17

[gaim-migrate @ 1064] yahoo prpl committer: Tailor Script <tailor@pidgin.im>
author Eric Warmenhoven <eric@warmenhoven.org>
date Fri, 03 Nov 2000 10:03:53 +0000
parents
children 4416ead31db7
line wrap: on
line source

/*
   Yahoo Pager Client Library

   This code is based on code by Douglas Winslow. The original info from
   his code is listed below. This code has taken his code and has been
   altered to my naming and coding conventions and has been made more
   usable as a library of routines.

   -- Nathan Neulinger <nneul@umr.edu>
 */

/*
   Yahoo Pager Client Emulator Pro - yppro.c
   A basic reference implementation
   Douglas Winslow <douglas@min.net>
   Tue Sep  1 02:28:21 EDT 1998
   Version 2, Revision 2
   Known to compile on Linux 2.0, FreeBSD 2.2, and BSDi 3.0.
   hi to aap bdc drw jfn jrc mm mcd [cejn]b #cz and rootshell

   Finally!
   Yahoo finally patched their server-side, and things will be getting
   back to "normal".  I will continue to maintain this code as long as
   there is interest for it.  Since Yahoo will be discontinuing YPNS1.1
   login support shortly, I've upgraded this client to do YPNS1.2.  You
   *must* have a password to pass authentication to the pager server.
   This authentication is done by a weird HTTP cookie method.

   This code is distributed under the GNU General Public License (GPL)
 */

#include "config.h"
#include <stdio.h>
#include <netdb.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
#if defined(WITH_GTK)
#include <gtk/gtk.h>
#endif
#include <unistd.h>
#if defined(HAVE_STRINGS_H)
#include <strings.h>
#endif
#if defined(HAVE_STRING_H)
#include <string.h>
#endif
#include <ctype.h>
#include "libyahoo.h"
#ifdef HAVE_DMALLOC
#include "dmalloc.h"
#else
#include <stdlib.h>
#endif

#include "memtok.h"

/* allow libyahoo to be used without gtkyahoo's debug support */
#ifdef ENABLE_LIBYAHOO_DEBUG
#include "libyahoo-debug.h"
#else
static void yahoo_dbg_Print(char *tmp, ...)
{
}

#define yahoo_dbg_NullCheck(x) ((x)?(x):("[NULL]"))
#endif

/* remap functions to gtk versions */
#if defined(WITH_GTK)
#define malloc g_malloc
#define free g_free
#define calloc(x,y) g_malloc0((x)*(y))
#endif

#if (!defined(TRUE) || !defined(FALSE))
# define TRUE 1
# define FALSE 0
#endif

/* Define a quick shortcut function to free a pointer and set it to null */
#define FREE(x) if (x) { free(x); x=NULL; }

#if defined(WITH_SOCKS4)
void SOCKSinit(char *argv0);
#endif

/* pager server host */
#define YAHOO_PAGER_HOST "cs.yahoo.com"
#define YAHOO_PAGER_PORT	5050
/* pager server host for http connections */
#define YAHOO_PAGER_HTTP_HOST "http.pager.yahoo.com"
#define YAHOO_PAGER_HTTP_PORT 80
/* authentication/login host */
#define YAHOO_AUTH_HOST		"msg.edit.yahoo.com"
#define YAHOO_AUTH_PORT		80
/* buddy/identity/config host */
#define YAHOO_DATA_HOST		YAHOO_AUTH_HOST
#define YAHOO_DATA_PORT     80
/* Address book host */
#define YAHOO_ADDRESS_HOST "uk.address.yahoo.com"
#define YAHOO_ADDRESS_PORT	80

/* User agent to use for HTTP connections */
/* It needs to have Mozilla/4 in it, otherwise it fails */
#ifndef VERSION
#define VERSION "1.0"
#endif
#define YAHOO_USER_AGENT	"Mozilla/4.6 (libyahoo/" VERSION ")"

#define YAHOO_PROTOCOL_HEADER "YPNS2.0"

/*
 *  Routines and data private to this library, should not be directly
 *  accessed outside of these routines.
 */

/* Service code labels for debugging output */
static struct yahoo_idlabel yahoo_service_codes[] = {
	{YAHOO_SERVICE_LOGON, "Pager Logon"},
	{YAHOO_SERVICE_LOGOFF, "Pager Logoff"},
	{YAHOO_SERVICE_ISAWAY, "Is Away"},
	{YAHOO_SERVICE_ISBACK, "Is Back"},
	{YAHOO_SERVICE_IDLE, "Idle"},
	{YAHOO_SERVICE_MESSAGE, "Message"},
	{YAHOO_SERVICE_IDACT, "Activate Identity"},
	{YAHOO_SERVICE_IDDEACT, "Deactivate Identity"},
	{YAHOO_SERVICE_MAILSTAT, "Mail Status"},
	{YAHOO_SERVICE_USERSTAT, "User Status"},
	{YAHOO_SERVICE_NEWMAIL, "New Mail"},
	{YAHOO_SERVICE_CHATINVITE, "Chat Invitation"},
	{YAHOO_SERVICE_CALENDAR, "Calendar Reminder"},
	{YAHOO_SERVICE_NEWPERSONALMAIL, "New Personals Mail"},
	{YAHOO_SERVICE_NEWCONTACT, "New Friend"},
	{YAHOO_SERVICE_GROUPRENAME, "Group Renamed"},
	{YAHOO_SERVICE_ADDIDENT, "Add Identity"},
	{YAHOO_SERVICE_ADDIGNORE, "Add Ignore"},
	{YAHOO_SERVICE_PING, "Ping"},
	{YAHOO_SERVICE_SYSMESSAGE, "System Message"},
	{YAHOO_SERVICE_CONFINVITE, "Conference Invitation"},
	{YAHOO_SERVICE_CONFLOGON, "Conference Logon"},
	{YAHOO_SERVICE_CONFDECLINE, "Conference Decline"},
	{YAHOO_SERVICE_CONFLOGOFF, "Conference Logoff"},
	{YAHOO_SERVICE_CONFMSG, "Conference Message"},
	{YAHOO_SERVICE_CONFADDINVITE, "Conference Additional Invitation"},
	{YAHOO_SERVICE_CHATLOGON, "Chat Logon"},
	{YAHOO_SERVICE_CHATLOGOFF, "Chat Logoff"},
	{YAHOO_SERVICE_CHATMSG, "Chat Message"},
	{YAHOO_SERVICE_GAMELOGON, "Game Logon"},
	{YAHOO_SERVICE_GAMELOGOFF, "Game Logoff"},
	{YAHOO_SERVICE_FILETRANSFER, "File Transfer"},
	{YAHOO_SERVICE_PASSTHROUGH2, "Passthrough 2"},
	{0, NULL}
};

/* Status codes */
static struct yahoo_idlabel yahoo_status_codes[] = {
	{YAHOO_STATUS_AVAILABLE, "I'm Available"},
	{YAHOO_STATUS_BRB, "Be Right Back"},
	{YAHOO_STATUS_BUSY, "Busy"},
	{YAHOO_STATUS_NOTATHOME, "Not at Home"},
	{YAHOO_STATUS_NOTATDESK, "Not at my Desk"},
	{YAHOO_STATUS_NOTINOFFICE, "Not in the Office"},
	{YAHOO_STATUS_ONPHONE, "On the Phone"},
	{YAHOO_STATUS_ONVACATION, "On Vacation"},
	{YAHOO_STATUS_OUTTOLUNCH, "Out to Lunch"},
	{YAHOO_STATUS_STEPPEDOUT, "Stepped Out"},
	{YAHOO_STATUS_INVISIBLE, "Invisible"},
	{YAHOO_STATUS_IDLE, "Idle"},
	{YAHOO_STATUS_CUSTOM, "Custom Message"},
	{0, NULL}
};

/* Status codes */
static struct yahoo_idlabel yahoo_status_append[] = {
	{YAHOO_STATUS_AVAILABLE, "is now available"},
	{YAHOO_STATUS_BRB, "will be right back"},
	{YAHOO_STATUS_BUSY, "is now busy"},
	{YAHOO_STATUS_NOTATHOME, "is not at home"},
	{YAHOO_STATUS_NOTATDESK, "is not at their desk"},
	{YAHOO_STATUS_NOTINOFFICE, "is not in the office"},
	{YAHOO_STATUS_ONPHONE, "is on the phone"},
	{YAHOO_STATUS_ONVACATION, "is on vacation"},
	{YAHOO_STATUS_OUTTOLUNCH, "is out to lunch"},
	{YAHOO_STATUS_STEPPEDOUT, "has stepped out"},
	{YAHOO_STATUS_INVISIBLE, "is now invisible"},
	{YAHOO_STATUS_IDLE, "is now idle"},
	{YAHOO_STATUS_CUSTOM, ""},
	{0, NULL}
};

/* Take a 4-byte character string in little-endian format and return
   a unsigned integer */
unsigned int yahoo_makeint(unsigned char *data)
{
	if (data)
	{
		return ((data[3] << 24) + (data[2] << 16) + (data[1] << 8) +
			(data[0]));
	}
	return 0;
}

/* Take an integer and store it into a 4 character little-endian string */
static void yahoo_storeint(unsigned char *data, unsigned int val)
{
	unsigned int tmp = val;
	int i;

	if (data)
	{
		for (i = 0; i < 4; i++)
		{
			data[i] = tmp % 256;
			tmp >>= 8;
		}
	}
}

/*
   converts a comma seperated list to an array of strings
   used primarily in conference code

   allocates a string in here -- caller needs to free it
 */
char **yahoo_list2array(char *buff)
{
	char **tmp_array = NULL;
	char *array_elem = NULL;
	char *tmp = NULL;

	char *buffer = 0;
	char *ptr_buffer = 0;

	int sublen = 0;
	int cnt = 0;
	int nxtelem = 0;
	unsigned int i = 0;
	unsigned int len = 0;

	if (0 == buff)
		return 0;

	buffer = strdup(buff);		/* play with a copy */
	ptr_buffer = buffer;

	/* count the number of users (commas + 1) */
	for (i = 0; i < strlen(buffer); i++)
	{
		if (buffer[i] == ',')
		{
			/*
			   if not looking at end of list
			   ( ignore extra pesky comma at end of list)
			 */
			if (i != (strlen(buffer) - 1))
				cnt++;
		}
	}

	/* add one more name than comma .. */
	cnt++;

	/* allocate the array to hold the list of buddys */
	/* null terminated array of pointers */
	tmp_array = (char **) malloc(sizeof(char *) * (cnt + 1));

	memset(tmp_array, 0, (sizeof(char *) * (cnt + 1)));

	/* Parse through the list and get all the entries */
	while ((ptr_buffer[sublen] != ',') && (ptr_buffer[sublen] != '\0'))
		sublen++;
	tmp = (char *) malloc(sizeof(char) * (sublen + 1));

	memcpy(tmp, ptr_buffer, sublen);
	tmp[sublen] = '\0';

	if (ptr_buffer[sublen] != '\0')
		ptr_buffer = &(ptr_buffer[sublen + 1]);
	else
		ptr_buffer = &(ptr_buffer[sublen]);	/* stay at the null char */
	sublen = 0;

	while (tmp && (strcmp(tmp, "") != 0))
	{
		len = strlen(tmp);
		array_elem = (char *) malloc(sizeof(char) * (len + 1));

		strncpy(array_elem, tmp, (len + 1));
		array_elem[len] = '\0';

		tmp_array[nxtelem++] = array_elem;
		array_elem = NULL;

		FREE(tmp);

		while ((ptr_buffer[sublen] != ',') && (ptr_buffer[sublen] != '\0'))
			sublen++;
		tmp = (char *) malloc(sizeof(char) * (sublen + 1));

		memcpy(tmp, ptr_buffer, sublen);
		tmp[sublen] = '\0';

		if (ptr_buffer[sublen] != '\0')
			ptr_buffer = &(ptr_buffer[sublen + 1]);
		else
			ptr_buffer = &(ptr_buffer[sublen]);	/* stay at the null char */

		sublen = 0;
	}
	tmp_array[nxtelem] = NULL;

	FREE(tmp);
	FREE(buffer);
	return (tmp_array);

}								/* yahoo_list2array() */

/*
 Free's the memory associated with an array generated bu yahoo_list2array
 */
void yahoo_arraykill(char **array)
{
	int nxtelem = 0;

	if (NULL == array)
		return;

	while (array[nxtelem] != NULL)
	{
		free(array[nxtelem++]);
	}

	free(array);
}								/* yahoo_arraykill() */

/*
   converts an array of strings to a comma seperated list
   used primarily in conference code

   allocates a string in here.. needs to be freed by caller program
 */
char *yahoo_array2list(char **array)
{
	char *list = NULL;
	int nxtelem = 0;
	int arraylength = 0;

	if (NULL == array)
		return NULL;

	while (array[nxtelem] != NULL)
	{
		arraylength += strlen(array[nxtelem++]);
		arraylength++;			/* comma */
	}

	nxtelem = 0;				/* reset array counter */

	/* allocate at least one - for NULL list - and to
	   allow my strcat to write past the end for the
	   last comma which gets converted to NULL */
	list = (char *) malloc(sizeof(char) * (arraylength + 1));

	memset(list, 0, (arraylength + 1));

	while (array[nxtelem] != NULL)
	{
		strcat(list, array[nxtelem++]);
		strcat(list, ",");
	}
	/*
	   overwrite last ',' with a NULL
	   makes the string end with two null characters, but this way
	   handles empty lists gracefully
	 */
	list[arraylength - 1] = '\0';

	return (list);
}								/* yahoo_array2list() */

/* Free a buddy list */
static void yahoo_free_buddies(struct yahoo_context *ctx)
{
	int i;

	if (!ctx->buddies)
	{
		return;
	}

	i = 0;
	while (ctx->buddies[i])
	{
		FREE(ctx->buddies[i]->group);
		FREE(ctx->buddies[i]->id);
		i++;
	}

	FREE(ctx->buddies);
}

/* Free a identities list */
static void yahoo_free_identities(struct yahoo_context *ctx)
{
	int i;

	if (!ctx->identities)
	{
		return;
	}

	i = 0;
	while (ctx->identities[i])
	{
		FREE(ctx->identities[i]);
		i++;
	}

	FREE(ctx->identities);
	yahoo_dbg_Print("libyahoo", "[libyahoo] yahoo_free_identities: done\n");
}

#if 0							/* not used at the moment */
static void yahoo_hexdump(char *label, unsigned char *data, int datalen)
{
	int i, j;
	int val, skipped_last;
	char current[100];
	char last[100];
	char tmp[15];
	char outline[100];
	static int last_datalen = 0;
	static unsigned char *last_data = NULL;

	if (last_data)
	{
		if (last_datalen == datalen && !memcmp(data, last_data, datalen))
		{
			printf("\n%s: <same as last dump>\n", label);
			return;
		}
		FREE(last_data);
	}

	/* Copy the packet so we can don't duplicate it next time. */
	last_datalen = datalen;
	last_data = (unsigned char *) malloc(datalen);
	memcpy(last_data, data, datalen);

	/* Handle printing the full entry out */
	printf("\n");
	printf("%s:\n", label);

	skipped_last = 0;
	last[0] = 0;
	for (j = 0; j * 16 < datalen; j++)
	{
		current[0] = 0;

		/* Print out in hex */
		for (i = j * 16; i < (j * 16 + 16); i++)
		{
			if (i < datalen)
			{
				val = data[i];
				sprintf(tmp, "%.2X ", val);
			}
			else
			{
				sprintf(tmp, "   ");
			}
			strcat(current, tmp);
		}

		/* Print out in ascii */
		strcat(current, "  ");
		for (i = j * 16; i < (j * 16) + 16; i++)
		{
			if (i < datalen)
			{
				if (isprint(data[i]))
				{
					sprintf(tmp, "%c", data[i]);
				}
				else
				{
					sprintf(tmp, ".");
				}
			}
			else
			{
				sprintf(tmp, " ");
			}
			strcat(current, tmp);
		}

		outline[0] = 0;
		if (!strcmp(current, last))
		{
			if (!skipped_last)
			{
				strcpy(outline, " ....:\n");
			}
			skipped_last = 1;
		}
		else
		{
			sprintf(outline, " %.4d:  %s\n", j * 16, current);
			skipped_last = 0;
		}
		printf("%s", outline);
		strcpy(last, current);
	}

	if (skipped_last)
	{
		printf("%s", outline);
	}
	printf("\n");
}
#endif

static int yahoo_socket_connect(struct yahoo_context *ctx, char *host,
	int port)
{
	struct sockaddr_in serv_addr;
	struct hostent *server;
	int servfd;
	int res;

	yahoo_dbg_Print("libyahoo",
		"[libyahoo] yahoo_socket_connect - starting [%s:%d]\n", host, port);

	if (!ctx || !host || !port)
	{
		yahoo_dbg_Print("libyahoo",
			"[libyahoo] yahoo_socket_connect - nulls\n");
		return 0;
	}

	server = gethostbyname(host);
	if (!server)
	{
		printf("[libyahoo] failed to look up server (%s:%d)\n", host, port);
		return (0);
	}

	servfd = socket(AF_INET, SOCK_STREAM, 0);

	bzero(&serv_addr, sizeof(serv_addr));
	serv_addr.sin_family = AF_INET;
	bcopy(server->h_addr, &serv_addr.sin_addr.s_addr, server->h_length);
	serv_addr.sin_port = htons(port);

	res = -1;
	if (ctx->connect_mode == YAHOO_CONNECT_SOCKS4)
	{
#if defined(WITH_SOCKS4)
		res =
			Rconnect(servfd, (struct sockaddr *) &serv_addr,
			sizeof(serv_addr));
#endif
	}
	else if (ctx->connect_mode == YAHOO_CONNECT_SOCKS5)
	{
#if defined(WITH_SOCKS5)
#endif
	}
	else if (ctx->connect_mode == YAHOO_CONNECT_NORMAL ||
		ctx->connect_mode == YAHOO_CONNECT_HTTP ||
		ctx->connect_mode == YAHOO_CONNECT_HTTPPROXY)
	{
		res =
			connect(servfd, (struct sockaddr *) &serv_addr,
			sizeof(serv_addr));
	}
	else
	{
		printf("[libyahoo] unhandled connect mode (%d).\n",
			ctx->connect_mode);
		return (0);
	}

	if (res < 0)
	{
		close(servfd);
		servfd = 0;
		printf("[libyahoo] failed to connect to server (%s:%d)\n", host,
			port);
		return (0);
	}

	yahoo_dbg_Print("libyahoo",
		"[libyahoo] yahoo_socket_connect - finished\n");
	return servfd;
}

/* really ugly brute force approach - someone find a GPL'd/free
   equivalent and replace this p.o.s. */
static char *yahoo_urlencode(char *data)
{
	static char *tmp = NULL;
	char buf[4];
	int i, len;

	len = 3 * strlen(data) + 1;

	FREE(tmp);

	if (!data)
	{
		return NULL;
	}

	/* change this at some point to re-use the buffer, no sense
	   allocating repeatedly */
	tmp = (char *) malloc(len);
	tmp[0] = 0;

	for (i = 0; i < strlen(data); i++)
	{
		if (isdigit((int) (data[i])) ||
			isalpha((int) data[i]) || data[i] == '_')
		{
			buf[0] = data[i];
			buf[1] = 0;
			strcat(tmp, buf);
		}
		else
		{
			sprintf(buf, "%%%.2X", data[i]);
			strcat(tmp, buf);
		}
	}

	return tmp;
}

static void yahoo_addtobuffer(struct yahoo_context *ctx, char *data,
	int datalen)
{
	//yahoo_hexdump("yahoo_addtobuffer", data, datalen);

	/* Check buffer, increase size if necessary */
	if (!ctx->io_buf
		|| ((ctx->io_buf_maxlen - ctx->io_buf_curlen) < (datalen + 100)))
	{
		char *new_io_buf;

		if (datalen < 10240)
		{
			ctx->io_buf_maxlen += 10240;
		}
		else
		{
			ctx->io_buf_maxlen += datalen;
		}
		new_io_buf = (char *) malloc(ctx->io_buf_maxlen);

		if (ctx->io_buf)
		{
			memcpy(new_io_buf, ctx->io_buf, ctx->io_buf_curlen);
			FREE(ctx->io_buf);
		}

		ctx->io_buf = new_io_buf;
	}

	memcpy(ctx->io_buf + ctx->io_buf_curlen, data, datalen);
	ctx->io_buf_curlen += datalen;
}

static int yahoo_tcp_readline(char *ptr, int maxlen, int fd)
{
	int n, rc;
	char c;

	for (n = 1; n < maxlen; n++)
	{
	  again:

		if ((rc = read(fd, &c, 1)) == 1)
		{
			*ptr++ = c;
			if (c == '\n')
				break;
		}
		else if (rc == 0)
		{
			if (n == 1)
				return (0);		/* EOF, no data */
			else
				break;			/* EOF, w/ data */
		}
		else
		{
			if (errno == EINTR)
				goto again;
			printf
				("Yahoo: Error reading from socket in yahoo_tcp_readline.\n");
			exit(1);
		}
	}

	*ptr = 0;
	return (n);
}

/*
 * Published library interfaces
 */

/* Initialize interface to yahoo library, sortof like a class object
   creation routine. */
struct yahoo_context *yahoo_init(char *user, char *password,
	struct yahoo_options *options)
{
	struct yahoo_context *tmp;

	if (!user || !password)
	{
		return NULL;
	}

	/* Allocate a new context */
	tmp = (struct yahoo_context *) calloc(1, sizeof(*tmp));

	/* Fill in any available info */
	tmp->user = strdup(user);
	tmp->password = strdup(password);
	if (options->proxy_host)
	{
		tmp->proxy_host = strdup(options->proxy_host);
	}
	tmp->proxy_port = options->proxy_port;
	tmp->connect_mode = options->connect_mode;

#if defined(WITH_SOCKS4)
	if (connect_mode == YAHOO_CONNECT_SOCKS4)
	{
		static int did_socks_init = 0;

		if (did_socks_init == 0)
		{
			SOCKSinit("libyahoo");
			did_socks_init = 1;
		}
	}
#endif

	/* Fetch the cookies */
	if (!yahoo_fetchcookies(tmp))
	{
		yahoo_free_context(tmp);
		return NULL;
	}

	return tmp;
}

/* Free a yahoo context */
void yahoo_free_context(struct yahoo_context *ctx)
{
	FREE(ctx->user);
	FREE(ctx->password);
	FREE(ctx->proxy_host);
	FREE(ctx->io_buf);
	FREE(ctx->cookie);
	FREE(ctx->login_cookie);
	FREE(ctx->login_id);

	yahoo_free_buddies(ctx);
	yahoo_free_identities(ctx);

	FREE(ctx);
}

/* Turn a status code into it's corresponding string */
char *yahoo_get_status_string(int statuscode)
{
	int i;

	for (i = 0; yahoo_status_codes[i].label; i++)
	{
		if (yahoo_status_codes[i].id == statuscode)
		{
			return yahoo_status_codes[i].label;
		}
	}
	return NULL;
}

/* Turn a status code into it's corresponding string */
char *yahoo_get_status_append(int statuscode)
{
	int i;

	for (i = 0; yahoo_status_append[i].label; i++)
	{
		if (yahoo_status_append[i].id == statuscode)
		{
			return yahoo_status_append[i].label;
		}
	}
	return NULL;
}

/* Turn a service code into it's corresponding string */
char *yahoo_get_service_string(int servicecode)
{
	int i;
	char *name = "Unknown Service";
	static char tmp[50];

	for (i = 0; yahoo_service_codes[i].label; i++)
	{
		if (yahoo_service_codes[i].id == servicecode)
		{
			name = yahoo_service_codes[i].label;
			break;
		}
	}

	snprintf(tmp, 50, "(%d) %s", servicecode, name);
	return tmp;
}

/* Return a malloc()'d copy of the users cookie */
int yahoo_fetchcookies(struct yahoo_context *ctx)
{
	char buffer[5000];
	int servfd;
	int i;
	int res;
	char *tmpstr;

	if (!ctx)
	{
		return 0;
	}

	yahoo_dbg_Print("libyahoo", "[libyahoo] yahoo_fetchcookies: starting\n");

	/* Check for cached cookie */
	if (ctx->cookie)
	{
		FREE(ctx->cookie);
	}
	if (ctx->login_cookie)
	{
		FREE(ctx->login_cookie);
	}

	if (ctx->connect_mode == YAHOO_CONNECT_HTTPPROXY)
	{
		servfd = yahoo_socket_connect(ctx, ctx->proxy_host, ctx->proxy_port);
	}
	else
	{
		servfd = yahoo_socket_connect(ctx, YAHOO_AUTH_HOST, YAHOO_AUTH_PORT);
	}
	if (!servfd)
	{
		printf("[libyahoo] failed to connect to pager auth server.\n");
		return (0);
	}

	strcpy(buffer, "GET ");
	if (ctx->connect_mode == YAHOO_CONNECT_HTTPPROXY)
	{
		strcat(buffer, "http://" YAHOO_AUTH_HOST);
	}
	strcat(buffer, "/config/ncclogin?login=");
	if (ctx->login_id)
	{
		strcat(buffer, yahoo_urlencode(ctx->login_id));
	}
	else
	{
		strcat(buffer, yahoo_urlencode(ctx->user));
	}
	strcat(buffer, "&passwd=");
	strcat(buffer, yahoo_urlencode(ctx->password));
	strcat(buffer, "&n=1 HTTP/1.0\r\n");
	strcat(buffer, "User-Agent: " YAHOO_USER_AGENT "\r\n");
	strcat(buffer, "Host: " YAHOO_AUTH_HOST "\r\n");
	strcat(buffer, "\r\n");

	write(servfd, buffer, strlen(buffer));

	yahoo_dbg_Print("libyahoo",
		"[libyahoo] yahoo_fetchcookies: writing buffer '%s'\n", buffer);

	ctx->cookie = NULL;
	while ((res = yahoo_tcp_readline(buffer, 5000, servfd)) > 0)
	{
		/* strip out any non-alphas */
		for (i = 0; i < strlen(buffer); i++)
		{
			if (!isprint((int) buffer[i]))
			{
				buffer[i] = 0;
			}
		}

		yahoo_dbg_Print("libyahoo",
			"[libyahoo] yahoo_fetchcookies: read buffer '%s'\n", buffer);

		if (!strcasecmp(buffer, "ERROR: Invalid NCC Login"))
		{
			yahoo_dbg_Print("libyahoo",
				"[libyahoo] yahoo_fetchcookies: password was invalid\n");
			return (0);
		}

		if (!strncasecmp(buffer, "Set-Cookie: Y=", 14))
		{
			FREE(ctx->cookie);
			ctx->cookie = strdup(buffer + 12);

			tmpstr = strchr(ctx->cookie, ';');
			if (tmpstr)
			{
				*tmpstr = '\0';
			}
		}
	}
	yahoo_dbg_Print("libyahoo",
		"[libyahoo] yahoo_fetchcookies: closing server connection\n");
	close(servfd);
	servfd = 0;
	yahoo_dbg_Print("libyahoo",
		"[libyahoo] yahoo_fetchcookies: closed server connection\n");

	if (ctx->cookie)
	{
		tmpstr = strstr(ctx->cookie, "n=");
		if (tmpstr)
		{
			ctx->login_cookie = strdup(tmpstr + 2);
		}

		tmpstr = strchr(ctx->login_cookie, '&');
		if (tmpstr)
		{
			*tmpstr = '\0';
		}
	}

	if (ctx->cookie)
		yahoo_dbg_Print("libyahoo",
			"[libyahoo] yahoo_fetchcookies: cookie (%s)\n", ctx->cookie);
	if (ctx->login_cookie)
		yahoo_dbg_Print("libyahoo",
			"[libyahoo] yahoo_fetchcookies: login cookie (%s)\n",
			ctx->login_cookie);

	yahoo_dbg_Print("libyahoo", "[libyahoo] yahoo_fetchcookies: done\n");

	return 1;
}

/* Add a buddy to your buddy list */
int yahoo_add_buddy(struct yahoo_context *ctx, char *addid,
	char *active_id, char *group, char *msg)
{
	char buffer[5000];
	int servfd;

	if (!ctx)
	{
		return 0;
	}

	if (ctx->connect_mode == YAHOO_CONNECT_HTTPPROXY)
	{
		yahoo_dbg_Print("libyahoo",
			"[libyahoo] yahoo_add_buddy - connecting via proxy\n");
		servfd = yahoo_socket_connect(ctx, ctx->proxy_host, ctx->proxy_port);
	}
	else
	{
		yahoo_dbg_Print("libyahoo",
			"[libyahoo] yahoo_add_buddy - connecting\n");
		servfd = yahoo_socket_connect(ctx, YAHOO_DATA_HOST, YAHOO_DATA_PORT);
	}
	if (!servfd)
	{
		yahoo_dbg_Print("libyahoo",
			"[libyahoo] yahoo_add_buddy: failed to connect\n");
		return (0);
	}

	strcpy(buffer, "GET ");
	if (ctx->connect_mode == YAHOO_CONNECT_HTTPPROXY)
	{
		strcat(buffer, "http://" YAHOO_DATA_HOST);
	}
	strcat(buffer, "/config/set_buddygrp?.bg=");
	strcat(buffer, yahoo_urlencode(group));
	strcat(buffer, "&.src=bl&.cmd=a&.bdl=");
	strcat(buffer, yahoo_urlencode(addid));
	strcat(buffer, "&.id=");
	strcat(buffer, yahoo_urlencode(active_id));
	strcat(buffer, "&.l=");
	strcat(buffer, yahoo_urlencode(ctx->user));
	strcat(buffer, "&.amsg=");
	strcat(buffer, yahoo_urlencode(msg));
	strcat(buffer, " HTTP/1.0\r\n");

	strcat(buffer, "User-Agent: " YAHOO_USER_AGENT "\r\n");
	strcat(buffer, "Host: " YAHOO_DATA_HOST "\r\n");
	strcat(buffer, "Cookie: ");
	strcat(buffer, ctx->cookie);
	strcat(buffer, "\r\n");
	strcat(buffer, "\r\n");

	write(servfd, buffer, strlen(buffer));
	while (yahoo_tcp_readline(buffer, 5000, servfd) > 0)
	{
		/* just dump the output, I don't care about errors at the moment */
	}
	close(servfd);
	servfd = 0;

	/* indicate success for now with 0 */
	yahoo_dbg_Print("libyahoo", "[libyahoo] yahoo_add_buddy: finished\n");
	return 0;
}

/* Remove a buddy from your buddy list */
int yahoo_remove_buddy(struct yahoo_context *ctx, char *addid,
	char *active_id, char *group, char *msg)
{
	char buffer[5000];
	int servfd;

	if (!ctx)
	{
		return 0;
	}

	if (ctx->connect_mode == YAHOO_CONNECT_HTTPPROXY)
	{
		yahoo_dbg_Print("libyahoo",
			"[libyahoo] yahoo_add_buddy - connecting via proxy\n");
		servfd = yahoo_socket_connect(ctx, ctx->proxy_host, ctx->proxy_port);
	}
	else
	{
		yahoo_dbg_Print("libyahoo", "[libyahoo] yahoo_add_buddy - connecting\n");
		servfd = yahoo_socket_connect(ctx, YAHOO_DATA_HOST, YAHOO_DATA_PORT);
	}
	if (!servfd)
	{
		yahoo_dbg_Print("libyahoo",
			"[libyahoo] yahoo_add_buddy: failed to connect\n");
		return (0);
	}

	strcpy(buffer, "GET ");
	if (ctx->connect_mode == YAHOO_CONNECT_HTTPPROXY)
	{
		strcat(buffer, "http://" YAHOO_DATA_HOST);
	}
	strcat(buffer, "/config/set_buddygrp?.bg=");
	strcat(buffer, yahoo_urlencode(group));
	strcat(buffer, "&.src=bl&.cmd=d&.bdl=");
	strcat(buffer, yahoo_urlencode(addid));
	strcat(buffer, "&.id=");
	strcat(buffer, yahoo_urlencode(active_id));
	strcat(buffer, "&.l=");
	strcat(buffer, yahoo_urlencode(ctx->user));
	strcat(buffer, "&.amsg=");
	strcat(buffer, yahoo_urlencode(msg));
	strcat(buffer, " HTTP/1.0\r\n");

	strcat(buffer, "User-Agent: " YAHOO_USER_AGENT "\r\n");
	strcat(buffer, "Host: " YAHOO_DATA_HOST "\r\n");
	strcat(buffer, "Cookie: ");
	strcat(buffer, ctx->cookie);
	strcat(buffer, "\r\n");
	strcat(buffer, "\r\n");

	write(servfd, buffer, strlen(buffer));
	while (yahoo_tcp_readline(buffer, 5000, servfd) > 0)
	{
		/* just dump the output, I don't care about errors at the moment */
	}
	close(servfd);
	servfd = 0;

	/* indicate success for now with 0 */
	return 0;
}

/* Retrieve the configuration from the server */
int yahoo_get_config(struct yahoo_context *ctx)
{
	char buffer[5000];
	int i, j;
	int servfd;
	int commas;
	int in_section;
	struct yahoo_buddy **buddylist = NULL;
	int buddycnt = 0;
	int nextbuddy = 0;

	/* Check for cached cookie */
	if (!ctx || !ctx->cookie)
	{
		return 0;
	}

	yahoo_dbg_Print("libyahoo", "[libyahoo] yahoo_get_config: starting\n");

	if (ctx->connect_mode == YAHOO_CONNECT_HTTPPROXY)
	{
		servfd = yahoo_socket_connect(ctx, ctx->proxy_host, ctx->proxy_port);
	}
	else
	{
		servfd = yahoo_socket_connect(ctx, YAHOO_DATA_HOST, YAHOO_DATA_PORT);
	}
	if (!servfd)
	{
		yahoo_dbg_Print("libyahoo",
			"[libyahoo] yahoo_get_config: failed to connect\n");
		return (0);
	}

	strcpy(buffer, "GET ");
	if (ctx->connect_mode == YAHOO_CONNECT_HTTPPROXY)
	{
		strcat(buffer, "http://" YAHOO_DATA_HOST);
	}
	strcat(buffer, "/config/get_buddylist?.src=bl HTTP/1.0\r\n");
	strcat(buffer, "Cookie: ");
	strcat(buffer, ctx->cookie);
	strcat(buffer, "\r\n");
	strcat(buffer, "\r\n");

	write(servfd, buffer, strlen(buffer));
	yahoo_dbg_Print("libyahoo", "[libyahoo] yahoo_get_config: sending '%s'\n",
		buffer);

	in_section = 0;
	while (yahoo_tcp_readline(buffer, 5000, servfd) > 0)
	{
		/* strip out any non-alphas */
		for (i = 0; i < strlen(buffer); i++)
		{
			if (!isprint((int) buffer[i]))
			{
				for (j = i; j < strlen(buffer); j++)
				{
					buffer[j] = buffer[j + 1];
				}
			}
		}

		yahoo_dbg_Print("libyahoo",
			"[libyahoo] yahoo_get_config: read '%s'\n", buffer);

		if (!strcasecmp(buffer, "BEGIN IDENTITIES"))
		{
			in_section = 1;
		}
		else if (!strcasecmp(buffer, "END IDENTITIES"))
		{
			in_section = 0;
		}
		else if (!strcasecmp(buffer, "BEGIN BUDDYLIST"))
		{
			in_section = 2;
		}
		else if (!strcasecmp(buffer, "END BUDDYLIST"))
		{
			in_section = 0;
		}
		else if (in_section == 1)
		{
			char *tmp;

			/* count the commas */
			commas = 0;
			for (i = 0; i < strlen(buffer); i++)
			{
				if (buffer[i] == ',')
				{
					commas++;
				}
			}

			/* make sure we've gotten rid of any previous identities array */
			yahoo_free_identities(ctx);

			/* allocate the array to hold the list of identities */
			ctx->identities = (char **) calloc(commas + 2, sizeof(char *));

			/* Parse through the list and get all the entries */
			i = 0;
			tmp = strtok(buffer, ",");
			while (tmp)
			{
				yahoo_dbg_Print("libyahoo",
					"[libyahoo] yahoo_get_config: retrieved "
					"identity '%s'\n", tmp);
				ctx->identities[i++] = strdup(tmp);
				tmp = strtok(NULL, ",");
			}
			ctx->identities[i] = 0;
		}
		else if (in_section == 2)
		{
			char *group;
			char *tmp;
			struct yahoo_buddy **tmp_buddylist;
			struct yahoo_buddy *tmpbuddy;
			int tmp_buddycnt;

			/* count the buddies on this line */
			tmp_buddycnt = buddycnt;
			for (i = 0; i < strlen(buffer); i++)
			{
				if (buffer[i] == ',')
				{
					buddycnt++;
				}
			}
			buddycnt++;			/* always one more than comma count */

			/* allocate the array to hold the list of buddy */
			tmp_buddylist = (struct yahoo_buddy **)
				malloc(sizeof(struct yahoo_buddy *) * (buddycnt + 1));

			/* Free and copy the old one if necessary */
			if (buddylist)
			{
				memcpy(tmp_buddylist, buddylist,

					(tmp_buddycnt + 1) * sizeof(struct yahoo_buddy *));

				FREE(buddylist);
			}
			buddylist = tmp_buddylist;

			/* Parse through the list and get all the entries */
			tmp = strtok(buffer, ",:");
			group = NULL;
			while (tmp)
			{
				if (tmp == buffer)	/* group name */
				{
					group = tmp;
				}
				else
				{
					tmpbuddy = (struct yahoo_buddy *)

						malloc(sizeof(struct yahoo_buddy));

					tmpbuddy->id = strdup(tmp);
					tmpbuddy->group = strdup(group);
					yahoo_dbg_Print("libyahoo",
						"[libyahoo] yahoo_get_config: retrieved buddy '%s:%s'\n",
						group, tmp);
					buddylist[nextbuddy++] = tmpbuddy;
				}
				tmp = strtok(NULL, ",");
			}
			buddylist[nextbuddy] = 0;
		}
		else if (!strncasecmp(buffer, "Mail=", strlen("Mail=")))
		{
			ctx->mail = atoi(buffer + strlen("Mail="));
			yahoo_dbg_Print("libyahoo",
				"[libyahoo] yahoo_get_config: retrieved mail flag '%d'\n",
				ctx->mail);
		}
		else if (!strncasecmp(buffer, "Login=", strlen("Login=")))
		{
			FREE(ctx->login_id);
			ctx->login_id = strdup(buffer + strlen("Login="));
			yahoo_dbg_Print("libyahoo",
				"[libyahoo] yahoo_get_config: retrieved login id '%s'\n",
				ctx->login_id);
		}
	}
	close(servfd);
	servfd = 0;

	yahoo_free_buddies(ctx);
	ctx->buddies = buddylist;

	/* fill in a bogus login_in, just in case */
	if (!ctx->login_id)
	{
		ctx->login_id = strdup(ctx->user);
	}

	/* refetch the cookie if the login_id is different so that
	   it will have the correct info in it */
	if (strcmp(ctx->login_id, ctx->user))
	{
		yahoo_dbg_Print("libyahoo",
			"[libyahoo] yahoo_get_config - refetching cookies\n");
		yahoo_fetchcookies(ctx);
	}

	yahoo_dbg_Print("libyahoo", "[libyahoo] yahoo_get_config - finished\n");

	return 1;
}

/* Log in, optionally activating other secondary identities */
int yahoo_cmd_logon(struct yahoo_context *ctx, unsigned int initial_status)
{
	char login_string[5000];	/* need to change to malloc ASAP */
	char *tmpid;
	char **identities = ctx->identities;
	int i;

	if (!ctx || !ctx->login_cookie)
	{
		yahoo_dbg_Print("libyahoo",
			"[libyahoo] yahoo_cmd_logon: logon called without "
			"context and/or cookie.\n");
		exit(1);
	}

	strcpy(login_string, ctx->login_cookie);
/* testing with new logon code */
//  strcpy(login_string, "$1$_2S43d5f$XXXXXXXXWtRKNclLWyy8C.");

	login_string[strlen(login_string) + 1] = 0;
	login_string[strlen(login_string)] = 1;	/* control-A */

	strcat(login_string, ctx->user);

	/* Send all identities */
	if (identities)
	{
		i = 0;
		tmpid = identities[i];
		while (tmpid)
		{
			if (strcasecmp(tmpid, ctx->user))
			{
				strcat(login_string, ",");
				strcat(login_string, tmpid);
			}
			tmpid = identities[i++];
		}
	}

	yahoo_sendcmd(ctx, YAHOO_SERVICE_LOGON, ctx->user, login_string,
		initial_status);

	/* something that the windows one sends, not sure what it is */
#if 0
	login_string[0] = 0;
	strcat(login_string, "C=0\002");
	strcat(login_string, "F=0,P=0,H=0,S=0,W=0,O=0\002");
	strcat(login_string, "M=0,P=0,C=0,S=0");
	yahoo_sendcmd(ctx, YAHOO_SERVICE_PASSTHROUGH2, ctx->user, login_string,
		0);
#endif

	return 0;
}

int yahoo_connect(struct yahoo_context *ctx)
{
	int res;

	res = 0;
	ctx->sockfd = 0;

	yahoo_dbg_Print("libyahoo", "[libyahoo] yahoo_connect - starting\n");

	switch (ctx->connect_mode)
	{
		case YAHOO_CONNECT_SOCKS4:
		case YAHOO_CONNECT_SOCKS5:
		case YAHOO_CONNECT_NORMAL:
			yahoo_dbg_Print("libyahoo",
				"[libyahoo] yahoo_connect - establishing socket connection\n");
			ctx->sockfd =
				yahoo_socket_connect(ctx, YAHOO_PAGER_HOST, YAHOO_PAGER_PORT);
			if (!ctx->sockfd)
			{
				printf("[libyahoo] couldn't connect to pager host\n");
				return (0);
			}
			break;

		case YAHOO_CONNECT_HTTP:
		case YAHOO_CONNECT_HTTPPROXY:
			yahoo_dbg_Print("libyahoo",
				"[libyahoo] yahoo_connect - no connect for HTTP\n");
			/* no pager connection will be established for this */
			break;

		default:
			printf("[libyahoo] unhandled connect mode (%d)\n",
				ctx->connect_mode);
	}

	yahoo_dbg_Print("libyahoo", "[libyahoo] yahoo_connect - finished\n");
	return (1);
}

/* Send a packet to the server via http connection method */
/* at moment only handles regular http connection, once I have that
   working, this code needs to also do http proxy connections as well */
int yahoo_sendcmd_http(struct yahoo_context *ctx, struct yahoo_rawpacket *pkt)
{
	int sockfd;
	char buffer[5000];
	char tmpbuf[1000];
	int size;
	int res;

	if (!ctx || !pkt)
	{
		return (0);
	}

	size = YAHOO_PACKET_HEADER_SIZE + strlen(pkt->content) + 1;

	if (ctx->connect_mode == YAHOO_CONNECT_HTTPPROXY)
	{
		sockfd = yahoo_socket_connect(ctx, ctx->proxy_host, ctx->proxy_port);
	}
	else
	{
		sockfd = yahoo_socket_connect(ctx, YAHOO_PAGER_HTTP_HOST,
			YAHOO_PAGER_HTTP_PORT);
	}
	if (!sockfd)
	{
		printf("[libyahoo] failed to connect to pager http server.\n");
		return (0);
	}

	strcpy(buffer, "POST ");
	if (ctx->connect_mode == YAHOO_CONNECT_HTTPPROXY)
	{
		strcat(buffer, "http://" YAHOO_PAGER_HTTP_HOST);
	}
	strcat(buffer, "/notify HTTP/1.0\r\n");

	strcat(buffer, "User-Agent: " YAHOO_USER_AGENT "\r\n");
	strcat(buffer, "Host: " YAHOO_PAGER_HTTP_HOST "\r\n");
	snprintf(tmpbuf, 1000, "Content-Length: %d\r\n", size);
	strcat(buffer, tmpbuf);

	strcat(buffer, "Pragma: No-Cache\r\n");

	strcat(buffer, "Cookie: ");
	strcat(buffer, ctx->cookie);
	strcat(buffer, "\r\n");
	strcat(buffer, "\r\n");

	write(sockfd, buffer, strlen(buffer));
	write(sockfd, pkt, size);
	write(sockfd, "\r\n", 2);

	/* now we need to read the results */
	/* I'm taking the cheat approach and just dumping them onto the
	   buffer, headers and all, the _skip_to_YHOO_ code will handle it
	   for now */

	while ((res = read(sockfd, buffer, 5000)) > 0)
	{
		if (res == -1)
		{
			printf("[libyahoo] Error reading data from server.\n");
			exit(1);
		}
		yahoo_addtobuffer(ctx, buffer, res);
	}
	close(sockfd);
	sockfd = 0;

	return (0);
}

/* Send a packet to the server, called by all routines that want to issue
   a command. */
int yahoo_sendcmd(struct yahoo_context *ctx, int service, char *active_nick,
	char *content, unsigned int msgtype)
{
	int size;
	struct yahoo_rawpacket *pkt;
	int maxcontentsize;

	/* why the )&*@#$( did they hardwire the packet size that gets sent
	   when the size of the packet is included in what is sent, bizarre */
	size = 4 * 256 + YAHOO_PACKET_HEADER_SIZE;
	pkt = (struct yahoo_rawpacket *) calloc(1, size);

	/* figure out max content length, including trailing null */
	maxcontentsize = size - sizeof(struct yahoo_rawpacket);

	/* Build the packet */
	strcpy(pkt->version, YAHOO_PROTOCOL_HEADER);
	yahoo_storeint(pkt->len, size);
	yahoo_storeint(pkt->service, service);

	/* not sure if this is valid with YPNS1.4 or if it needs 2.0 */
	yahoo_storeint(pkt->msgtype, msgtype);

	/* Not sure, but might as well send for regular connections as well. */
	yahoo_storeint(pkt->magic_id, ctx->magic_id);
	strcpy(pkt->nick1, ctx->login_id);
	strcpy(pkt->nick2, active_nick);
	strncpy(pkt->content, content, maxcontentsize);

	// yahoo_hexdump("send_cmd", (char *) pkt, size);

	switch (ctx->connect_mode)
	{
		case YAHOO_CONNECT_SOCKS4:
		case YAHOO_CONNECT_SOCKS5:
		case YAHOO_CONNECT_NORMAL:
			write(ctx->sockfd, pkt, size);
			break;
		case YAHOO_CONNECT_HTTP:
		case YAHOO_CONNECT_HTTPPROXY:
			yahoo_sendcmd_http(ctx, pkt);
			break;
	}

	FREE(pkt);
	return (0);
}

int yahoo_cmd_ping(struct yahoo_context *ctx)
{
	yahoo_sendcmd(ctx, YAHOO_SERVICE_PING, ctx->user, "", 0);
	return (0);
}

int yahoo_cmd_idle(struct yahoo_context *ctx)
{
	yahoo_sendcmd(ctx, YAHOO_SERVICE_IDLE, ctx->user, "", 0);
	return (0);
}

int yahoo_cmd_sendfile(struct yahoo_context *ctx, char *active_user,
	char *touser, char *msg, char *filename)
{
	yahoo_dbg_Print("libyahoo", "yahoo_cmd_sendfile not implemented yet!");
	return (0);
}

int yahoo_cmd_msg(struct yahoo_context *ctx, char *active_user,
	char *touser, char *msg)
{
	char *content;

	content = (char *) malloc(strlen(touser) + strlen(msg) + 5);

	if (strlen(touser))
	{
		sprintf(content, "%s,%s", touser, msg);
		yahoo_sendcmd(ctx, YAHOO_SERVICE_MESSAGE, active_user, content, 0);
	}

	FREE(content);
	return (0);
}

int yahoo_cmd_msg_offline(struct yahoo_context *ctx, char *active_user,
	char *touser, char *msg)
{
	char *content;

	content = (char *) malloc(strlen(touser) + strlen(msg) + 5);

	if (strlen(touser))
	{
		sprintf(content, "%s,%s", touser, msg);
		yahoo_sendcmd(ctx, YAHOO_SERVICE_MESSAGE, active_user, content,
			YAHOO_MSGTYPE_KNOWN_USER);
	}

	FREE(content);
	return (0);
}

/* appended the " " so that won't trigger yahoo bug - hack for the moment */
int yahoo_cmd_set_away_mode(struct yahoo_context *ctx, int status, char *msg)
{
	char statusstring[500];

	yahoo_dbg_Print("libyahoo",
		"[libyahoo] yahoo_cmd_set_away_mode: set status (%d), msg(%s)\n",
		status, yahoo_dbg_NullCheck(msg));

	if (status == YAHOO_STATUS_CUSTOM)
	{
		if (msg && msg[0] != 0)
		{
			snprintf(statusstring, 500, "%d%c%s", status, 1, msg);
		}
		else
		{
			snprintf(statusstring, 500, "%d%c---", status, 1);
		}
	}
	else
	{
		snprintf(statusstring, 500, "%d", status);
	}
	yahoo_sendcmd(ctx, YAHOO_SERVICE_ISAWAY, ctx->user, statusstring, 0);

	return 0;
}

int yahoo_cmd_set_back_mode(struct yahoo_context *ctx, int status, char *msg)
{
	char statusstring[500];

	yahoo_dbg_Print("libyahoo",
		"[libyahoo] yahoo_cmd_set_back_mode: set status (%d), msg(%s)\n",
		status, yahoo_dbg_NullCheck(msg));

	snprintf(statusstring, 500, "%d%c%s ", status, 1, msg ? msg : "");
	yahoo_sendcmd(ctx, YAHOO_SERVICE_ISBACK, ctx->user, statusstring, 0);

	return 0;
}

int yahoo_cmd_activate_id(struct yahoo_context *ctx, char *newid)
{
	if (strlen(newid))
	{
		yahoo_sendcmd(ctx, YAHOO_SERVICE_IDACT, newid, newid, 0);
	}
	return (0);
}

int yahoo_cmd_user_status(struct yahoo_context *ctx)
{
	yahoo_sendcmd(ctx, YAHOO_SERVICE_USERSTAT, ctx->user, "", 0);
	return (0);
}

int yahoo_cmd_logoff(struct yahoo_context *ctx)
{
	yahoo_sendcmd(ctx, YAHOO_SERVICE_LOGOFF, ctx->user, ctx->user, 0);
	return (0);
}

/*

yahoo_cmd_start_conf()

   Starts a conference. (You create the conference)

Arguments:
   char *conf_id == The conference id -- usually of the form name-number,
                    though it doesn't seem to matter much. ex: jaylubo-123
		    You create this id to start the conference, but pass it
		    along after that.
   char **userlist == Users to invite. Null terminated array of strings.
   car *msg == Invitiation message.
   int type == 0 - normal, 1 - voice (not supported yet)

Packet format:
   id^invited-users^msg^0or1
*/
int yahoo_cmd_start_conf(struct yahoo_context *ctx, char *conf_id,
	char **userlist, char *msg, int type)
{
	char ctrlb = 2;
	char *content;
	char *new_userlist = yahoo_array2list(userlist);
	int cont_len = 0;

#ifdef ENABLE_LIBYAHOO_DEBUG
	char *unraw_msg = NULL;
#endif /* def ENABLE_LIBYAHOO_DEBUG */

	int size = strlen(conf_id) + strlen(msg) + 8 + strlen(new_userlist);

	content = (char *) malloc(size);
	memset(content, 0, size);

	cont_len = snprintf(content,
		size - 1,
		"%s%c%s%c%s%c%d",
		conf_id, ctrlb, new_userlist, ctrlb, msg, ctrlb, type);

#ifdef ENABLE_LIBYAHOO_DEBUG
	unraw_msg = yahoo_unraw_buffer(content, cont_len);
	yahoo_dbg_Print("libyahoo", "[libyahoo] yahoo_cmd_start_conf: %s\n",
		unraw_msg);
	free(unraw_msg);
#endif /* def ENABLE_LIBYAHOO_DEBUG */
	yahoo_sendcmd(ctx, YAHOO_SERVICE_CONFINVITE, ctx->user, content, 0);

	FREE(new_userlist);
	FREE(content);
	return (0);
}

/*
yahoo_cmd_conf_logon()

   Reply to a conference invitation, logs you into conference.

Arguments:
   char *conf_id ==   The conference id -- usually of the form name-number,
                      though it doesn't seem to matter much. ex: jaylubo-123
		      This comes from the invitiation.
   char *host ==      The person that sent you the invitation.
   char **userlist == Everyone else invited. This comes from the invitiation.
                      Null terminated array of strings.

Packet format:
   id^all-invited-users-and-host

*/
int yahoo_cmd_conf_logon(struct yahoo_context *ctx, char *conf_id,
	char *host, char **userlist)
{
	char ctrlb = 2;
	char *content;
	char *new_userlist = yahoo_array2list(userlist);
	int cont_len = 0;

#ifdef ENABLE_LIBYAHOO_DEBUG
	char *unraw_msg = NULL;
#endif /* def ENABLE_LIBYAHOO_DEBUG */

	int size = strlen(conf_id) + strlen(host) + 8 + strlen(new_userlist);

	content = (char *) malloc(size);
	memset(content, 0, size);

	cont_len =
		sprintf(content, "%s%c%s,%s", conf_id, ctrlb, host, new_userlist);

#ifdef ENABLE_LIBYAHOO_DEBUG
	unraw_msg = yahoo_unraw_buffer(content, cont_len);
	yahoo_dbg_Print("libyahoo", "[libyahoo] yahoo_cmd_conf_logon: %s\n",
		unraw_msg);
	free(unraw_msg);
#endif /* def ENABLE_LIBYAHOO_DEBUG */
	yahoo_sendcmd(ctx, YAHOO_SERVICE_CONFLOGON, ctx->user, content, 0);

	FREE(new_userlist);
	FREE(content);
	return (0);
}

/*

yahoo_cmd_decline_conf()

   Reply to a conference invitation, decline offer.

Arguments:
   char *conf_id ==   The conference id -- usually of the form name-number,
                      though it doesn't seem to matter much. ex: jaylubo-123
		      This comes from the invitiation.
   char *host ==      The person that sent you the invitation.
   char **userlist == Everyone else invited. This comes from the invitiation.
                      Null terminated array of strings.
		      (Null if replying to a conference additional invite )
   char *msg ==       Reason for declining.

Packet format:
   id^all-invited-users-and-host^msg

*/
int yahoo_cmd_decline_conf(struct yahoo_context *ctx, char *conf_id,
	char *host, char **userlist, char *msg)
{
	char ctrlb = 2;
	char *content;
	char *new_userlist = yahoo_array2list(userlist);

	int size =

		strlen(conf_id) + strlen(host) + strlen(msg) + 8 +
		strlen(new_userlist);

	content = (char *) malloc(size);
	memset(content, 0, size);

	sprintf(content, "%s%c%s,%s%c%s", conf_id, ctrlb, host, new_userlist,
		ctrlb, msg);

	yahoo_dbg_Print("libyahoo", "[libyahoo] yahoo_cmd_decline_conf: %s\n",
		content);
	yahoo_sendcmd(ctx, YAHOO_SERVICE_CONFDECLINE, ctx->user, content, 0);

	FREE(new_userlist);
	FREE(content);
	return (0);
}

/*

yahoo_cmd_conf_logoff()

   Logoff of a conference.

Arguments:
   char *conf_id ==   The conference id -- usually of the form name-number,
                      though it doesn't seem to matter much. ex: jaylubo-123
		      This comes from the invitiation.
   char **userlist == Everyone in conference.
                      Null terminated array of strings.

Packet format:
   id^all-invited-users

*/

int yahoo_cmd_conf_logoff(struct yahoo_context *ctx, char *conf_id,
	char **userlist)
{
	char ctrlb = 2;
	char *content;
	int cont_len = 0;

#ifdef ENABLE_LIBYAHOO_DEBUG
	char *unraw_msg = NULL;
#endif /* def ENABLE_LIBYAHOO_DEBUG */
	char *new_userlist = yahoo_array2list(userlist);

	int size = strlen(conf_id) + strlen(new_userlist) + 8;

	content = (char *) malloc(size);
	memset(content, 0, size);

	cont_len =
		snprintf(content, size, "%s%c%s", conf_id, ctrlb, new_userlist);
#ifdef ENABLE_LIBYAHOO_DEBUG
	unraw_msg = yahoo_unraw_buffer(content, cont_len);
	yahoo_dbg_Print("libyahoo", "[libyahoo] yahoo_cmd_conf_logoff: %s\n",
		unraw_msg);
	free(unraw_msg);
#endif /* def ENABLE_LIBYAHOO_DEBUG */
	yahoo_sendcmd(ctx, YAHOO_SERVICE_CONFLOGOFF, ctx->user, content, 0);

	FREE(new_userlist);
	FREE(content);
	return (0);
}

/*

yahoo_cmd_conf_invite()

   Invite another user to an already running conference.

Arguments:
   char *conf_id ==   The conference id -- usually of the form name-number,
                      though it doesn't seem to matter much. ex: jaylubo-123
		      This comes from the invitiation.
   char *invited_user == The person being invited to conference.
   char **userlist == Everyone else in conference.
                      Null terminated array of strings.
   char *msg ==       Invitation message.

Packet format:
   id^invited-user^who-else-in-conf^who-else-in-conf^msg^0

*/

int yahoo_cmd_conf_invite(struct yahoo_context *ctx, char *conf_id,
	char **userlist, char *invited_user, char *msg)
{
	char ctrlb = 2;
	char *content;
	char *new_userlist = yahoo_array2list(userlist);

	int size = strlen(conf_id) + strlen(invited_user)
		+ (2 * strlen(new_userlist)) + strlen(msg) + 7;

	content = (char *) malloc(size);
	memset(content, 0, size);

	sprintf(content, "%s%c%s%c%s%c%s%c%s%c0", conf_id, ctrlb,
		invited_user, ctrlb, new_userlist, ctrlb,
		new_userlist, ctrlb, msg, ctrlb);
	yahoo_dbg_Print("libyahoo", "[libyahoo] yahoo_cmd_conf_invite: %s\n",
		content);
	yahoo_sendcmd(ctx, YAHOO_SERVICE_CONFADDINVITE, ctx->user, content, 0);

	FREE(new_userlist);
	FREE(content);
	return (0);
}

/*

yahoo_cmd_conf_msg()

   Send a message to everyone in conference.

Arguments:
   char *conf_id ==   The conference id -- usually of the form name-number,
                      though it doesn't seem to matter much. ex: jaylubo-123
		      This comes from the invitiation.
   char **userlist == Everyone in conference.
                      Null terminated array of strings.
   char *msg ==       Message to send.

Packet format:
   id^all-invited-users^msg

*/
int yahoo_cmd_conf_msg(struct yahoo_context *ctx, char *conf_id,
	char **userlist, char *msg)
{
	char ctrlb = 2;
	char *content;
	int cont_len = 0;

#ifdef ENABLE_LIBYAHOO_DEBUG
	char *unraw_msg = NULL;
#endif /* def ENABLE_LIBYAHOO_DEBUG */
	char *new_userlist = yahoo_array2list(userlist);

	int size = strlen(conf_id) + strlen(new_userlist) + strlen(msg) + 8;

	content = (char *) malloc(size);
	memset(content, 0, size);

	cont_len =
		snprintf(content, size, "%s%c%s%c%s", conf_id, ctrlb, new_userlist,
		ctrlb, msg);
#ifdef ENABLE_LIBYAHOO_DEBUG
	unraw_msg = yahoo_unraw_buffer(content, cont_len);
	yahoo_dbg_Print("libyahoo", "yahoo_cmd_conf_msg: %s\n", unraw_msg);
	free(unraw_msg);
#endif /* def ENABLE_LIBYAHOO_DEBUG */
	yahoo_sendcmd(ctx, YAHOO_SERVICE_CONFMSG, ctx->user, content, 0);

	FREE(new_userlist);
	FREE(content);
	return (0);
}

/*
 * Free the rawpacket structure - primarily a placeholder
 * since all static elements at the moment
 */
void yahoo_free_rawpacket(struct yahoo_rawpacket *pkt)
{
	FREE(pkt);
}

/*
 * Free entire packet structure including string elements
 */
void yahoo_free_packet(struct yahoo_packet *pkt)
{
	int i;

	if (pkt)
	{
		FREE(pkt->real_id);
		FREE(pkt->active_id);
		FREE(pkt->conf_id);
		FREE(pkt->conf_host);
		FREE(pkt->conf_user);
		FREE(pkt->conf_msg);
		FREE(pkt->cal_url);
		FREE(pkt->cal_timestamp);
		FREE(pkt->cal_title);
		FREE(pkt->cal_description);
		FREE(pkt->chat_invite_content);
		FREE(pkt->msg_id);
		FREE(pkt->msg_timestamp);
		FREE(pkt->msg);
		FREE(pkt->file_from);
		FREE(pkt->file_flag);
		FREE(pkt->file_url);
		FREE(pkt->file_description);
		FREE(pkt->group_old);
		FREE(pkt->group_new);
		if (pkt->idstatus)
		{
			for (i = 0; i < pkt->idstatus_count; i++)
			{
				yahoo_free_idstatus(pkt->idstatus[i]);
			}
			free(pkt->idstatus);
		}
		free(pkt);
	}
}

void yahoo_free_idstatus(struct yahoo_idstatus *idstatus)
{
	if (!idstatus)
	{
		return;
	}

	FREE(idstatus->id);
	FREE(idstatus->connection_id);
	FREE(idstatus->status_msg);
	FREE(idstatus);
}

struct yahoo_packet *yahoo_parsepacket(struct yahoo_context *ctx,
	struct yahoo_rawpacket *inpkt)
{
	struct yahoo_packet *pkt;

	/* If no valid inpkt passed, return */
	if (!inpkt)
	{
		return NULL;
	}

	/* Allocate the packet structure, zeroed out */
	pkt = (struct yahoo_packet *) calloc(sizeof(*pkt), 1);

	/* Pull out the standard data */
	pkt->service = yahoo_makeint(inpkt->service);
	pkt->connection_id = yahoo_makeint(inpkt->connection_id);
	pkt->real_id = strdup(inpkt->nick1);
	pkt->active_id = strdup(inpkt->nick2);

	pkt->magic_id = yahoo_makeint(inpkt->magic_id);
	pkt->unknown1 = yahoo_makeint(inpkt->unknown1);
	pkt->msgtype = yahoo_makeint(inpkt->msgtype);

	/* doing this seems like a cleaner approach, but am not sure if it is
	   a valid one */
	if (pkt->magic_id != 0)
	{
		ctx->magic_id = pkt->magic_id;
	}
	if (pkt->connection_id != 0)
	{
		ctx->connection_id = pkt->connection_id;
	}

	/* Call a particular parse routine to pull out the content */
	switch (pkt->service)
	{
		case YAHOO_SERVICE_LOGON:
		case YAHOO_SERVICE_LOGOFF:
		case YAHOO_SERVICE_ISAWAY:
		case YAHOO_SERVICE_ISBACK:
		case YAHOO_SERVICE_USERSTAT:
		case YAHOO_SERVICE_CHATLOGON:
		case YAHOO_SERVICE_CHATLOGOFF:
		case YAHOO_SERVICE_GAMELOGON:
		case YAHOO_SERVICE_GAMELOGOFF:
			yahoo_parsepacket_status(ctx, pkt, inpkt);
			break;
		case YAHOO_SERVICE_IDACT:
		case YAHOO_SERVICE_IDDEACT:
			/* nothing needs done, only has main fields */
			break;
		case YAHOO_SERVICE_MESSAGE:
		case YAHOO_SERVICE_SYSMESSAGE:
		case YAHOO_SERVICE_CHATMSG:
			yahoo_parsepacket_message(ctx, pkt, inpkt);
			break;
		case YAHOO_SERVICE_NEWMAIL:
		case YAHOO_SERVICE_NEWPERSONALMAIL:
			yahoo_parsepacket_newmail(ctx, pkt, inpkt);
			break;
		case YAHOO_SERVICE_CALENDAR:
			yahoo_parsepacket_calendar(ctx, pkt, inpkt);
			break;
		case YAHOO_SERVICE_CHATINVITE:
			yahoo_parsepacket_chatinvite(ctx, pkt, inpkt);
			break;
		case YAHOO_SERVICE_NEWCONTACT:
			yahoo_parsepacket_newcontact(ctx, pkt, inpkt);
			break;
		case YAHOO_SERVICE_GROUPRENAME:
			yahoo_parsepacket_grouprename(ctx, pkt, inpkt);
			break;
		case YAHOO_SERVICE_CONFINVITE:
			yahoo_parsepacket_conference_invite(ctx, pkt, inpkt);
			break;
		case YAHOO_SERVICE_CONFLOGON:
		case YAHOO_SERVICE_CONFLOGOFF:
			yahoo_parsepacket_conference_user(ctx, pkt, inpkt);
			break;
		case YAHOO_SERVICE_CONFDECLINE:
			yahoo_parsepacket_conference_decline(ctx, pkt, inpkt);
			break;
		case YAHOO_SERVICE_CONFADDINVITE:
			yahoo_parsepacket_conference_addinvite(ctx, pkt, inpkt);
			break;
		case YAHOO_SERVICE_CONFMSG:
			yahoo_parsepacket_conference_msg(ctx, pkt, inpkt);
			break;
		case YAHOO_SERVICE_PING:
			yahoo_parsepacket_ping(ctx, pkt, inpkt);
			break;
		case YAHOO_SERVICE_FILETRANSFER:
			yahoo_parsepacket_filetransfer(ctx, pkt, inpkt);
			break;
		default:
			yahoo_dbg_Print("libyahoo",
				"yahoo_parsepacket: can't parse packet type (%d)\n",
				pkt->service);
			break;
	}

	return pkt;
}

int yahoo_parsepacket_ping(struct yahoo_context *ctx,
	struct yahoo_packet *pkt, struct yahoo_rawpacket *inpkt)
{
	char *content;

	/* Make working copy of content */
	content = inpkt->content;

	pkt->msg = NULL;
	if (content)
	{
		pkt->msg = strdup(content);
	}

	return 0;
}

int yahoo_parsepacket_newmail(struct yahoo_context *ctx,
	struct yahoo_packet *pkt, struct yahoo_rawpacket *inpkt)
{
	char *content;
	int len;

	/* Make working copy of content */
	content = inpkt->content;
	len = strlen(content);

	if (pkt->service == YAHOO_SERVICE_NEWMAIL)
	{
		pkt->mail_status = 0;
		if (len > 0)
		{
			pkt->mail_status = atoi(content);
		}
	}
	else if (pkt->service == YAHOO_SERVICE_NEWPERSONALMAIL)
	{
		pkt->mail_status = 0;
		if (len > 0)
		{
			pkt->mail_status = atoi(content);
		}
	}

	return 0;
}

int yahoo_parsepacket_grouprename(struct yahoo_context *ctx,
	struct yahoo_packet *pkt, struct yahoo_rawpacket *inpkt)
{
	char *content;
	char *tmp, delim[5];

	/* Make working copy of content */
	content = strdup(inpkt->content);

	/* init elements to all null */
	pkt->group_old = NULL;
	pkt->group_new = NULL;

	tmp = NULL;
	delim[0] = 1;				/* control-a */
	delim[1] = 0;

	if (content)
	{
		tmp = strtok(content, delim);
	}

	if (tmp)					/* got the conference id */
	{
		pkt->group_old = strdup(tmp);
		tmp = strtok(NULL, delim);
	}

	if (tmp)					/* conference host */
	{
		pkt->group_new = strdup(tmp);
		tmp = strtok(NULL, delim);
	}

	FREE(content);
	return (0);
}

/*

yahoo_parsepacket_conference_invite()

Packet format:
   id^host^invited-users^msg^0or1

Parses Arguments:
   char *conf_id ==   The conference id -- usually of the form name-number,
                      though it doesn't seem to matter much. ex: jaylubo-123
   char *conf_host == The person inviting you to conference.
   char **userlist == Everyone else invited to conference.
                      Null terminated array of strings.
   char *msg ==       Invitation message.
   int conf_type ==   Type of conference ( 0 = text, 1 = voice )

*/
int yahoo_parsepacket_conference_invite(struct yahoo_context *ctx,
	struct yahoo_packet *pkt, struct yahoo_rawpacket *inpkt)
{
	char *content;
	char *tmp = 0;
	size_t found = 0, len = yahoo_makeint(inpkt->len);

	/* Make working copy of content */
	content = memdup(inpkt->content, len);

	/* init elements to all null */
	pkt->conf_id = NULL;
	pkt->conf_host = NULL;
	pkt->conf_user = pkt->active_id;
	pkt->conf_userlist = NULL;
	pkt->conf_inviter = NULL;
	pkt->conf_msg = NULL;

	if (content)
	{
		tmp = memtok(content, len, "\002", 2, &found);
	}

	if (tmp)					/* got the conference id */
	{
		pkt->conf_id = memdupasstr(tmp, found);
		tmp = memtok(0, 0, "\002", 2, &found);
	}

	if (tmp)					/* conference host */
	{
		pkt->conf_host = memdupasstr(tmp, found);
		tmp = memtok(0, 0, "\002", 2, &found);
	}

	if (tmp)					/* who else is invited */
	{
		char *userlist = memdupasstr(tmp, found);

		pkt->conf_userlist = yahoo_list2array(userlist);
		FREE(userlist);
		tmp = memtok(0, 0, "\002", 2, &found);
	}

	if (tmp)					/* msg */
	{
		pkt->conf_msg = memdupasstr(tmp, found);
		tmp = memtok(0, 0, "\002", 2, &found);
	}

	if (tmp)					/* 0 == text chat 1 == voice chat */
	{
		char *conftype = memdupasstr(tmp, found);

		if (0 != conftype)
			pkt->conf_type = atoi(conftype);
		FREE(conftype);
		tmp = memtok(0, 0, "\002", 2, &found);
	}

	FREE(content);
	return 0;
}

/*

yahoo_parsepacket_conference_decline()

Packet format:
   id^user-who-declined^msg

Parses Arguments:
   char *conf_id ==   The conference id -- usually of the form name-number,
                      though it doesn't seem to matter much. ex: jaylubo-123
   char *conf_user == User who declined.
   char *msg ==       Reason for declining.

*/
int yahoo_parsepacket_conference_decline(struct yahoo_context *ctx,
	struct yahoo_packet *pkt, struct yahoo_rawpacket *inpkt)
{
	char *content;
	char *tmp, delim[2];

	/* Make working copy of content */
	content = strdup(inpkt->content);

	/* init elements to all null */
	pkt->conf_id = NULL;
	pkt->conf_host = NULL;
	pkt->conf_user = NULL;
	pkt->conf_userlist = NULL;
	pkt->conf_inviter = NULL;
	pkt->conf_msg = NULL;

	tmp = NULL;
	delim[0] = 2;				/* control-b */
	delim[1] = 0;

	if (content)
	{
		tmp = strtok(content, delim);
	}

	if (tmp)					/* got the conference id */
	{
		pkt->conf_id = strdup(tmp);
		tmp = strtok(NULL, delim);
	}
	if (tmp)					/* got the user who declined */
	{
		pkt->conf_user = strdup(tmp);
		tmp = strtok(NULL, delim);
	}
	if (tmp)					/* msg */
	{
		pkt->conf_msg = strdup(tmp);
		tmp = strtok(NULL, delim);
	}

	FREE(content);
	return 0;

}

/*

yahoo_parsepacket_conference_addinvite()

Packet format:
Msgtype == 1
   id^inviter^who-else-invited^who-else-in-conf^msg^0or1
Msgtype == 11
   id^inviter^invited-user

Parses Arguments:
   char *conf_id ==   The conference id -- usually of the form name-number,
                      though it doesn't seem to matter much. ex: jaylubo-123
   char *conf_inviter == The person inviting you to conference.
   char **userlist == Everyone else in conference.
                      Null terminated array of strings.
   char *msg ==       Invitation message.
   int conf_type ==   Type of conference ( 0 = text, 1 = voice )

   char *conf_user == User invited to conference (msgtype == 11)
*/
int yahoo_parsepacket_conference_addinvite(struct yahoo_context *ctx,
	struct yahoo_packet *pkt, struct yahoo_rawpacket *inpkt)
{
	char *content = 0, *tmp = 0;
	size_t found = 0, len = yahoo_makeint(inpkt->len);

	/* Make working copy of content */
	content = memdup(inpkt->content, len);

	/* init elements to all null */
	pkt->conf_id = NULL;
	pkt->conf_host = NULL;
	pkt->conf_user = NULL;
	pkt->conf_userlist = NULL;
	pkt->conf_inviter = NULL;
	pkt->conf_msg = NULL;

	if (pkt->msgtype == 1)
	{
		if (content)
		{
			tmp = memtok(content, len, "\002", 2, &found);
		}

		if (tmp)				/* got the conference id */
		{
			pkt->conf_id = memdupasstr(tmp, found);
			tmp = memtok(0, 0, "\002", 2, &found);
		}
		if (tmp)				/* got the inviter */
		{
			pkt->conf_inviter = memdupasstr(tmp, found);
			tmp = memtok(0, 0, "\002", 2, &found);
		}
		if (tmp)				/* got who-else-invited */
		{
			/* don't use this field, its the same as the next one
			   so I'm going to use the second field */
			/* pkt->conf_userlist = yahoo_list2array(tmp); */
			tmp = memtok(0, 0, "\002", 2, &found);
		}
		if (tmp)				/* got the people in conference
								   not counting the inviter */
		{
			char *userlist = memdupasstr(tmp, found);

			pkt->conf_userlist = yahoo_list2array(userlist);
			FREE(userlist);
			tmp = memtok(0, 0, "\002", 2, &found);
		}
		if (tmp)				/* got the message */
		{
			pkt->conf_msg = memdupasstr(tmp, found);
			tmp = memtok(0, 0, "\002", 2, &found);
		}
		if (tmp)				/* 0 at the end */
		{
			char *conftype = memdupasstr(tmp, found);

			if (0 != conftype)
				pkt->conf_type = atoi(conftype);
			FREE(conftype);
			/* tmp = memtok (0, 0, "\002", 2, &found); */
		}
	}
	else
		/* msgid == 11 (someone else is being invited) */
	{
		if (content)
		{
			tmp = memtok(content, len, "\002", 2, &found);
		}

		if (tmp)				/* got the conference id */
		{
			pkt->conf_id = memdupasstr(tmp, found);
			tmp = memtok(0, 0, "\002", 2, &found);
		}

		if (tmp)				/* got the inviter */
		{
			pkt->conf_inviter = memdupasstr(tmp, found);
			tmp = memtok(0, 0, "\002", 2, &found);
		}

		if (tmp)				/* got the invited-user */
		{
			pkt->conf_user = memdupasstr(tmp, found);
			/* tmp = memtok (0, 0, "\002", 2, &found); */
		}
	}

	FREE(content);
	return 0;
}

/*

yahoo_parsepacket_conference_msg()

Packet format:
   id^who-from^msg

Parses Arguments:
   char *conf_id ==   The conference id -- usually of the form name-number,
                      though it doesn't seem to matter much. ex: jaylubo-123
   char *conf_user == User who sent message.
   char *msg ==       Message.

*/
int yahoo_parsepacket_conference_msg(struct yahoo_context *ctx,
	struct yahoo_packet *pkt, struct yahoo_rawpacket *inpkt)
{
	char *content;
	char *tmp, delim[5];

	/* Make working copy of content */
	content = strdup(inpkt->content);

	/* init elements to all null */
	pkt->conf_id = NULL;
	pkt->conf_host = NULL;
	pkt->conf_user = NULL;
	pkt->conf_userlist = NULL;
	pkt->conf_inviter = NULL;
	pkt->conf_msg = NULL;

	tmp = NULL;
	delim[0] = 2;				/* control-b */
	delim[1] = 0;

	/* parse error messages first */
	if (pkt->msgtype == YAHOO_MSGTYPE_ERROR)
	{
		FREE(content);
		return 0;
	}

	if (content)
	{
		tmp = strtok(content, delim);
	}

	if (tmp)					/* got the conference id */
	{
		pkt->conf_id = strdup(tmp);
		tmp = strtok(NULL, delim);
	}

	if (tmp)					/* conference user */
	{
		pkt->conf_user = strdup(tmp);
		tmp = strtok(NULL, delim);
	}

	if (tmp)					/* msg */
	{
		pkt->conf_msg = strdup(tmp);
		tmp = strtok(NULL, delim);
	}

	FREE(content);
	return 0;
}

/*

yahoo_parsepacket_conference_user()
   (User logged on/off to conference)
Packet format:
   id^user_who_logged_on/off

Parses Arguments:
   char *conf_id ==   The conference id -- usually of the form name-number,
                      though it doesn't seem to matter much. ex: jaylubo-123
   char *conf_user == User who logged on to conference.

*/
int yahoo_parsepacket_conference_user(struct yahoo_context *ctx,
	struct yahoo_packet *pkt, struct yahoo_rawpacket *inpkt)
{
	char *content;
	char *tmp, delim[5];

	/* Make working copy of content */
	content = strdup(inpkt->content);

	/* init elements to all null */
	pkt->conf_id = NULL;
	pkt->conf_host = NULL;
	pkt->conf_user = NULL;
	pkt->conf_userlist = NULL;
	pkt->conf_inviter = NULL;
	pkt->conf_msg = NULL;

	tmp = NULL;
	delim[0] = 2;				/* control-b */
	delim[1] = 0;

	if (content)
	{
		tmp = strtok(content, delim);
	}

	if (tmp)					/* got the conference id */
	{
		pkt->conf_id = strdup(tmp);
		tmp = strtok(NULL, delim);
	}

	if (tmp)					/* conference user */
	{
		pkt->conf_user = strdup(tmp);
		tmp = strtok(NULL, delim);
	}

	FREE(content);
	return 0;
}

int yahoo_parsepacket_filetransfer(struct yahoo_context *ctx,
	struct yahoo_packet *pkt, struct yahoo_rawpacket *inpkt)
{
	char *content;
	char *tmp[5];
	int i, j, section;

	/* Make working copy of content */
	content = strdup(inpkt->content);

	/* init elements to all null */
	pkt->file_from = NULL;
	pkt->file_flag = NULL;
	pkt->file_url = NULL;
	pkt->file_expires = 0;
	pkt->file_description = NULL;

	/* overkill allocation, but simple since only temporary use */
	tmp[0] = strdup(content);
	tmp[1] = strdup(content);
	tmp[2] = strdup(content);
	tmp[3] = strdup(content);
	tmp[4] = strdup(content);

	/* raw data format: from,flag,url,timestamp,description */

	i = 0;
	j = 0;
	section = 0;
	tmp[0][0] = 0;
	tmp[1][0] = 0;
	tmp[2][0] = 0;
	tmp[3][0] = 0;
	tmp[4][0] = 0;

	while (i < strlen(content))
	{
		char ch = content[i];

		if (ch == ',' && section < 4)
		{
			j = 0;
			section++;
		}
		else
		{
			tmp[section][j++] = ch;
			tmp[section][j] = 0;
		}
		i++;
	}

	/* do stuff with extracted parts */
	pkt->file_from = strdup(tmp[0]);
	pkt->file_flag = strdup(tmp[1]);
	pkt->file_url = strdup(tmp[2]);
	pkt->file_expires = atoi(tmp[3]);
	pkt->file_description = strdup(tmp[4]);

	/* free working variables */
	FREE(tmp[0]);
	FREE(tmp[1]);
	FREE(tmp[2]);
	FREE(tmp[3]);
	FREE(tmp[4]);
	FREE(content);
	return 0;
}

int yahoo_parsepacket_calendar(struct yahoo_context *ctx,
	struct yahoo_packet *pkt, struct yahoo_rawpacket *inpkt)
{
	char *content;
	char *tmp, delim[5];

	/* Make working copy of content */
	content = strdup(inpkt->content);

	/* init elements to all null */
	pkt->cal_url = NULL;
	pkt->cal_timestamp = NULL;
	pkt->cal_type = 0;
	pkt->cal_title = NULL;
	pkt->cal_description = NULL;

	tmp = NULL;
	delim[0] = 2;				/* control-b */
	delim[1] = 0;

	if (content)
	{
		tmp = strtok(content, delim);
	}

	if (tmp)					/* got the url */
	{
		pkt->cal_url = strdup(tmp);
		tmp = strtok(NULL, delim);

/*
   v= is not the type code
   i= doesn't look like it either
   tmp2 = strstr(pkt->cal_url, "v=");
   if ( tmp2 )
   {
   pkt->cal_type = atoi(tmp2);
   }
 */

	}

	if (tmp)					/* unknown (type code?) */
	{
/* appears this isn't it either, I don't see where it is */
/*      pkt->cal_type = atoi(tmp);  */
		tmp = strtok(NULL, "\r\n");
	}

	if (tmp)					/* timestamp */
	{
		pkt->cal_timestamp = strdup(tmp);
		tmp = strtok(NULL, "\r\n");
	}

	if (tmp)					/* title */
	{
		pkt->cal_title = strdup(tmp);
		tmp = strtok(NULL, delim);	/* use delim since it won't occur again */
	}

	if (tmp)
	{
		pkt->cal_description = strdup(tmp);
	}

	FREE(content);
	return 0;
}

int yahoo_parsepacket_chatinvite(struct yahoo_context *ctx,
	struct yahoo_packet *pkt, struct yahoo_rawpacket *inpkt)
{
	char *content;
	int len;

	/* Make working copy of content */
	content = strdup(inpkt->content);
	len = strlen(content);

	/* do special parsing for invite later on */
	pkt->chat_invite_content = strdup(content);

	return 0;
}

int yahoo_parsepacket_newcontact(struct yahoo_context *ctx,
	struct yahoo_packet *pkt, struct yahoo_rawpacket *inpkt)
{
	char *content;
	int len;

	/* Make working copy of content */
	content = strdup(inpkt->content);
	len = strlen(content);

	/* cheat for now, say if first digit is number */
	if (len > 0)
	{
		if (isdigit((int) content[0]))
		{
			return yahoo_parsepacket_status(ctx, pkt, inpkt);
		}
		else
		{
			return yahoo_parsepacket_message(ctx, pkt, inpkt);
		}
	}

	return 0;
}

int yahoo_parsepacket_status(struct yahoo_context *ctx,
	struct yahoo_packet *pkt, struct yahoo_rawpacket *inpkt)
{
	char *content;
	char *tmpc;
	char *tmp1;
	int i;
	int len;
	int index;
	int realcount;

	/* Make working copy of content */
	content = strdup(inpkt->content);
	len = strlen(content);

	/* Pull off the flag from the initial part of the content */
	/* this flag indicates the number of buddy that're online */
	pkt->flag = 0;
	tmpc = content;
	while (tmpc[0] && isdigit((int) tmpc[0]))
	{
		pkt->flag = pkt->flag * 10 + (content[0] - '0');
		tmpc++;
	}
	if (tmpc[0] && tmpc[0] == ',')
	{
		tmpc++;
	}

	/*
	   We're receiving either this:
	   2,buddy1(0,728EE9FB,0,1,0,0),buddy2(0,7AC00000,0,1,0,0)
	   or this:
	   buddy1(0,728EE9FB,0,1,0,0)
	   hence:
	 */

	if (pkt->flag == 0)
	{
		pkt->idstatus_count = 1;
	}
	else
	{
		pkt->idstatus_count = pkt->flag;
	}

	/* print an error if I get the was not AWAY */
	if (strstr(tmpc, "was not AWAY"))
	{
		pkt->idstatus_count = 0;
		yahoo_dbg_Print("libyahoo", "yahoo_parsepacket_status: "
			"got a 'was not AWAY' message\n");
	}

	if (pkt->idstatus_count == 0)
	{
		/* No entries, so no array needed */
		pkt->idstatus = NULL;
	}
	else
	{
		/* Allocate the array */
		pkt->idstatus = (struct yahoo_idstatus **)
			calloc(sizeof(struct yahoo_idstatus), pkt->idstatus_count);

		for (i = 0; i < pkt->idstatus_count; i++)
		{
			pkt->idstatus[i] = (struct yahoo_idstatus *)

				calloc(1, sizeof(struct yahoo_idstatus));
		}
	}

	index = 0;
	tmp1 = NULL;
	realcount = 0;
	while (tmpc && tmpc[0] && pkt->idstatus)
	{
		struct yahoo_idstatus *tmpid;

		/* Get pointer to allocated structure to hold status data */
		tmpid = pkt->idstatus[index++];
		if (!tmpid)
		{
			/* shortcut, we know there can't be any more status entries
			   at this point */
			/* yahoo_dbg_Print("status", "null tmpid"); */
			break;
		}

		/* YPNS2.0 nick(status,msg,connection_id,UNK,in_pager,in_chat,in_game) */
		/* tnneul(99,test,message^A,6AD68325,0,1,0,0) */
		/*         0 1               2       3 4 5 6 */

		/* YPNS1.0 nick(status,connection_id,UNK,in_pager,in_chat,in_game) */
		/* nneul(0,7081F531,0,1,0,0) */
		/*       0 2        3 4 5 6 */

		/* rewrite this whole section in a less ugly fashion */
		/* first pull off the id */

		/* YUCK - YPNS2.0 has variable format status records, if type is 99,
		   it has 7 fields, second is msg */

#if 0
		yahoo_dbg_Print("status", "whole string = '%s'\n",
			yahoo_dbg_NullCheck(tmpc));
#endif

		if (tmp1)
		{
			tmp1 = strtok(NULL, "(");
		}
		else
		{
			tmp1 = strtok(tmpc, "(");
		}
		if (tmp1 && tmp1[0] == ',')
		{
			tmp1++;
		}

		if (tmp1)
		{
			tmpid->id = strdup(tmp1);
			realcount++;

			for (i = 0; i <= 6 && tmp1; i++)
			{
#if 0
				yahoo_dbg_Print("status", "i==%d\n", i);
#endif

				if (i == 6)		/* end of status area */
				{
					tmp1 = strtok(NULL, "),");
				}
				else if (i == 1)
				{
					char delim[3];

					if (tmpid->status == YAHOO_STATUS_CUSTOM)
					{
						delim[0] = 1;
						delim[1] = ',';
						delim[1] = 0;
						tmp1 = strtok(NULL, delim);
					}
					else
					{
						i = 2;
						tmp1 = strtok(NULL, ",");
					}
				}
				else
				{

					tmp1 = strtok(NULL, ",");
				}

				/* then pull off the particular element of the list */
				if (tmp1)
				{
					switch (i)
					{
						case 0:	/* status */
							tmpid->status = atoi(tmp1);
							break;
						case 1:	/* msg */
							if (tmpid->status == YAHOO_STATUS_CUSTOM)
							{
								tmpid->status_msg = strdup(tmp1);
							}
							break;
						case 2:	/* session id */
							tmpid->connection_id = strdup(tmp1);
							break;
						case 3:	/* dunno what this is */
							break;
						case 4:
							tmpid->in_pager = atoi(tmp1);
							break;
						case 5:
							tmpid->in_chat = atoi(tmp1);
							break;
						case 6:
							tmpid->in_game = atoi(tmp1);
							break;
					}
				}
			}
		}
	}

	for (i = realcount; i <= pkt->idstatus_count; i++)
	{
		if (pkt->idstatus && pkt->idstatus[i])
		{
			FREE(pkt->idstatus[i]);
		}
	}
	pkt->idstatus_count = realcount;

	/* Free working copy of content */
	FREE(content);

	/* Return ok for success */
	return (0);
}

int yahoo_parsepacket_message(struct yahoo_context *ctx,
	struct yahoo_packet *pkt, struct yahoo_rawpacket *inpkt)
{
	char *content;
	char *tmp_id;
	int i, j, section;

	if (pkt->msgtype == YAHOO_MSGTYPE_OFFLINE)
	{
		return yahoo_parsepacket_message_offline(ctx, pkt, inpkt);
	}

	/* Make working copy of content */
	content = strdup(inpkt->content);
	tmp_id = strdup(content);

	/* initialize */
	pkt->msg_status = 0;

	/* possible message content formats: */
/*     userid(#) *//* msgtype == YAHOO_MSGTYPE_STATUS */
	/*     userid,,msg */

	/* this needed butchered */
	/* YAHOO_MSGTYPE_OFFLINE */
	/* 6,6,tnneul,nneul,Tue Mar  7 12:14:50 2000,test offline msg^A */

	i = 0;
	j = 0;
	section = 0;
	tmp_id[0] = 0;
	while (i < strlen(content))
	{
		char ch = content[i];

		if (section == 0)		/* parsing userid */
		{
			if (ch == ',')
			{
				j = 0;
				section = 1;
			}
			else if (ch == '(')
			{
				j = 0;
				section = 2;
			}
			else
			{
				tmp_id[j++] = ch;
				tmp_id[j] = 0;
			}
		}
		else if (section == 1)	/* parsing flag */
		{
			if (ch == ',')
			{
				j = 0;
				section = 3;
			}
		}
		else if (section == 2)	/* parsing status */
		{
			if (ch == ')')
			{
				j = 0;
				section = 3;
			}
			else
			{
				if (isdigit((int) ch))
				{
					pkt->msg_status *= 10;
					pkt->msg_status += ch - '0';
				}
			}
		}
		else
		{
			pkt->msg = strdup(&content[i]);
			break;
		}

		i++;
	}

	/* do stuff with extracted parts */
	pkt->msg_id = strdup(tmp_id);

	/* handle empty message case */
	/* don't pass a message if it's just a status update */
	if (!pkt->msg && pkt->msgtype != YAHOO_MSGTYPE_STATUS)
	{
		pkt->msg = strdup("");
	}

	/* free working variables */
	FREE(tmp_id);
	FREE(content);

	/* Return ok for success */
	return (0);
}

/* This parses a special format offline message, and is only currently
called from yahoo_parsepacket_message. */
int yahoo_parsepacket_message_offline(struct yahoo_context *ctx,
	struct yahoo_packet *pkt, struct yahoo_rawpacket *inpkt)
{
	char *content;
	char *to_id;
	char *from_id;
	char *timestamp;
	int i, j, section;

	/* Make working copy of content */
	content = strdup(inpkt->content);
	to_id = strdup(content);
	from_id = strdup(content);
	timestamp = strdup(content);

	/* initialize */
	pkt->msg_status = 0;

	/* 6,6,tnneul,nneul,Tue Mar  7 12:14:50 2000,test offline msg^A */
	/* sec0,sec1,sec2=to,sec3=from,sec4=tstamp,sec5=msg */

	i = 0;
	j = 0;
	section = 0;
	to_id[0] = 0;
	from_id[0] = 0;
	timestamp[0] = 0;

	while (i < strlen(content))
	{
		char ch = content[i];

		if (section == 0)		/* parsing first unknown number */
		{
			if (ch == ',')
			{
				j = 0;
				section = 1;
			}
		}
		else if (section == 1)	/* parsing second unknown number */
		{
			if (ch == ',')
			{
				j = 0;
				section = 2;
			}
		}
		else if (section == 2)	/* parsing to-id */
		{
			if (ch == ',')
			{
				j = 0;
				section = 3;
			}
			else
			{
				to_id[j++] = ch;
				to_id[j] = 0;
			}
		}
		else if (section == 3)	/* parsing from-id */
		{
			if (ch == ',')
			{
				j = 0;
				section = 4;
			}
			else
			{
				from_id[j++] = ch;
				from_id[j] = 0;
			}
		}
		else if (section == 4)	/* parsing timestamp */
		{
			if (ch == ',')
			{
				j = 0;
				section = 5;
			}
			else
			{
				timestamp[j++] = ch;
				timestamp[j] = 0;
			}
		}
		else
		{
			pkt->msg = strdup(&content[i]);
			break;
		}

		i++;
	}

	/* do stuff with extracted parts */
	pkt->msg_id = strdup(from_id);
	pkt->msg_timestamp = strdup(timestamp);
	if (pkt->active_id)
	{
		FREE(pkt->active_id);
		pkt->active_id = strdup(to_id);
	}

	/* free working variables */
	FREE(timestamp);
	FREE(from_id);
	FREE(to_id) FREE(content);

	/* Return ok for success */
	return (0);
}

int yahoo_getdata(struct yahoo_context *ctx)
{
	char buf[1000];
	int res;

	/* This is a http mode connection, so just send a ping to get any
	   new data from the server. */
	if (ctx->connect_mode == YAHOO_CONNECT_HTTP ||
		ctx->connect_mode == YAHOO_CONNECT_HTTPPROXY)
	{
		yahoo_sendcmd(ctx, YAHOO_SERVICE_PING, ctx->user, "", 0);
		return (1);
	}

	/* this assumes that data is ready */
	/* Read from the connection to the server and get any new data */
	res = read(ctx->sockfd, buf, 1000);
	if (res == -1)
	{
		yahoo_dbg_Print("io",
			"yahoo_getdata: error reading data from server\n");
		return (0);
	}
	if (res > 0)
	{
		yahoo_addtobuffer(ctx, buf, res);
		yahoo_dbg_Print("io", "[libyahoo] yahoo_getdata: read (%d) bytes\n",
			res);
		return 1;
	}
	else if (res == 0)
	{
		yahoo_dbg_Print("io",
			"[libyahoo] yahoo_getdata: got zero length read\n", res);
		return 0;
	}

	return (1);
}

struct yahoo_rawpacket *yahoo_getpacket(struct yahoo_context *ctx)
{
	struct yahoo_rawpacket *pkt;
	struct yahoo_rawpacket *retpkt;
	int *buflen = &ctx->io_buf_curlen;
	char *buffer = ctx->io_buf;
	unsigned int contentlen;

	/* If buffer doesn't start with YHOO, skip bytes until it
	   does. This is to protect against possible packet alignment
	   errors if I size something wrong at any time. */

	while ((*buflen >= 4) && (memcmp(buffer, "YHOO", 4)))
	{
/* making quiet for now so I don't have to work too hard on the HTTP support */
#if 0
		printf("\nskipped buffer byte (%d)\n", buffer[0]);
#endif
		memmove(buffer, buffer + 1, *buflen - 1);
		*buflen = *buflen - 1;
	}

	/* Don't do anything if the buffer doesn't have at least a full
	   header */
	if (*buflen < YAHOO_PACKET_HEADER_SIZE)
	{
// printf("returning null cause buffer is too small\n");
		return NULL;
	}

/* print out the beginning of the buffer */
#if 0
	printf("Buffer (buflen = %d):\n", *buflen);
	for (i = 0; i < *buflen; i++)
	{
		if ((i) % 10 == 0)
		{
			printf("\n%.4d: ", i);
		}
		if (isprint(buffer[i]))
		{
			printf("%-3d %c  ", buffer[i], buffer[i]);
		}
		else
		{
			printf("%-3d    ", buffer[i]);
		}
	}
	printf("\n");
#endif
	/* Make pkt point to buffer for ease of use */
	pkt = (struct yahoo_rawpacket *) buffer;

	/* Determine the content size specified by the header */
	contentlen = yahoo_makeint(pkt->len) - YAHOO_PACKET_HEADER_SIZE;
// printf("contentlen = %d\n", contentlen);

	/* Don't continue if buffer doesn't have full content in it */
	if (*buflen < (YAHOO_PACKET_HEADER_SIZE + contentlen))
	{
// printf("buffer not big enough for contentlen\n");
		return NULL;
	}

	/* Copy this packet */
	retpkt =
		(struct yahoo_rawpacket *) malloc(YAHOO_PACKET_HEADER_SIZE +
		contentlen);
	memcpy(retpkt, buffer, YAHOO_PACKET_HEADER_SIZE + contentlen);

	/* Shift the buffer */
	memmove(buffer, buffer + YAHOO_PACKET_HEADER_SIZE + contentlen,
		*buflen - YAHOO_PACKET_HEADER_SIZE - contentlen);

	/* Adjust the buffer length */
	*buflen -= (YAHOO_PACKET_HEADER_SIZE + contentlen);

	/* Return the packet */
	return retpkt;
}

int yahoo_isbuddy(struct yahoo_context *ctx, const char *id)
{
	int i;
	char *buddy = NULL;

	if (!id || !ctx || !ctx->buddies)
	{
		return FALSE;
	}

	for (i = 0; ctx->buddies[i]; i++)
	{
		buddy = (ctx->buddies[i])->id;
		if (!strcasecmp(id, buddy))
		{
			return TRUE;
		}
	}

	return FALSE;
}

static void yahoo_free_address (struct yahoo_address *add)
{
	yahoo_dbg_Print("addressbook",
		"[libyahoo] yahoo_free_address: record at address 0x%08p for user %s (%s %s) being free'd\n",
		add, add->id, add->firstname, add->lastname);

	FREE (add->firstname);
	FREE (add->lastname);
	FREE (add->emailnickname);
	FREE (add->email);
	FREE (add->workphone);
	FREE (add->homephone);
}

void yahoo_freeaddressbook(struct yahoo_context *ctx)
{
	unsigned int count = ctx->address_count;
	struct yahoo_address *add_p = ctx->addresses;

	if (NULL == ctx || NULL == ctx->addresses)
		return;

	while (count-- > 0)
	{
		yahoo_free_address (add_p++);
	}

	ctx->address_count = 0;
	FREE (ctx->addresses);
}

static void yahoo_data_to_addressbook (char *block, struct yahoo_context *ctx)
{
	char *token = NULL;
	int record = 0;
	struct yahoo_address *add = NULL;

	if (NULL == block || NULL == ctx)
		return;

	yahoo_freeaddressbook (ctx);

	add = ctx->addresses = calloc (ctx->address_count, sizeof (struct yahoo_address));

	/*
	 Okay!
	 At this point we have a char * (block) that has \012 delimited records
	 Each record (as a string when retreived with strtok) follows the format:
	<ID>:<FIRSTNAME>\011<LASTNAME>\011<EMAILNICKNAME>\011<EMAIL>\011<HOMEPHONE>\011<WORKPHONE>\011[01]\011<ENTRYID>\000
	 */

	token = strtok (block, "\012");
	while (NULL != token)
	{
		/*
		 Here we must use memtok because we'll get some repeated tokens!!!!!
		 */
		char *field = NULL;
		size_t token_len = 0, found = 0;

		++record;
		token_len = strlen (token);

		field = memtok(token, token_len, ":", 1, &found);

		if (NULL != field)
		{
			add->id = memdupasstr(field, found);
			field = memtok(0, 0, "\011", 1, &found);
		}

		if (NULL != field)
		{
			add->firstname = memdupasstr(field, found);
			field = memtok(0, 0, "\011", 1, &found);
		}

		if (NULL != field)
		{
			add->lastname = memdupasstr(field, found);
			field = memtok(0, 0, "\011", 1, &found);
		}

		if (NULL != field)
		{
			add->emailnickname = memdupasstr(field, found);
			field = memtok(0, 0, "\011", 1, &found);
		}

		if (NULL != field)
		{
			add->email = memdupasstr(field, found);
			field = memtok(0, 0, "\011", 1, &found);
		}

		if (NULL != field)
		{
			add->homephone = memdupasstr(field, found);
			field = memtok(0, 0, "\011", 1, &found);
		}

		if (NULL != field)
		{
			add->workphone = memdupasstr(field, found);
			field = memtok(0, 0, "\011", 1, &found);
		}

		if (NULL != field)
		{
			add->primary_phone = (*field == '0' ? home : work);
			field = memtok(0, 0, "", 1, &found);
		}

		if (NULL != field)
		{
			char *entryid = memdupasstr(field, found);
			if (NULL != entryid)
			{
				add->entryid = atoi (entryid);
				FREE (entryid);
			}
		}

		yahoo_dbg_Print("addressbook",
			"[libyahoo] yahoo_fetchaddressbook: record #%d is for user %s (%s %s)\n",
			record, add->id, add->firstname, add->lastname);

		++add;

		token = strtok (NULL, "\012");
	}
}

/* retreive the details of the friends in your address book that have a Yahoo! id listed */
int yahoo_fetchaddressbook(struct yahoo_context *ctx)
{
	char buffer[5000];
	int servfd;
	int res;
	int copied = 0, size = 5000;
	char *address = NULL, *copy = NULL;

	if (!ctx)
	{
		return 0;
	}

	yahoo_dbg_Print("addressbook",
		"[libyahoo] yahoo_fetchaddressbook: starting\n");

	/* Check for cached addresses */
	if (ctx->addresses)
	{
		yahoo_freeaddressbook(ctx);
	}

	if (ctx->connect_mode == YAHOO_CONNECT_HTTPPROXY)
	{
		servfd = yahoo_socket_connect(ctx, ctx->proxy_host, ctx->proxy_port);
	}
	else
	{
		servfd = yahoo_socket_connect(ctx, YAHOO_ADDRESS_HOST, YAHOO_ADDRESS_PORT);
	}

	if (!servfd)
	{
		printf("[libyahoo] failed to connect to address book server.\n");
		return (0);
	}

	strcpy(buffer, "GET ");
	if (ctx->connect_mode == YAHOO_CONNECT_HTTPPROXY)
	{
		strcat(buffer, YAHOO_ADDRESS_HOST);
	}
	strcat(buffer, "/yab/uk/yab?v=PG&A=s");
	strcat(buffer, " HTTP/1.0\r\n");
	strcat(buffer, "User-Agent: " YAHOO_USER_AGENT "\r\n");
	strcat(buffer, "Host: " YAHOO_AUTH_HOST "\r\n");
	strcat(buffer, "Cookie: ");
	strcat(buffer, ctx->cookie);
	strcat(buffer, "\r\n");
	strcat(buffer, "\r\n");

	write(servfd, buffer, strlen(buffer));

	yahoo_dbg_Print("addressbook",
		"[libyahoo] yahoo_fetchaddressbook: writing buffer '%s'\n", buffer);

	while ((res = yahoo_tcp_readline(buffer, 5000, servfd)) > 0)
	{
		if ('\012' == buffer[0])
			continue;

		if (0 == strncmp (buffer, "1\011", 2))
		{
			yahoo_dbg_Print("addressbook",
				"[libyahoo] yahoo_fetchaddressbook: found first line\n");
			if (3 == res)
			{
				yahoo_dbg_Print("addressbook",
					"[libyahoo] yahoo_fetchaddressbook: however there's been a problem\n");
				break;
			}

			address = &buffer[2];
		}
		else if (NULL != address)
		{
			address = &buffer[0];
		}

		if (NULL != address)
		{
			if (NULL == copy)
			{
				copy = malloc (size);
				memset (copy, 0, size);
			}

			if ((copied + res) > size)
			{
				char *newcopy = NULL;

				yahoo_dbg_Print("addressbook",
					"[libyahoo] yahoo_fetchaddressbook: resizing buffer from %d bytes to %d bytes\n", size, size * 2);
				size *= 2;
				newcopy = malloc (size);
				memset (newcopy, 0, size);
				memcpy (newcopy, copy, copied);
				free (copy);
				copy = newcopy;
			}

			copied += res;
			strcat (copy, address);
			++ctx->address_count;
		}
	}

	yahoo_data_to_addressbook (copy, ctx);
	FREE (copy);

	yahoo_dbg_Print("addressbook",
		"[libyahoo] yahoo_fetchaddressbook: closing server connection\n");
	close(servfd);
	servfd = 0;
	yahoo_dbg_Print("addressbook",
		"[libyahoo] yahoo_fetchaddressbook: closed server connection\n");

	yahoo_dbg_Print("addressbook", "[libyahoo] yahoo_fetchaddressbook: done (%d addresses retreived)\n", ctx->address_count);

	return ctx->address_count;
}