diff -r e73b159a9047 -r dc1fb3647374 plugins/yay/libyahoo.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugins/yay/libyahoo.c Fri Nov 03 10:03:53 2000 +0000 @@ -0,0 +1,3600 @@ +/* + 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 + */ + +/* + Yahoo Pager Client Emulator Pro - yppro.c + A basic reference implementation + Douglas Winslow + 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 +#include +#include +#include +#include +#include +#if defined(WITH_GTK) +#include +#endif +#include +#if defined(HAVE_STRINGS_H) +#include +#endif +#if defined(HAVE_STRING_H) +#include +#endif +#include +#include "libyahoo.h" +#ifdef HAVE_DMALLOC +#include "dmalloc.h" +#else +#include +#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: \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: + :\011\011\011\011\011\011[01]\011\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; +}