diff options
Diffstat (limited to 'protocols/yahoo/libyahoo2.c')
-rw-r--r-- | protocols/yahoo/libyahoo2.c | 5437 |
1 files changed, 5437 insertions, 0 deletions
diff --git a/protocols/yahoo/libyahoo2.c b/protocols/yahoo/libyahoo2.c new file mode 100644 index 00000000..07689809 --- /dev/null +++ b/protocols/yahoo/libyahoo2.c @@ -0,0 +1,5437 @@ +/* + * libyahoo2: libyahoo2.c + * + * Some code copyright (C) 2002-2004, Philip S Tellis <philip.tellis AT gmx.net> + * YMSG16 code copyright (C) 2009, + * Siddhesh Poyarekar <siddhesh dot poyarekar at gmail dot com> + * + * Yahoo Search copyright (C) 2003, Konstantin Klyagin <konst AT konst.org.ua> + * + * Much of this code was taken and adapted from the yahoo module for + * gaim released under the GNU GPL. This code is also released under the + * GNU GPL. + * + * This code is derivitive of Gaim <http://gaim.sourceforge.net> + * copyright (C) 1998-1999, Mark Spencer <markster@marko.net> + * 1998-1999, Adam Fritzler <afritz@marko.net> + * 1998-2002, Rob Flynn <rob@marko.net> + * 2000-2002, Eric Warmenhoven <eric@warmenhoven.org> + * 2001-2002, Brian Macke <macke@strangelove.net> + * 2001, Anand Biligiri S <abiligiri@users.sf.net> + * 2001, Valdis Kletnieks + * 2002, Sean Egan <bj91704@binghamton.edu> + * 2002, Toby Gray <toby.gray@ntlworld.com> + * + * This library also uses code from other libraries, namely: + * Portions from libfaim copyright 1998, 1999 Adam Fritzler + * <afritz@auk.cx> + * Portions of Sylpheed copyright 2000-2002 Hiroyuki Yamamoto + * <hiro-y@kcn.ne.jp> + * + * YMSG16 authentication code based mostly on write-up at: + * http://www.carbonize.co.uk/ymsg16.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef _WIN32 +#include <unistd.h> +#endif +#include <errno.h> +#include <stdio.h> +#include <stdarg.h> + +#if STDC_HEADERS +# include <string.h> +#else +# if !HAVE_STRCHR +# define strchr index +# define strrchr rindex +# endif +char *strchr (), *strrchr (); +# if !HAVE_MEMCPY +# define memcpy(d, s, n) bcopy ((s), (d), (n)) +# define memmove(d, s, n) bcopy ((s), (d), (n)) +# endif +#endif + +#include <sys/types.h> + +#ifdef __MINGW32__ +# include <winsock2.h> +#endif + +#include <stdlib.h> +#include <ctype.h> + +#include "sha1.h" +#include "md5.h" +#include "yahoo2.h" +#include "yahoo_httplib.h" +#include "yahoo_util.h" +#include "yahoo_fn.h" + +#include "yahoo2_callbacks.h" +#include "yahoo_debug.h" +#if defined(__MINGW32__) && !defined(HAVE_GLIB) +#define snprintf _snprintf +#define vsnprintf _vsnprintf +#endif + +#include "base64.h" +#include "http_client.h" + +#ifdef USE_STRUCT_CALLBACKS +struct yahoo_callbacks *yc = NULL; + +void yahoo_register_callbacks(struct yahoo_callbacks *tyc) +{ + yc = tyc; +} + +#define YAHOO_CALLBACK(x) yc->x +#else +#define YAHOO_CALLBACK(x) x +#endif + +static int yahoo_send_data(void *fd, void *data, int len); +static void _yahoo_http_connected(int id, void *fd, int error, void *data); +static void yahoo_connected(void *fd, int error, void *data); + +int yahoo_log_message(char *fmt, ...) +{ + char out[1024]; + va_list ap; + va_start(ap, fmt); + vsnprintf(out, sizeof(out), fmt, ap); + va_end(ap); + return YAHOO_CALLBACK(ext_yahoo_log) ("%s", out); +} + +int yahoo_connect(char *host, int port) +{ + return YAHOO_CALLBACK(ext_yahoo_connect) (host, port); +} + +static enum yahoo_log_level log_level = YAHOO_LOG_NONE; + +enum yahoo_log_level yahoo_get_log_level() +{ + return log_level; +} + +int yahoo_set_log_level(enum yahoo_log_level level) +{ + enum yahoo_log_level l = log_level; + log_level = level; + return l; +} + +/* default values for servers */ +static char *default_pager_hosts[] = { "scs.msg.yahoo.com", + "scsa.msg.yahoo.com", + "scsb.msg.yahoo.com", + "scsc.msg.yahoo.com", + NULL}; + +static int pager_port = 5050; +static int fallback_ports[] = { 23, 25, 80, 20, 119, 8001, 8002, 5050, 0 }; + +static char filetransfer_host[] = "filetransfer.msg.yahoo.com"; +static int filetransfer_port = 80; +static char webcam_host[] = "webcam.yahoo.com"; +static int webcam_port = 5100; +static char webcam_description[] = ""; +static char local_host[] = ""; +static int conn_type = Y_WCM_DSL; + +static char profile_url[] = "http://profiles.yahoo.com/"; + +struct connect_callback_data { + struct yahoo_data *yd; + int tag; + int i; + int server_i; +}; + +struct yahoo_pair { + int key; + char *value; +}; + +struct yahoo_packet { + unsigned short int service; + unsigned int status; + unsigned int id; + YList *hash; +}; + +struct yahoo_search_state { + int lsearch_type; + char *lsearch_text; + int lsearch_gender; + int lsearch_agerange; + int lsearch_photo; + int lsearch_yahoo_only; + int lsearch_nstart; + int lsearch_nfound; + int lsearch_ntotal; +}; + +struct data_queue { + unsigned char *queue; + int len; +}; + +struct yahoo_input_data { + struct yahoo_data *yd; + struct yahoo_webcam *wcm; + struct yahoo_webcam_data *wcd; + struct yahoo_search_state *ys; + + void *fd; + enum yahoo_connection_type type; + + unsigned char *rxqueue; + int rxlen; + int read_tag; + + YList *txqueues; + int write_tag; +}; + +struct yahoo_server_settings { + char *pager_host; + int pager_port; + char *filetransfer_host; + int filetransfer_port; + char *webcam_host; + int webcam_port; + char *webcam_description; + char *local_host; + int conn_type; + char **pager_host_list; +}; + +static void yahoo_process_ft_connection(struct yahoo_input_data *yid, int over); + +static void yahoo_process_filetransfer(struct yahoo_input_data *yid, + struct yahoo_packet *pkt); +static void yahoo_process_filetransferinfo(struct yahoo_input_data *yid, + struct yahoo_packet *pkt); +static void yahoo_process_filetransferaccept(struct yahoo_input_data *yid, + struct yahoo_packet *pkt); + +static void yahoo_https_auth(struct yahoo_input_data *yid, const char *seed, const char *sn); + +static void *_yahoo_default_server_settings() +{ + struct yahoo_server_settings *yss = + y_new0(struct yahoo_server_settings, 1); + + /* Give preference to the default host list + * Make sure that only one of the two is set at any time + */ + yss->pager_host = NULL; + yss->pager_host_list = default_pager_hosts; + + yss->pager_port = pager_port; + yss->filetransfer_host = strdup(filetransfer_host); + yss->filetransfer_port = filetransfer_port; + yss->webcam_host = strdup(webcam_host); + yss->webcam_port = webcam_port; + yss->webcam_description = strdup(webcam_description); + yss->local_host = strdup(local_host); + yss->conn_type = conn_type; + + return yss; +} + +static void *_yahoo_assign_server_settings(va_list ap) +{ + struct yahoo_server_settings *yss = _yahoo_default_server_settings(); + char *key; + char *svalue; + int nvalue; + char **pvalue; + + while (1) { + key = va_arg(ap, char *); + if (key == NULL) + break; + + if (!strcmp(key, "pager_host")) { + svalue = va_arg(ap, char *); + free(yss->pager_host); + yss->pager_host = strdup(svalue); + yss->pager_host_list = NULL; + } else if (!strcmp(key, "pager_host_list")) { + pvalue = va_arg(ap, char **); + yss->pager_host_list = pvalue; + free(yss->pager_host); + yss->pager_host = NULL; + } else if (!strcmp(key, "pager_port")) { + nvalue = va_arg(ap, int); + yss->pager_port = nvalue; + } else if (!strcmp(key, "filetransfer_host")) { + svalue = va_arg(ap, char *); + free(yss->filetransfer_host); + yss->filetransfer_host = strdup(svalue); + } else if (!strcmp(key, "filetransfer_port")) { + nvalue = va_arg(ap, int); + yss->filetransfer_port = nvalue; + } else if (!strcmp(key, "webcam_host")) { + svalue = va_arg(ap, char *); + free(yss->webcam_host); + yss->webcam_host = strdup(svalue); + } else if (!strcmp(key, "webcam_port")) { + nvalue = va_arg(ap, int); + yss->webcam_port = nvalue; + } else if (!strcmp(key, "webcam_description")) { + svalue = va_arg(ap, char *); + free(yss->webcam_description); + yss->webcam_description = strdup(svalue); + } else if (!strcmp(key, "local_host")) { + svalue = va_arg(ap, char *); + free(yss->local_host); + yss->local_host = strdup(svalue); + } else if (!strcmp(key, "conn_type")) { + nvalue = va_arg(ap, int); + yss->conn_type = nvalue; + } else { + WARNING(("Unknown key passed to yahoo_init, " + "perhaps you didn't terminate the list " + "with NULL")); + } + } + + return yss; +} + +static void yahoo_free_server_settings(struct yahoo_server_settings *yss) +{ + if (!yss) + return; + + free(yss->pager_host); + free(yss->filetransfer_host); + free(yss->webcam_host); + free(yss->webcam_description); + free(yss->local_host); + + free(yss); +} + +static YList *conns = NULL; +static YList *inputs = NULL; +static int last_id = 0; + +static void add_to_list(struct yahoo_data *yd) +{ + conns = y_list_prepend(conns, yd); +} + +static struct yahoo_data *find_conn_by_id(int id) +{ + YList *l; + for (l = conns; l; l = y_list_next(l)) { + struct yahoo_data *yd = l->data; + if (yd->client_id == id) + return yd; + } + return NULL; +} + +static void del_from_list(struct yahoo_data *yd) +{ + conns = y_list_remove(conns, yd); +} + +/* call repeatedly to get the next one */ +/* +static struct yahoo_input_data * find_input_by_id(int id) +{ + YList *l; + for(l = inputs; l; l = y_list_next(l)) { + struct yahoo_input_data *yid = l->data; + if(yid->yd->client_id == id) + return yid; + } + return NULL; +} +*/ + +static struct yahoo_input_data *find_input_by_id_and_webcam_user(int id, + const char *who) +{ + YList *l; + LOG(("find_input_by_id_and_webcam_user")); + for (l = inputs; l; l = y_list_next(l)) { + struct yahoo_input_data *yid = l->data; + if (yid->type == YAHOO_CONNECTION_WEBCAM + && yid->yd->client_id == id && yid->wcm && ((who + && yid->wcm->user + && !strcmp(who, yid->wcm->user)) + || !(yid->wcm->user && !who))) + return yid; + } + return NULL; +} + +static struct yahoo_input_data *find_input_by_id_and_type(int id, + enum yahoo_connection_type type) +{ + YList *l; + LOG(("find_input_by_id_and_type")); + for (l = inputs; l; l = y_list_next(l)) { + struct yahoo_input_data *yid = l->data; + if (yid->type == type && yid->yd->client_id == id) + return yid; + } + return NULL; +} + +static struct yahoo_input_data *find_input_by_id_and_fd(int id, void *fd) +{ + YList *l; + LOG(("find_input_by_id_and_fd")); + for (l = inputs; l; l = y_list_next(l)) { + struct yahoo_input_data *yid = l->data; + if (yid->fd == fd && yid->yd->client_id == id) + return yid; + } + return NULL; +} + +static int count_inputs_with_id(int id) +{ + int c = 0; + YList *l; + LOG(("counting %d", id)); + for (l = inputs; l; l = y_list_next(l)) { + struct yahoo_input_data *yid = l->data; + if (yid->yd->client_id == id) + c++; + } + LOG(("%d", c)); + return c; +} + +extern char *yahoo_crypt(char *, char *); + +/* Free a buddy list */ +static void yahoo_free_buddies(YList *list) +{ + YList *l; + + for (l = list; l; l = l->next) { + struct yahoo_buddy *bud = l->data; + if (!bud) + continue; + + FREE(bud->group); + FREE(bud->id); + FREE(bud->real_name); + if (bud->yab_entry) { + FREE(bud->yab_entry->fname); + FREE(bud->yab_entry->lname); + FREE(bud->yab_entry->nname); + FREE(bud->yab_entry->id); + FREE(bud->yab_entry->email); + FREE(bud->yab_entry->hphone); + FREE(bud->yab_entry->wphone); + FREE(bud->yab_entry->mphone); + FREE(bud->yab_entry); + } + FREE(bud); + l->data = bud = NULL; + } + + y_list_free(list); +} + +/* Free an identities list */ +static void yahoo_free_identities(YList *list) +{ + while (list) { + YList *n = list; + FREE(list->data); + list = y_list_remove_link(list, list); + y_list_free_1(n); + } +} + +/* Free webcam data */ +static void yahoo_free_webcam(struct yahoo_webcam *wcm) +{ + if (wcm) { + FREE(wcm->user); + FREE(wcm->server); + FREE(wcm->key); + FREE(wcm->description); + FREE(wcm->my_ip); + } + FREE(wcm); +} + +static void yahoo_free_data(struct yahoo_data *yd) +{ + FREE(yd->user); + FREE(yd->password); + FREE(yd->cookie_y); + FREE(yd->cookie_t); + FREE(yd->cookie_b); + FREE(yd->cookie_c); + FREE(yd->login_cookie); + FREE(yd->login_id); + + yahoo_free_buddies(yd->buddies); + yahoo_free_buddies(yd->ignore); + yahoo_free_identities(yd->identities); + + yahoo_free_server_settings(yd->server_settings); + + FREE(yd); +} + +#define YAHOO_PACKET_HDRLEN (4 + 2 + 2 + 2 + 2 + 4 + 4) + +static struct yahoo_packet *yahoo_packet_new(enum yahoo_service service, + enum ypacket_status status, int id) +{ + struct yahoo_packet *pkt = y_new0(struct yahoo_packet, 1); + + pkt->service = service; + pkt->status = status; + pkt->id = id; + + return pkt; +} + +static void yahoo_packet_hash(struct yahoo_packet *pkt, int key, + const char *value) +{ + struct yahoo_pair *pair = y_new0(struct yahoo_pair, 1); + pair->key = key; + pair->value = strdup(value); + pkt->hash = y_list_append(pkt->hash, pair); +} + +static int yahoo_packet_length(struct yahoo_packet *pkt) +{ + YList *l; + + int len = 0; + + for (l = pkt->hash; l; l = l->next) { + struct yahoo_pair *pair = l->data; + int tmp = pair->key; + do { + tmp /= 10; + len++; + } while (tmp); + len += 2; + len += strlen(pair->value); + len += 2; + } + + return len; +} + +#define yahoo_put16(buf, data) ( \ + (*(buf) = (unsigned char)((data)>>8)&0xff), \ + (*((buf)+1) = (unsigned char)(data)&0xff), \ + 2) +#define yahoo_get16(buf) ((((*(buf))&0xff)<<8) + ((*((buf)+1)) & 0xff)) +#define yahoo_put32(buf, data) ( \ + (*((buf)) = (unsigned char)((data)>>24)&0xff), \ + (*((buf)+1) = (unsigned char)((data)>>16)&0xff), \ + (*((buf)+2) = (unsigned char)((data)>>8)&0xff), \ + (*((buf)+3) = (unsigned char)(data)&0xff), \ + 4) +#define yahoo_get32(buf) ((((*(buf) )&0xff)<<24) + \ + (((*((buf)+1))&0xff)<<16) + \ + (((*((buf)+2))&0xff)<< 8) + \ + (((*((buf)+3))&0xff))) + +static void yahoo_packet_read(struct yahoo_packet *pkt, unsigned char *data, + int len) +{ + int pos = 0; + + while (pos + 1 < len) { + char *key, *value = NULL; + int accept; + int x; + + struct yahoo_pair *pair = y_new0(struct yahoo_pair, 1); + + key = malloc(len + 1); + x = 0; + while (pos + 1 < len) { + if (data[pos] == 0xc0 && data[pos + 1] == 0x80) + break; + key[x++] = data[pos++]; + } + key[x] = 0; + pos += 2; + pair->key = strtol(key, NULL, 10); + free(key); + + /* Libyahoo2 developer(s) don't seem to have the time to fix + this problem, so for now try to work around it: + + Sometimes we receive an invalid packet with not any more + data at this point. I don't know how to handle this in a + clean way, but let's hope this is clean enough: */ + + if (pos + 1 < len) { + accept = x; + /* if x is 0 there was no key, so don't accept it */ + if (accept) + value = malloc(len - pos + 1); + x = 0; + while (pos + 1 < len) { + if (data[pos] == 0xc0 && data[pos + 1] == 0x80) + break; + if (accept) + value[x++] = data[pos++]; + } + if (accept) + value[x] = 0; + pos += 2; + } else { + accept = 0; + } + + if (accept) { + pair->value = strdup(value); + FREE(value); + pkt->hash = y_list_append(pkt->hash, pair); + DEBUG_MSG(("Key: %d \tValue: %s", pair->key, + pair->value)); + } else { + FREE(pair); + } + } +} + +static void yahoo_packet_write(struct yahoo_packet *pkt, unsigned char *data) +{ + YList *l; + int pos = 0; + + for (l = pkt->hash; l; l = l->next) { + struct yahoo_pair *pair = l->data; + unsigned char buf[100]; + + snprintf((char *)buf, sizeof(buf), "%d", pair->key); + strcpy((char *)data + pos, (char *)buf); + pos += strlen((char *)buf); + data[pos++] = 0xc0; + data[pos++] = 0x80; + + strcpy((char *)data + pos, pair->value); + pos += strlen(pair->value); + data[pos++] = 0xc0; + data[pos++] = 0x80; + } +} + +static void yahoo_dump_unhandled(struct yahoo_packet *pkt) +{ + YList *l; + + NOTICE(("Service: 0x%02x\tStatus: %d", pkt->service, pkt->status)); + for (l = pkt->hash; l; l = l->next) { + struct yahoo_pair *pair = l->data; + NOTICE(("\t%d => %s", pair->key, pair->value)); + } +} + +static void yahoo_packet_dump(unsigned char *data, int len) +{ + if (yahoo_get_log_level() >= YAHOO_LOG_DEBUG) { + int i; + for (i = 0; i < len; i++) { + if ((i % 8 == 0) && i) + YAHOO_CALLBACK(ext_yahoo_log) (" "); + if ((i % 16 == 0) && i) + YAHOO_CALLBACK(ext_yahoo_log) ("\n"); + YAHOO_CALLBACK(ext_yahoo_log) ("%02x ", data[i]); + } + YAHOO_CALLBACK(ext_yahoo_log) ("\n"); + for (i = 0; i < len; i++) { + if ((i % 8 == 0) && i) + YAHOO_CALLBACK(ext_yahoo_log) (" "); + if ((i % 16 == 0) && i) + YAHOO_CALLBACK(ext_yahoo_log) ("\n"); + if (isprint(data[i])) + YAHOO_CALLBACK(ext_yahoo_log) (" %c ", data[i]); + else + YAHOO_CALLBACK(ext_yahoo_log) (" . "); + } + YAHOO_CALLBACK(ext_yahoo_log) ("\n"); + } +} + +/* raw bytes in quasi-big-endian order to base 64 string (NUL-terminated) */ +static void to_y64(unsigned char *out, const unsigned char *in, int inlen) +{ + base64_encode_real(in, inlen, out, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789._-"); +} + +static void yahoo_add_to_send_queue(struct yahoo_input_data *yid, void *data, + int length) +{ + struct data_queue *tx = y_new0(struct data_queue, 1); + tx->queue = y_new0(unsigned char, length); + tx->len = length; + memcpy(tx->queue, data, length); + + yid->txqueues = y_list_append(yid->txqueues, tx); + + if (!yid->write_tag) + yid->write_tag = + YAHOO_CALLBACK(ext_yahoo_add_handler) (yid->yd-> + client_id, yid->fd, YAHOO_INPUT_WRITE, yid); +} + +static void yahoo_send_packet(struct yahoo_input_data *yid, + struct yahoo_packet *pkt, int extra_pad) +{ + int pktlen = yahoo_packet_length(pkt); + int len = YAHOO_PACKET_HDRLEN + pktlen; + unsigned char *data; + int pos = 0; + + if (yid->fd < 0) + return; + + data = y_new0(unsigned char, len + 1); + + memcpy(data + pos, "YMSG", 4); + pos += 4; + pos += yahoo_put16(data + pos, YAHOO_PROTO_VER); /* version [latest 12 0x000c] */ + pos += yahoo_put16(data + pos, 0x0000); /* HIWORD pkt length??? */ + pos += yahoo_put16(data + pos, pktlen + extra_pad); /* LOWORD pkt length? */ + pos += yahoo_put16(data + pos, pkt->service); /* service */ + pos += yahoo_put32(data + pos, pkt->status); /* status [4bytes] */ + pos += yahoo_put32(data + pos, pkt->id); /* session [4bytes] */ + + yahoo_packet_write(pkt, data + pos); + + yahoo_packet_dump(data, len); + + if (yid->type == YAHOO_CONNECTION_FT) + yahoo_send_data(yid->fd, data, len); + else + yahoo_add_to_send_queue(yid, data, len); + FREE(data); +} + +static void yahoo_packet_free(struct yahoo_packet *pkt) +{ + while (pkt->hash) { + struct yahoo_pair *pair = pkt->hash->data; + YList *tmp; + FREE(pair->value); + FREE(pair); + tmp = pkt->hash; + pkt->hash = y_list_remove_link(pkt->hash, pkt->hash); + y_list_free_1(tmp); + } + FREE(pkt); +} + +static int yahoo_send_data(void *fd, void *data, int len) +{ + int ret; + int e; + + if (fd == NULL) + return -1; + + yahoo_packet_dump(data, len); + + do { + ret = YAHOO_CALLBACK(ext_yahoo_write) (fd, data, len); + } while (ret == -1 && errno == EINTR); + e = errno; + + if (ret == -1) { + LOG(("wrote data: ERR %s", strerror(errno))); + } else { + LOG(("wrote data: OK")); + } + + errno = e; + return ret; +} + +void yahoo_close(int id) +{ + struct yahoo_data *yd = find_conn_by_id(id); + if (!yd) + return; + + del_from_list(yd); + + yahoo_free_data(yd); + if (id == last_id) + last_id--; +} + +static void yahoo_input_close(struct yahoo_input_data *yid) +{ + inputs = y_list_remove(inputs, yid); + + LOG(("yahoo_input_close(read)")); + YAHOO_CALLBACK(ext_yahoo_remove_handler) (yid->yd->client_id, + yid->read_tag); + LOG(("yahoo_input_close(write)")); + YAHOO_CALLBACK(ext_yahoo_remove_handler) (yid->yd->client_id, + yid->write_tag); + yid->read_tag = yid->write_tag = 0; + if (yid->fd) + YAHOO_CALLBACK(ext_yahoo_close) (yid->fd); + yid->fd = 0; + FREE(yid->rxqueue); + if (count_inputs_with_id(yid->yd->client_id) == 0) { + LOG(("closing %d", yid->yd->client_id)); + yahoo_close(yid->yd->client_id); + } + yahoo_free_webcam(yid->wcm); + if (yid->wcd) + FREE(yid->wcd); + if (yid->ys) { + FREE(yid->ys->lsearch_text); + FREE(yid->ys); + } + FREE(yid); +} + +static int is_same_bud(const void *a, const void *b) +{ + const struct yahoo_buddy *subject = a; + const struct yahoo_buddy *object = b; + + return strcmp(subject->id, object->id); +} + +static char *getcookie(char *rawcookie) +{ + char *cookie = NULL; + char *tmpcookie; + char *cookieend; + + if (strlen(rawcookie) < 2) + return NULL; + + tmpcookie = strdup(rawcookie + 2); + cookieend = strchr(tmpcookie, ';'); + + if (cookieend) + *cookieend = '\0'; + + cookie = strdup(tmpcookie); + FREE(tmpcookie); + /* cookieend=NULL; not sure why this was there since the value is not preserved in the stack -dd */ + + return cookie; +} + +static char *getlcookie(char *cookie) +{ + char *tmp; + char *tmpend; + char *login_cookie = NULL; + + tmpend = strstr(cookie, "n="); + if (tmpend) { + tmp = strdup(tmpend + 2); + tmpend = strchr(tmp, '&'); + if (tmpend) + *tmpend = '\0'; + login_cookie = strdup(tmp); + FREE(tmp); + } + + return login_cookie; +} + +static void yahoo_process_notify(struct yahoo_input_data *yid, + struct yahoo_packet *pkt) +{ + struct yahoo_data *yd = yid->yd; + char *msg = NULL; + char *from = NULL; + char *to = NULL; + int stat = 0; + int accept = 0; + char *ind = NULL; + YList *l; + for (l = pkt->hash; l; l = l->next) { + struct yahoo_pair *pair = l->data; + if (pair->key == 4) + from = pair->value; + if (pair->key == 5) + to = pair->value; + if (pair->key == 49) + msg = pair->value; + if (pair->key == 13) + stat = atoi(pair->value); + if (pair->key == 14) + ind = pair->value; + if (pair->key == 16) { /* status == -1 */ + NOTICE((pair->value)); + return; + } + + } + + if (!msg) + return; + + if (!strncasecmp(msg, "TYPING", strlen("TYPING"))) + YAHOO_CALLBACK(ext_yahoo_typing_notify) (yd->client_id, to, + from, stat); + else if (!strncasecmp(msg, "GAME", strlen("GAME"))) + YAHOO_CALLBACK(ext_yahoo_game_notify) (yd->client_id, to, from, + stat, ind); + else if (!strncasecmp(msg, "WEBCAMINVITE", strlen("WEBCAMINVITE"))) { + if (!strcmp(ind, " ")) { + YAHOO_CALLBACK(ext_yahoo_webcam_invite) (yd->client_id, + to, from); + } else { + accept = atoi(ind); + /* accept the invitation (-1 = deny 1 = accept) */ + if (accept < 0) + accept = 0; + YAHOO_CALLBACK(ext_yahoo_webcam_invite_reply) (yd-> + client_id, to, from, accept); + } + } else + LOG(("Got unknown notification: %s", msg)); +} + +static void yahoo_process_conference(struct yahoo_input_data *yid, + struct yahoo_packet *pkt) +{ + struct yahoo_data *yd = yid->yd; + char *msg = NULL; + char *host = NULL; + char *who = NULL; + char *room = NULL; + char *id = NULL; + int utf8 = 0; + YList *members = NULL; + YList *l; + + for (l = pkt->hash; l; l = l->next) { + struct yahoo_pair *pair = l->data; + if (pair->key == 50) + host = pair->value; + + if (pair->key == 52) { /* invite */ + members = y_list_append(members, strdup(pair->value)); + } + if (pair->key == 53) /* logon */ + who = pair->value; + if (pair->key == 54) /* decline */ + who = pair->value; + if (pair->key == 55) /* unavailable (status == 2) */ + who = pair->value; + if (pair->key == 56) /* logoff */ + who = pair->value; + + if (pair->key == 57) + room = pair->value; + + if (pair->key == 58) /* join message */ + msg = pair->value; + if (pair->key == 14) /* decline/conf message */ + msg = pair->value; + + if (pair->key == 13) ; + if (pair->key == 16) /* error */ + msg = pair->value; + + if (pair->key == 1) /* my id */ + id = pair->value; + if (pair->key == 3) /* message sender */ + who = pair->value; + + if (pair->key == 97) + utf8 = atoi(pair->value); + } + + if (!room) + return; + + if (host) { + for (l = members; l; l = l->next) { + char *w = l->data; + if (!strcmp(w, host)) + break; + } + if (!l) + members = y_list_append(members, strdup(host)); + } + /* invite, decline, join, left, message -> status == 1 */ + + switch (pkt->service) { + case YAHOO_SERVICE_CONFINVITE: + if (pkt->status == 2) ; + else if (members) + YAHOO_CALLBACK(ext_yahoo_got_conf_invite) (yd-> + client_id, id, host, room, msg, members); + else if (msg) + YAHOO_CALLBACK(ext_yahoo_error) (yd->client_id, msg, 0, + E_CONFNOTAVAIL); + break; + case YAHOO_SERVICE_CONFADDINVITE: + if (pkt->status == 1) + YAHOO_CALLBACK(ext_yahoo_got_conf_invite) (yd-> + client_id, id, host, room, msg, members); + break; + case YAHOO_SERVICE_CONFDECLINE: + if (who) + YAHOO_CALLBACK(ext_yahoo_conf_userdecline) (yd-> + client_id, id, who, room, msg); + break; + case YAHOO_SERVICE_CONFLOGON: + if (who) + YAHOO_CALLBACK(ext_yahoo_conf_userjoin) (yd->client_id, + id, who, room); + break; + case YAHOO_SERVICE_CONFLOGOFF: + if (who) + YAHOO_CALLBACK(ext_yahoo_conf_userleave) (yd->client_id, + id, who, room); + break; + case YAHOO_SERVICE_CONFMSG: + if (who) + YAHOO_CALLBACK(ext_yahoo_conf_message) (yd->client_id, + id, who, room, msg, utf8); + break; + } +} + +static void yahoo_process_chat(struct yahoo_input_data *yid, + struct yahoo_packet *pkt) +{ + char *msg = NULL; + char *id = NULL; + char *who = NULL; + char *room = NULL; + char *topic = NULL; + YList *members = NULL; + struct yahoo_chat_member *currentmember = NULL; + int msgtype = 1; + int utf8 = 0; + int firstjoin = 0; + int membercount = 0; + int chaterr = 0; + YList *l; + + yahoo_dump_unhandled(pkt); + for (l = pkt->hash; l; l = l->next) { + struct yahoo_pair *pair = l->data; + + if (pair->key == 1) { + /* My identity */ + id = pair->value; + } + + if (pair->key == 104) { + /* Room name */ + room = pair->value; + } + + if (pair->key == 105) { + /* Room topic */ + topic = pair->value; + } + + if (pair->key == 108) { + /* Number of members in this packet */ + membercount = atoi(pair->value); + } + + if (pair->key == 109) { + /* message sender */ + who = pair->value; + + if (pkt->service == YAHOO_SERVICE_CHATJOIN) { + currentmember = + y_new0(struct yahoo_chat_member, 1); + currentmember->id = strdup(pair->value); + members = y_list_append(members, currentmember); + } + } + + if (pair->key == 110) { + /* age */ + if (pkt->service == YAHOO_SERVICE_CHATJOIN) + currentmember->age = atoi(pair->value); + } + + if (pair->key == 113) { + /* attribs */ + if (pkt->service == YAHOO_SERVICE_CHATJOIN) + currentmember->attribs = atoi(pair->value); + } + + if (pair->key == 141) { + /* alias */ + if (pkt->service == YAHOO_SERVICE_CHATJOIN) + currentmember->alias = strdup(pair->value); + } + + if (pair->key == 142) { + /* location */ + if (pkt->service == YAHOO_SERVICE_CHATJOIN) + currentmember->location = strdup(pair->value); + } + + if (pair->key == 130) { + /* first join */ + firstjoin = 1; + } + + if (pair->key == 117) { + /* message */ + msg = pair->value; + } + + if (pair->key == 124) { + /* Message type */ + msgtype = atoi(pair->value); + } + if (pair->key == 114) { + /* message error not sure what all the pair values mean */ + /* but -1 means no session in room */ + chaterr = atoi(pair->value); + } + } + + if (!room) { + if (pkt->service == YAHOO_SERVICE_CHATLOGOUT) { /* yahoo originated chat logout */ + YAHOO_CALLBACK(ext_yahoo_chat_yahoologout) (yid->yd-> + client_id, id); + return; + } + if (pkt->service == YAHOO_SERVICE_COMMENT && chaterr) { + YAHOO_CALLBACK(ext_yahoo_chat_yahooerror) (yid->yd-> + client_id, id); + return; + } + + WARNING(("We didn't get a room name, ignoring packet")); + return; + } + + switch (pkt->service) { + case YAHOO_SERVICE_CHATJOIN: + if (y_list_length(members) != membercount) { + WARNING(("Count of members doesn't match No. of members we got")); + } + if (firstjoin && members) { + YAHOO_CALLBACK(ext_yahoo_chat_join) (yid->yd->client_id, + id, room, topic, members, yid->fd); + } else if (who) { + if (y_list_length(members) != 1) { + WARNING(("Got more than 1 member on a normal join")); + } + /* this should only ever have one, but just in case */ + while (members) { + YList *n = members->next; + currentmember = members->data; + YAHOO_CALLBACK(ext_yahoo_chat_userjoin) (yid-> + yd->client_id, id, room, currentmember); + y_list_free_1(members); + members = n; + } + } + break; + case YAHOO_SERVICE_CHATEXIT: + if (who) { + YAHOO_CALLBACK(ext_yahoo_chat_userleave) (yid->yd-> + client_id, id, room, who); + } + break; + case YAHOO_SERVICE_COMMENT: + if (who) { + YAHOO_CALLBACK(ext_yahoo_chat_message) (yid->yd-> + client_id, id, who, room, msg, msgtype, utf8); + } + break; + } +} + +static void yahoo_process_message(struct yahoo_input_data *yid, + struct yahoo_packet *pkt) +{ + struct yahoo_data *yd = yid->yd; + YList *l; + YList *messages = NULL; + + struct m { + int i_31; + int i_32; + char *to; + char *from; + long tm; + char *msg; + int utf8; + char *gunk; + } *message = y_new0(struct m, 1); + + for (l = pkt->hash; l; l = l->next) { + struct yahoo_pair *pair = l->data; + if (pair->key == 1 || pair->key == 4) { + if (!message->from) + message->from = pair->value; + } else if (pair->key == 5) + message->to = pair->value; + else if (pair->key == 15) + message->tm = strtol(pair->value, NULL, 10); + else if (pair->key == 97) + message->utf8 = atoi(pair->value); + /* This comes when the official client sends us a message */ + else if (pair->key == 429) + message->gunk = pair->value; + /* user message *//* sys message */ + else if (pair->key == 14 || pair->key == 16) + message->msg = pair->value; + else if (pair->key == 31) { + if (message->i_31) { + messages = y_list_append(messages, message); + message = y_new0(struct m, 1); + } + message->i_31 = atoi(pair->value); + } else if (pair->key == 32) + message->i_32 = atoi(pair->value); + else + LOG(("yahoo_process_message: status: %d, key: %d, value: %s", pkt->status, pair->key, pair->value)); + } + + messages = y_list_append(messages, message); + + for (l = messages; l; l = l->next) { + message = l->data; + if (pkt->service == YAHOO_SERVICE_SYSMESSAGE) { + YAHOO_CALLBACK(ext_yahoo_system_message) (yd->client_id, + message->to, message->from, message->msg); + } else if (pkt->status <= 2 || pkt->status == 5) { + /* Confirm message receipt if we got the gunk */ + if(message->gunk) { + struct yahoo_packet *outpkt; + + outpkt = yahoo_packet_new(YAHOO_SERVICE_MESSAGE_CONFIRM, + YPACKET_STATUS_DEFAULT, 0); + yahoo_packet_hash(outpkt, 1, yd->user); + yahoo_packet_hash(outpkt, 5, message->from); + yahoo_packet_hash(outpkt, 302, "430"); + yahoo_packet_hash(outpkt, 430, message->gunk); + yahoo_packet_hash(outpkt, 303, "430"); + yahoo_packet_hash(outpkt, 450, "0"); + yahoo_send_packet(yid, outpkt, 0); + + yahoo_packet_free(outpkt); + } + + if (!strcmp(message->msg, "<ding>")) + YAHOO_CALLBACK(ext_yahoo_got_buzz) (yd->client_id, + message->to, message->from, message->tm); + else + YAHOO_CALLBACK(ext_yahoo_got_im) (yd->client_id, + message->to, message->from, message->msg, + message->tm, pkt->status, message->utf8); + } else if (pkt->status == 0xffffffff) { + YAHOO_CALLBACK(ext_yahoo_error) (yd->client_id, + message->msg, 0, E_SYSTEM); + } + FREE(message); + } + + y_list_free(messages); +} + +/* + * Here's what multi-level packets look like. Data in brackets is the value. + * + * 3 level: + * ======= + * + * 302 (318) - Beginning level 1 + * 300 (318) - Begin level 2 + * 302 (319) - End level 2 header + * 300 (319) - Begin level 3 + * 301 (319) - End level 3 + * 303 (319) - End level 2 + * 303 (318) - End level 1 + * + * 2 level: + * ======= + * + * 302 (315) - Beginning level 1 + * 300 (315) - Begin level 2 + * 301 (315) - End level 2 + * 303 (315) - End level 1 + * + */ +static void yahoo_process_status(struct yahoo_input_data *yid, + struct yahoo_packet *pkt) +{ + YList *l; + struct yahoo_data *yd = yid->yd; + + struct yahoo_process_status_entry *u; + + YList *users = 0; + + if (pkt->service == YAHOO_SERVICE_LOGOFF && pkt->status == -1) { + YAHOO_CALLBACK(ext_yahoo_login_response) (yd->client_id, + YAHOO_LOGIN_DUPL, NULL); + return; + } + + /* + * Status updates may be spread accross multiple packets and not + * even on buddy boundaries, so keeping some state is important. + * So, continue where we left off, and only add a user entry to + * the list once it's complete (301-315 End buddy). + */ + u = yd->half_user; + + for (l = pkt->hash; l; l = l->next) { + struct yahoo_pair *pair = l->data; + + switch (pair->key) { + case 300: /* Begin buddy */ + if (!strcmp(pair->value, "315") && !u) { + u = yd->half_user = y_new0(struct yahoo_process_status_entry, 1); + } + break; + case 301: /* End buddy */ + if (!strcmp(pair->value, "315") && u) { + /* Sometimes user info comes in an odd format with no + "begin buddy" but *with* an "end buddy". Don't add + it twice. */ + if (!y_list_find(users, u)) + users = y_list_prepend(users, u); + u = yd->half_user = NULL; + } + break; + case 0: /* we won't actually do anything with this */ + NOTICE(("key %d:%s", pair->key, pair->value)); + break; + case 1: /* we don't get the full buddy list here. */ + if (!yd->logged_in) { + yd->logged_in = 1; + if (yd->current_status < 0) + yd->current_status = yd->initial_status; + YAHOO_CALLBACK(ext_yahoo_login_response) (yd-> + client_id, YAHOO_LOGIN_OK, NULL); + } + break; + case 8: /* how many online buddies we have */ + NOTICE(("key %d:%s", pair->key, pair->value)); + break; + case 7: /* the current buddy */ + if (!u) { + /* This will only happen in case of a single level message */ + u = y_new0(struct yahoo_process_status_entry, 1); + users = y_list_prepend(users, u); + } + u->name = pair->value; + break; + case 10: /* state */ + u->state = strtol(pair->value, NULL, 10); + break; + case 19: /* custom status message */ + u->msg = pair->value; + break; + case 47: /* is it an away message or not. Not applicable for YMSG16 anymore */ + u->away = atoi(pair->value); + break; + case 137: /* seconds idle */ + u->idle = atoi(pair->value); + break; + case 11: /* this is the buddy's session id */ + u->buddy_session = atoi(pair->value); + break; + case 17: /* in chat? */ + u->f17 = atoi(pair->value); + break; + case 13: /* bitmask, bit 0 = pager, bit 1 = chat, bit 2 = game */ + u->flags = atoi(pair->value); + break; + case 60: /* SMS -> 1 MOBILE USER */ + /* sometimes going offline makes this 2, but invisible never sends it */ + u->mobile = atoi(pair->value); + break; + case 138: + u->f138 = atoi(pair->value); + break; + case 184: + u->f184 = pair->value; + break; + case 192: + u->f192 = atoi(pair->value); + break; + case 10001: + u->f10001 = atoi(pair->value); + break; + case 10002: + u->f10002 = atoi(pair->value); + break; + case 198: + u->f198 = atoi(pair->value); + break; + case 197: + u->f197 = pair->value; + break; + case 205: + u->f205 = pair->value; + break; + case 213: + u->f213 = atoi(pair->value); + break; + case 16: /* Custom error message */ + YAHOO_CALLBACK(ext_yahoo_error) (yd->client_id, + pair->value, 0, E_CUSTOM); + break; + default: + WARNING(("unknown status key %d:%s", pair->key, + pair->value)); + break; + } + } + + while (users) { + YList *t = users; + struct yahoo_process_status_entry *u = users->data; + + if (u->name != NULL) { + if (pkt->service == + YAHOO_SERVICE_LOGOFF + /*|| u->flags == 0 No flags for YMSG16 */ ) { + YAHOO_CALLBACK(ext_yahoo_status_changed) (yd-> + client_id, u->name, + YAHOO_STATUS_OFFLINE, NULL, 1, 0, 0); + } else { + /* Key 47 always seems to be 1 for YMSG16 */ + if (!u->state) + u->away = 0; + else + u->away = 1; + + YAHOO_CALLBACK(ext_yahoo_status_changed) (yd-> + client_id, u->name, u->state, u->msg, + u->away, u->idle, u->mobile); + } + } + + users = y_list_remove_link(users, users); + y_list_free_1(t); + FREE(u); + } +} + +static void yahoo_process_buddy_list(struct yahoo_input_data *yid, + struct yahoo_packet *pkt) +{ + struct yahoo_data *yd = yid->yd; + YList *l; + int last_packet = 0; + char *cur_group = NULL; + struct yahoo_buddy *newbud = NULL; + + /* we could be getting multiple packets here */ + for (l = pkt->hash; l; l = l->next) { + struct yahoo_pair *pair = l->data; + + switch (pair->key) { + case 300: + case 301: + case 302: + break; /* Separators. Our logic does not need them */ + case 303: + if (318 == atoi(pair->value)) + last_packet = 1; + break; + case 65: + cur_group = strdup(pair->value); + break; + case 7: + newbud = y_new0(struct yahoo_buddy, 1); + newbud->id = strdup(pair->value); + if (cur_group) + newbud->group = strdup(cur_group); + else if (yd->buddies) { + struct yahoo_buddy *lastbud = + (struct yahoo_buddy *)y_list_nth(yd-> + buddies, + y_list_length(yd->buddies) - 1)->data; + newbud->group = strdup(lastbud->group); + } else + newbud->group = strdup("Buddies"); + + yd->buddies = y_list_append(yd->buddies, newbud); + + break; + } + } + + /* we could be getting multiple packets here */ + if (pkt->hash && !last_packet) + return; + + YAHOO_CALLBACK(ext_yahoo_got_buddies) (yd->client_id, yd->buddies); + + /* Logged in */ + if (!yd->logged_in) { + yd->logged_in = 1; + if (yd->current_status < 0) + yd->current_status = yd->initial_status; + YAHOO_CALLBACK(ext_yahoo_login_response) (yd->client_id, + YAHOO_LOGIN_OK, NULL); + + /* + yahoo_set_away(yd->client_id, yd->initial_status, NULL, + (yd->initial_status == YAHOO_STATUS_AVAILABLE) ? 0 : 1); + + yahoo_get_yab(yd->client_id); + */ + } + +} + +static void yahoo_process_list(struct yahoo_input_data *yid, + struct yahoo_packet *pkt) +{ + struct yahoo_data *yd = yid->yd; + YList *l; + + /* we could be getting multiple packets here */ + for (l = pkt->hash; l; l = l->next) { + struct yahoo_pair *pair = l->data; + + switch (pair->key) { + case 89: /* identities */ + { + char **identities = + y_strsplit(pair->value, ",", -1); + int i; + for (i = 0; identities[i]; i++) + yd->identities = + y_list_append(yd->identities, + strdup(identities[i])); + y_strfreev(identities); + } + YAHOO_CALLBACK(ext_yahoo_got_identities) (yd->client_id, + yd->identities); + break; + case 59: /* cookies */ + if (pair->value[0] == 'Y') { + FREE(yd->cookie_y); + FREE(yd->login_cookie); + + yd->cookie_y = getcookie(pair->value); + yd->login_cookie = getlcookie(yd->cookie_y); + + } else if (pair->value[0] == 'T') { + FREE(yd->cookie_t); + yd->cookie_t = getcookie(pair->value); + + } else if (pair->value[0] == 'C') { + FREE(yd->cookie_c); + yd->cookie_c = getcookie(pair->value); + } + + break; + case 3: /* my id */ + case 90: /* 1 */ + case 100: /* 0 */ + case 101: /* NULL */ + case 102: /* NULL */ + case 93: /* 86400/1440 */ + break; + } + } + + if (yd->cookie_y && yd->cookie_t) /* We don't get cookie_c anymore */ + YAHOO_CALLBACK(ext_yahoo_got_cookies) (yd->client_id); +} + +static void yahoo_process_verify(struct yahoo_input_data *yid, + struct yahoo_packet *pkt) +{ + struct yahoo_data *yd = yid->yd; + + if (pkt->status != 0x01) { + DEBUG_MSG(("expected status: 0x01, got: %d", pkt->status)); + YAHOO_CALLBACK(ext_yahoo_login_response) (yd->client_id, + YAHOO_LOGIN_LOCK, ""); + return; + } + + pkt = yahoo_packet_new(YAHOO_SERVICE_AUTH, YPACKET_STATUS_DEFAULT, + yd->session_id); + + yahoo_packet_hash(pkt, 1, yd->user); + yahoo_send_packet(yid, pkt, 0); + + yahoo_packet_free(pkt); + +} + +static void yahoo_process_picture_checksum(struct yahoo_input_data *yid, + struct yahoo_packet *pkt) +{ + struct yahoo_data *yd = yid->yd; + char *from = NULL; + char *to = NULL; + int checksum = 0; + YList *l; + + for (l = pkt->hash; l; l = l->next) { + struct yahoo_pair *pair = l->data; + + switch (pair->key) { + case 1: + case 4: + from = pair->value; + case 5: + to = pair->value; + break; + case 212: + break; + case 192: + checksum = atoi(pair->value); + break; + } + } + + YAHOO_CALLBACK(ext_yahoo_got_buddyicon_checksum) (yd->client_id, to, + from, checksum); +} + +static void yahoo_process_picture(struct yahoo_input_data *yid, + struct yahoo_packet *pkt) +{ + struct yahoo_data *yd = yid->yd; + char *url = NULL; + char *from = NULL; + char *to = NULL; + int status = 0; + int checksum = 0; + YList *l; + + for (l = pkt->hash; l; l = l->next) { + struct yahoo_pair *pair = l->data; + + switch (pair->key) { + case 1: + case 4: /* sender */ + from = pair->value; + break; + case 5: /* we */ + to = pair->value; + break; + case 13: /* request / sending */ + status = atoi(pair->value); + break; + case 20: /* url */ + url = pair->value; + break; + case 192: /*checksum */ + checksum = atoi(pair->value); + break; + } + } + + switch (status) { + case 1: /* this is a request, ignore for now */ + YAHOO_CALLBACK(ext_yahoo_got_buddyicon_request) (yd->client_id, + to, from); + break; + case 2: /* this is cool - we get a picture :) */ + YAHOO_CALLBACK(ext_yahoo_got_buddyicon) (yd->client_id, to, + from, url, checksum); + break; + } +} + +static void yahoo_process_picture_upload(struct yahoo_input_data *yid, + struct yahoo_packet *pkt) +{ + struct yahoo_data *yd = yid->yd; + YList *l; + char *url = NULL; + + if (pkt->status != 1) + return; /* something went wrong */ + + for (l = pkt->hash; l; l = l->next) { + struct yahoo_pair *pair = l->data; + + switch (pair->key) { + case 5: /* we */ + break; + case 20: /* url */ + url = pair->value; + break; + case 27: /* local filename */ + break; + case 38: /* time */ + break; + } + } + + YAHOO_CALLBACK(ext_yahoo_buddyicon_uploaded) (yd->client_id, url); +} + +void yahoo_login(int id, int initial) +{ + struct yahoo_data *yd = find_conn_by_id(id); + struct connect_callback_data *ccd; + struct yahoo_server_settings *yss; + int tag; + + char *host; + + struct yahoo_input_data *yid = y_new0(struct yahoo_input_data, 1); + yid->yd = yd; + yid->type = YAHOO_CONNECTION_PAGER; + inputs = y_list_prepend(inputs, yid); + + yd->initial_status = initial; + yss = yd->server_settings; + + ccd = y_new0(struct connect_callback_data, 1); + ccd->yd = yd; + + host = yss->pager_host; + + if (!host) + host = yss->pager_host_list[0]; + + tag = YAHOO_CALLBACK(ext_yahoo_connect_async) (yd->client_id, + host, yss->pager_port, yahoo_connected, ccd, 0); + + /* + * if tag <= 0, then callback has already been called + * so ccd will have been freed + */ + if (tag > 0) + ccd->tag = tag; + else if (tag < 0) + YAHOO_CALLBACK(ext_yahoo_login_response) (yd->client_id, + YAHOO_LOGIN_SOCK, NULL); +} + +struct yahoo_https_auth_data +{ + struct yahoo_input_data *yid; + char *token; + char *chal; +}; + +static void yahoo_https_auth_token_init(struct yahoo_https_auth_data *had); +static void yahoo_https_auth_token_finish(struct http_request *req); +static void yahoo_https_auth_init(struct yahoo_https_auth_data *had); +static void yahoo_https_auth_finish(struct http_request *req); + +/* Extract a value from a login.yahoo.com response. Assume CRLF-linebreaks + and FAIL miserably if they're not there... */ +static char *yahoo_ha_find_key(char *response, char *key) +{ + char *s, *end; + int len = strlen(key); + + s = response; + do { + if (strncmp(s, key, len) == 0 && s[len] == '=') { + s += len + 1; + if ((end = strchr(s, '\r'))) + return g_strndup(s, end - s); + else + return g_strdup(s); + } + + if ((s = strchr(s, '\n'))) + s ++; + } while (s && *s); + + return NULL; +} + +static enum yahoo_status yahoo_https_status_parse(int code) +{ + switch (code) + { + case 1212: return YAHOO_LOGIN_PASSWD; + case 1213: return YAHOO_LOGIN_LOCK; + case 1235: return YAHOO_LOGIN_UNAME; + default: return (enum yahoo_status) code; + } +} + +static void yahoo_https_auth(struct yahoo_input_data *yid, const char *seed, const char *sn) +{ + struct yahoo_https_auth_data *had = g_new0(struct yahoo_https_auth_data, 1); + + had->yid = yid; + had->chal = g_strdup(seed); + + yahoo_https_auth_token_init(had); +} + +static void yahoo_https_auth_token_init(struct yahoo_https_auth_data *had) +{ + struct yahoo_input_data *yid = had->yid; + struct yahoo_data *yd = yid->yd; + struct http_request *req; + char *login, *passwd, *chal; + char *url; + + login = g_strndup(yd->user, 3 * strlen(yd->user)); + http_encode(login); + passwd = g_strndup(yd->password, 3 * strlen(yd->password)); + http_encode(passwd); + chal = g_strndup(had->chal, 3 * strlen(had->chal)); + http_encode(chal); + + url = g_strdup_printf("https://login.yahoo.com/config/pwtoken_get?src=ymsgr&ts=%d&login=%s&passwd=%s&chal=%s", + (int) time(NULL), login, passwd, chal); + + req = http_dorequest_url(url, yahoo_https_auth_token_finish, had); + + g_free(url); + g_free(chal); + g_free(passwd); + g_free(login); +} + +static void yahoo_https_auth_token_finish(struct http_request *req) +{ + struct yahoo_https_auth_data *had = req->data; + struct yahoo_input_data *yid; + struct yahoo_data *yd; + int st; + + if (y_list_find(inputs, had->yid) == NULL) + return; + + yid = had->yid; + yd = yid->yd; + + if (req->status_code != 200) { + YAHOO_CALLBACK(ext_yahoo_login_response) (yd->client_id, 2000 + req->status_code, NULL); + goto fail; + } + + if (sscanf(req->reply_body, "%d", &st) != 1 || st != 0) { + YAHOO_CALLBACK(ext_yahoo_login_response) (yd->client_id, yahoo_https_status_parse(st), NULL); + goto fail; + } + + if ((had->token = yahoo_ha_find_key(req->reply_body, "ymsgr")) == NULL) { + YAHOO_CALLBACK(ext_yahoo_login_response) (yd->client_id, 3001, NULL); + goto fail; + } + + yahoo_https_auth_init(had); + return; + +fail: + g_free(had->token); + g_free(had->chal); + g_free(had); +} + +static void yahoo_https_auth_init(struct yahoo_https_auth_data *had) +{ + struct http_request *req; + char *url; + + url = g_strdup_printf("https://login.yahoo.com/config/pwtoken_login?src=ymsgr&ts=%d&token=%s", + (int) time(NULL), had->token); + + req = http_dorequest_url(url, yahoo_https_auth_finish, had); + + g_free(url); +} + +static void yahoo_https_auth_finish(struct http_request *req) +{ + struct yahoo_https_auth_data *had = req->data; + struct yahoo_input_data *yid; + struct yahoo_data *yd; + struct yahoo_packet *pack; + char *crumb = NULL; + int st; + + if (y_list_find(inputs, had->yid) == NULL) + return; + + yid = had->yid; + yd = yid->yd; + + md5_byte_t result[16]; + md5_state_t ctx; + + unsigned char yhash[32]; + + if (req->status_code != 200) { + YAHOO_CALLBACK(ext_yahoo_login_response) (yd->client_id, 2000 + req->status_code, NULL); + goto fail; + } + + if (sscanf(req->reply_body, "%d", &st) != 1 || st != 0) { + YAHOO_CALLBACK(ext_yahoo_login_response) (yd->client_id, yahoo_https_status_parse(st), NULL); + goto fail; + } + + if ((yd->cookie_y = yahoo_ha_find_key(req->reply_body, "Y")) == NULL || + (yd->cookie_t = yahoo_ha_find_key(req->reply_body, "T")) == NULL || + (crumb = yahoo_ha_find_key(req->reply_body, "crumb")) == NULL) { + YAHOO_CALLBACK(ext_yahoo_login_response) (yd->client_id, 3002, NULL); + goto fail; + } + + md5_init(&ctx); + md5_append(&ctx, (unsigned char*) crumb, 11); + md5_append(&ctx, (unsigned char*) had->chal, strlen(had->chal)); + md5_finish(&ctx, result); + to_y64(yhash, result, 16); + + pack = yahoo_packet_new(YAHOO_SERVICE_AUTHRESP, yd->initial_status, yd->session_id); + yahoo_packet_hash(pack, 1, yd->user); + yahoo_packet_hash(pack, 0, yd->user); + yahoo_packet_hash(pack, 277, yd->cookie_y); + yahoo_packet_hash(pack, 278, yd->cookie_t); + yahoo_packet_hash(pack, 307, (char*) yhash); + yahoo_packet_hash(pack, 244, "524223"); + yahoo_packet_hash(pack, 2, yd->user); + yahoo_packet_hash(pack, 2, "1"); + yahoo_packet_hash(pack, 98, "us"); + yahoo_packet_hash(pack, 135, "7.5.0.647"); + + yahoo_send_packet(yid, pack, 0); + + yahoo_packet_free(pack); + +fail: + g_free(crumb); + g_free(had->token); + g_free(had->chal); + g_free(had); +} + +static void yahoo_process_auth(struct yahoo_input_data *yid, + struct yahoo_packet *pkt) +{ + char *seed = NULL; + char *sn = NULL; + YList *l = pkt->hash; + int m = 0; + struct yahoo_data *yd = yid->yd; + + while (l) { + struct yahoo_pair *pair = l->data; + + switch (pair->key) { + case 94: + seed = pair->value; + break; + case 1: + sn = pair->value; + break; + case 13: + m = atoi(pair->value); + break; + } + l = l->next; + } + + if (!seed) + return; + + if (m==2) + yahoo_https_auth(yid, seed, sn); + else { + /* call error */ + WARNING(("unknown auth type %d", m)); + YAHOO_CALLBACK(ext_yahoo_login_response) (yd->client_id, + YAHOO_LOGIN_UNKNOWN, NULL); + } +} + +static void yahoo_process_auth_resp(struct yahoo_input_data *yid, + struct yahoo_packet *pkt) +{ + struct yahoo_data *yd = yid->yd; + char *login_id; + char *handle; + char *url = NULL; + int login_status = -1; + + YList *l; + + for (l = pkt->hash; l; l = l->next) { + struct yahoo_pair *pair = l->data; + if (pair->key == 0) + login_id = pair->value; + else if (pair->key == 1) + handle = pair->value; + else if (pair->key == 20) + url = pair->value; + else if (pair->key == 66) + login_status = atoi(pair->value); + } + + if (pkt->status == YPACKET_STATUS_DISCONNECTED) { + YAHOO_CALLBACK(ext_yahoo_login_response) (yd->client_id, + login_status, url); + /* yahoo_logoff(yd->client_id); */ + } +} + +static void yahoo_process_mail(struct yahoo_input_data *yid, + struct yahoo_packet *pkt) +{ + struct yahoo_data *yd = yid->yd; + char *who = NULL; + char *email = NULL; + char *subj = NULL; + int count = 0; + YList *l; + + for (l = pkt->hash; l; l = l->next) { + struct yahoo_pair *pair = l->data; + if (pair->key == 9) + count = strtol(pair->value, NULL, 10); + else if (pair->key == 43) + who = pair->value; + else if (pair->key == 42) + email = pair->value; + else if (pair->key == 18) + subj = pair->value; + else + LOG(("key: %d => value: %s", pair->key, pair->value)); + } + + if (who && email && subj) { + char from[1024]; + snprintf(from, sizeof(from), "%s (%s)", who, email); + YAHOO_CALLBACK(ext_yahoo_mail_notify) (yd->client_id, from, + subj, count); + } else if (count > 0) + YAHOO_CALLBACK(ext_yahoo_mail_notify) (yd->client_id, NULL, + NULL, count); +} + +static void yahoo_process_new_contact(struct yahoo_input_data *yid, + struct yahoo_packet *pkt) +{ + struct yahoo_data *yd = yid->yd; + char *me = NULL; + char *who = NULL; + char *msg = NULL; + int online = -1; + + YList *l; + + for (l = pkt->hash; l; l = l->next) { + struct yahoo_pair *pair = l->data; + if (pair->key == 4) + who = pair->value; + else if (pair->key == 5) + me = pair->value; + else if (pair->key == 14) + msg = pair->value; + else if (pair->key == 13) + online = strtol(pair->value, NULL, 10); + } + + if (who && online < 0) + YAHOO_CALLBACK(ext_yahoo_contact_added) (yd->client_id, me, who, + msg); + else if (online == 2) + YAHOO_CALLBACK(ext_yahoo_rejected) (yd->client_id, who, msg); +} + +/* UNUSED? */ +static void yahoo_process_contact(struct yahoo_input_data *yid, + struct yahoo_packet *pkt) +{ + struct yahoo_data *yd = yid->yd; + char *id = NULL; + char *who = NULL; + char *msg = NULL; + char *name = NULL; + long tm = 0L; + int state = YAHOO_STATUS_AVAILABLE; + int online = 0; + int away = 0; + int idle = 0; + int mobile = 0; + + YList *l; + + for (l = pkt->hash; l; l = l->next) { + struct yahoo_pair *pair = l->data; + if (pair->key == 1) + id = pair->value; + else if (pair->key == 3) + who = pair->value; + else if (pair->key == 14) + msg = pair->value; + else if (pair->key == 7) + name = pair->value; + else if (pair->key == 10) + state = strtol(pair->value, NULL, 10); + else if (pair->key == 15) + tm = strtol(pair->value, NULL, 10); + else if (pair->key == 13) + online = strtol(pair->value, NULL, 10); + else if (pair->key == 47) + away = strtol(pair->value, NULL, 10); + else if (pair->key == 137) + idle = strtol(pair->value, NULL, 10); + else if (pair->key == 60) + mobile = strtol(pair->value, NULL, 10); + + } + + if (id) + YAHOO_CALLBACK(ext_yahoo_contact_added) (yd->client_id, id, who, + msg); + else if (name) + YAHOO_CALLBACK(ext_yahoo_status_changed) (yd->client_id, name, + state, msg, away, idle, mobile); + else if (pkt->status == 0x07) + YAHOO_CALLBACK(ext_yahoo_rejected) (yd->client_id, who, msg); +} + +static void yahoo_process_buddyadd(struct yahoo_input_data *yid, + struct yahoo_packet *pkt) +{ + struct yahoo_data *yd = yid->yd; + char *who = NULL; + char *where = NULL; + int status = 0; + char *me = NULL; + + struct yahoo_buddy *bud = NULL; + + YList *l; + for (l = pkt->hash; l; l = l->next) { + struct yahoo_pair *pair = l->data; + if (pair->key == 1) + me = pair->value; + if (pair->key == 7) + who = pair->value; + if (pair->key == 65) + where = pair->value; + if (pair->key == 66) + status = strtol(pair->value, NULL, 10); + } + + if (!who) + return; + if (!where) + where = "Unknown"; + + bud = y_new0(struct yahoo_buddy, 1); + bud->id = strdup(who); + bud->group = strdup(where); + bud->real_name = NULL; + + yd->buddies = y_list_append(yd->buddies, bud); + +#if 0 + /* BitlBee: This seems to be wrong in my experience. I think: + status = 0: Success + status = 2: Already on list + status = 3: Doesn't exist + status = 42: Invalid handle (possibly banned/reserved, I get it for + handles like joe or jjjjjj) + Haven't seen others yet. But whenever the add is successful, there + will be a separate "went online" packet when the auth. request is + accepted. Couldn't find any test account that doesn't require auth. + unfortunately (if there is even such a thing?) */ + + /* A non-zero status (i've seen 2) seems to mean the buddy is already + * added and is online */ + if (status) { + LOG(("Setting online see packet for info")); + yahoo_dump_unhandled(pkt); + YAHOO_CALLBACK(ext_yahoo_status_changed) (yd->client_id, who, + YAHOO_STATUS_AVAILABLE, NULL, 0, 0, 0); + } +#endif + /* BitlBee: Need ACK of added buddy, if it was successful. */ + if (status == 0) { + YList *tmp = y_list_append(NULL, bud); + YAHOO_CALLBACK(ext_yahoo_got_buddies) (yd->client_id, tmp); + y_list_free(tmp); + } +} + +static void yahoo_process_buddydel(struct yahoo_input_data *yid, + struct yahoo_packet *pkt) +{ + struct yahoo_data *yd = yid->yd; + char *who = NULL; + char *where = NULL; + int unk_66 = 0; + char *me = NULL; + struct yahoo_buddy *bud; + + YList *buddy; + + YList *l; + for (l = pkt->hash; l; l = l->next) { + struct yahoo_pair *pair = l->data; + if (pair->key == 1) + me = pair->value; + else if (pair->key == 7) + who = pair->value; + else if (pair->key == 65) + where = pair->value; + else if (pair->key == 66) + unk_66 = strtol(pair->value, NULL, 10); + else + DEBUG_MSG(("unknown key: %d = %s", pair->key, + pair->value)); + } + + if (!who || !where) + return; + + bud = y_new0(struct yahoo_buddy, 1); + bud->id = strdup(who); + bud->group = strdup(where); + + buddy = y_list_find_custom(yd->buddies, bud, is_same_bud); + + FREE(bud->id); + FREE(bud->group); + FREE(bud); + + if (buddy) { + bud = buddy->data; + yd->buddies = y_list_remove_link(yd->buddies, buddy); + y_list_free_1(buddy); + + FREE(bud->id); + FREE(bud->group); + FREE(bud->real_name); + FREE(bud); + + bud = NULL; + } +} + +static void yahoo_process_ignore(struct yahoo_input_data *yid, + struct yahoo_packet *pkt) +{ + char *who = NULL; + int status = 0; + char *me = NULL; + int un_ignore = 0; + + YList *l; + for (l = pkt->hash; l; l = l->next) { + struct yahoo_pair *pair = l->data; + if (pair->key == 0) + who = pair->value; + if (pair->key == 1) + me = pair->value; + if (pair->key == 13) /* 1 == ignore, 2 == unignore */ + un_ignore = strtol(pair->value, NULL, 10); + if (pair->key == 66) + status = strtol(pair->value, NULL, 10); + } + + /* + * status + * 0 - ok + * 2 - already in ignore list, could not add + * 3 - not in ignore list, could not delete + * 12 - is a buddy, could not add + */ + +/* if(status) + YAHOO_CALLBACK(ext_yahoo_error)(yd->client_id, who, 0, status); +*/ +} + +static void yahoo_process_voicechat(struct yahoo_input_data *yid, + struct yahoo_packet *pkt) +{ + char *who = NULL; + char *me = NULL; + char *room = NULL; + char *voice_room = NULL; + + YList *l; + for (l = pkt->hash; l; l = l->next) { + struct yahoo_pair *pair = l->data; + if (pair->key == 4) + who = pair->value; + if (pair->key == 5) + me = pair->value; + if (pair->key == 13) + voice_room = pair->value; + if (pair->key == 57) + room = pair->value; + } + + NOTICE(("got voice chat invite from %s in %s to identity %s", who, room, + me)); + /* + * send: s:0 1:me 5:who 57:room 13:1 + * ???? s:4 5:who 10:99 19:-1615114531 + * gotr: s:4 5:who 10:99 19:-1615114615 + * ???? s:1 5:me 4:who 57:room 13:3room + * got: s:1 5:me 4:who 57:room 13:1room + * rej: s:0 1:me 5:who 57:room 13:3 + * rejr: s:4 5:who 10:99 19:-1617114599 + */ +} + +static void yahoo_process_ping(struct yahoo_input_data *yid, + struct yahoo_packet *pkt) +{ + char *errormsg = NULL; + + YList *l; + for (l = pkt->hash; l; l = l->next) { + struct yahoo_pair *pair = l->data; + if (pair->key == 16) + errormsg = pair->value; + } + + NOTICE(("got ping packet")); + YAHOO_CALLBACK(ext_yahoo_got_ping) (yid->yd->client_id, errormsg); +} + +static void yahoo_process_buddy_change_group(struct yahoo_input_data *yid, + struct yahoo_packet *pkt) +{ + YList *l; + char *me = NULL; + char *who = NULL; + char *old_group = NULL; + char *new_group = NULL; + + for (l = pkt->hash; l; l = l->next) { + struct yahoo_pair *pair = l->data; + if (pair->key == 1) + me = pair->value; + if (pair->key == 7) + who = pair->value; + if (pair->key == 224) + old_group = pair->value; + if (pair->key == 264) + new_group = pair->value; + } + + YAHOO_CALLBACK(ext_yahoo_got_buddy_change_group) (yid->yd->client_id, + me, who, old_group, new_group); +} + +static void _yahoo_webcam_get_server_connected(void *fd, int error, void *d) +{ + struct yahoo_input_data *yid = d; + char *who = yid->wcm->user; + char *data = NULL; + char *packet = NULL; + unsigned char magic_nr[] = { 0, 1, 0 }; + unsigned char header_len = 8; + unsigned int len = 0; + unsigned int pos = 0; + + if (error || !fd) { + FREE(who); + FREE(yid); + return; + } + + yid->fd = fd; + inputs = y_list_prepend(inputs, yid); + + /* send initial packet */ + if (who) + data = strdup("<RVWCFG>"); + else + data = strdup("<RUPCFG>"); + yahoo_add_to_send_queue(yid, data, strlen(data)); + FREE(data); + + /* send data */ + if (who) { + data = strdup("g="); + data = y_string_append(data, who); + data = y_string_append(data, "\r\n"); + } else { + data = strdup("f=1\r\n"); + } + len = strlen(data); + packet = y_new0(char, header_len + len); + packet[pos++] = header_len; + memcpy(packet + pos, magic_nr, sizeof(magic_nr)); + pos += sizeof(magic_nr); + pos += yahoo_put32(packet + pos, len); + memcpy(packet + pos, data, len); + pos += len; + yahoo_add_to_send_queue(yid, packet, pos); + FREE(packet); + FREE(data); + + yid->read_tag = + YAHOO_CALLBACK(ext_yahoo_add_handler) (yid->yd->client_id, fd, + YAHOO_INPUT_READ, yid); +} + +static void yahoo_webcam_get_server(struct yahoo_input_data *y, char *who, + char *key) +{ + struct yahoo_input_data *yid = y_new0(struct yahoo_input_data, 1); + struct yahoo_server_settings *yss = y->yd->server_settings; + + yid->type = YAHOO_CONNECTION_WEBCAM_MASTER; + yid->yd = y->yd; + yid->wcm = y_new0(struct yahoo_webcam, 1); + yid->wcm->user = who ? strdup(who) : NULL; + yid->wcm->direction = who ? YAHOO_WEBCAM_DOWNLOAD : YAHOO_WEBCAM_UPLOAD; + yid->wcm->key = strdup(key); + + YAHOO_CALLBACK(ext_yahoo_connect_async) (yid->yd->client_id, + yss->webcam_host, yss->webcam_port, + _yahoo_webcam_get_server_connected, yid, 0); + +} + +static YList *webcam_queue = NULL; +static void yahoo_process_webcam_key(struct yahoo_input_data *yid, + struct yahoo_packet *pkt) +{ + char *me = NULL; + char *key = NULL; + char *who = NULL; + + YList *l; + yahoo_dump_unhandled(pkt); + for (l = pkt->hash; l; l = l->next) { + struct yahoo_pair *pair = l->data; + if (pair->key == 5) + me = pair->value; + if (pair->key == 61) + key = pair->value; + } + + l = webcam_queue; + if (!l) + return; + who = l->data; + webcam_queue = y_list_remove_link(webcam_queue, webcam_queue); + y_list_free_1(l); + yahoo_webcam_get_server(yid, who, key); + FREE(who); +} + +static void yahoo_packet_process(struct yahoo_input_data *yid, + struct yahoo_packet *pkt) +{ + DEBUG_MSG(("yahoo_packet_process: 0x%02x", pkt->service)); + switch (pkt->service) { + case YAHOO_SERVICE_USERSTAT: + case YAHOO_SERVICE_LOGON: + case YAHOO_SERVICE_LOGOFF: + case YAHOO_SERVICE_ISAWAY: + case YAHOO_SERVICE_ISBACK: + case YAHOO_SERVICE_GAMELOGON: + case YAHOO_SERVICE_GAMELOGOFF: + case YAHOO_SERVICE_IDACT: + case YAHOO_SERVICE_IDDEACT: + case YAHOO_SERVICE_Y6_STATUS_UPDATE: + case YAHOO_SERVICE_Y8_STATUS: + yahoo_process_status(yid, pkt); + break; + case YAHOO_SERVICE_NOTIFY: + yahoo_process_notify(yid, pkt); + break; + case YAHOO_SERVICE_MESSAGE: + case YAHOO_SERVICE_GAMEMSG: + case YAHOO_SERVICE_SYSMESSAGE: + yahoo_process_message(yid, pkt); + break; + case YAHOO_SERVICE_NEWMAIL: + yahoo_process_mail(yid, pkt); + break; + case YAHOO_SERVICE_Y7_AUTHORIZATION: + yahoo_process_new_contact(yid, pkt); + break; + case YAHOO_SERVICE_NEWCONTACT: + yahoo_process_contact(yid, pkt); + break; + case YAHOO_SERVICE_LIST: + yahoo_process_list(yid, pkt); + break; + case YAHOO_SERVICE_VERIFY: + yahoo_process_verify(yid, pkt); + break; + case YAHOO_SERVICE_AUTH: + yahoo_process_auth(yid, pkt); + break; + case YAHOO_SERVICE_AUTHRESP: + yahoo_process_auth_resp(yid, pkt); + break; + case YAHOO_SERVICE_CONFINVITE: + case YAHOO_SERVICE_CONFADDINVITE: + case YAHOO_SERVICE_CONFDECLINE: + case YAHOO_SERVICE_CONFLOGON: + case YAHOO_SERVICE_CONFLOGOFF: + case YAHOO_SERVICE_CONFMSG: + yahoo_process_conference(yid, pkt); + break; + case YAHOO_SERVICE_CHATONLINE: + case YAHOO_SERVICE_CHATGOTO: + case YAHOO_SERVICE_CHATJOIN: + case YAHOO_SERVICE_CHATLEAVE: + case YAHOO_SERVICE_CHATEXIT: + case YAHOO_SERVICE_CHATLOGOUT: + case YAHOO_SERVICE_CHATPING: + case YAHOO_SERVICE_COMMENT: + yahoo_process_chat(yid, pkt); + break; + case YAHOO_SERVICE_P2PFILEXFER: + case YAHOO_SERVICE_Y7_FILETRANSFER: + yahoo_process_filetransfer(yid, pkt); + break; + case YAHOO_SERVICE_Y7_FILETRANSFERINFO: + yahoo_process_filetransferinfo(yid, pkt); + break; + case YAHOO_SERVICE_Y7_FILETRANSFERACCEPT: + yahoo_process_filetransferaccept(yid, pkt); + break; + case YAHOO_SERVICE_ADDBUDDY: + yahoo_process_buddyadd(yid, pkt); + break; + case YAHOO_SERVICE_REMBUDDY: + yahoo_process_buddydel(yid, pkt); + break; + case YAHOO_SERVICE_IGNORECONTACT: + yahoo_process_ignore(yid, pkt); + break; + case YAHOO_SERVICE_VOICECHAT: + yahoo_process_voicechat(yid, pkt); + break; + case YAHOO_SERVICE_WEBCAM: + yahoo_process_webcam_key(yid, pkt); + break; + case YAHOO_SERVICE_PING: + yahoo_process_ping(yid, pkt); + break; + case YAHOO_SERVICE_Y7_CHANGE_GROUP: + yahoo_process_buddy_change_group(yid, pkt); + break; + case YAHOO_SERVICE_IDLE: + case YAHOO_SERVICE_MAILSTAT: + case YAHOO_SERVICE_CHATINVITE: + case YAHOO_SERVICE_CALENDAR: + case YAHOO_SERVICE_NEWPERSONALMAIL: + case YAHOO_SERVICE_ADDIDENT: + case YAHOO_SERVICE_ADDIGNORE: + case YAHOO_SERVICE_GOTGROUPRENAME: + case YAHOO_SERVICE_GROUPRENAME: + case YAHOO_SERVICE_PASSTHROUGH2: + case YAHOO_SERVICE_CHATLOGON: + case YAHOO_SERVICE_CHATLOGOFF: + case YAHOO_SERVICE_CHATMSG: + case YAHOO_SERVICE_REJECTCONTACT: + case YAHOO_SERVICE_PEERTOPEER: + WARNING(("unhandled service 0x%02x", pkt->service)); + yahoo_dump_unhandled(pkt); + break; + case YAHOO_SERVICE_PICTURE: + yahoo_process_picture(yid, pkt); + break; + case YAHOO_SERVICE_PICTURE_CHECKSUM: + yahoo_process_picture_checksum(yid, pkt); + break; + case YAHOO_SERVICE_PICTURE_UPLOAD: + yahoo_process_picture_upload(yid, pkt); + break; + case YAHOO_SERVICE_Y8_LIST: /* Buddy List */ + yahoo_process_buddy_list(yid, pkt); + break; + default: + WARNING(("unknown service 0x%02x", pkt->service)); + yahoo_dump_unhandled(pkt); + break; + } +} + +static struct yahoo_packet *yahoo_getdata(struct yahoo_input_data *yid) +{ + struct yahoo_packet *pkt; + struct yahoo_data *yd = yid->yd; + int pos = 0; + int pktlen; + + if (!yd) + return NULL; + + DEBUG_MSG(("rxlen is %d", yid->rxlen)); + if (yid->rxlen < YAHOO_PACKET_HDRLEN) { + DEBUG_MSG(("len < YAHOO_PACKET_HDRLEN")); + return NULL; + } + + pos += 4; /* YMSG */ + pos += 2; + pos += 2; + + pktlen = yahoo_get16(yid->rxqueue + pos); + pos += 2; + DEBUG_MSG(("%d bytes to read, rxlen is %d", pktlen, yid->rxlen)); + + if (yid->rxlen < (YAHOO_PACKET_HDRLEN + pktlen)) { + DEBUG_MSG(("len < YAHOO_PACKET_HDRLEN + pktlen")); + return NULL; + } + + LOG(("reading packet")); + yahoo_packet_dump(yid->rxqueue, YAHOO_PACKET_HDRLEN + pktlen); + + pkt = yahoo_packet_new(0, 0, 0); + + pkt->service = yahoo_get16(yid->rxqueue + pos); + pos += 2; + pkt->status = yahoo_get32(yid->rxqueue + pos); + pos += 4; + DEBUG_MSG(("Yahoo Service: 0x%02x Status: %d", pkt->service, + pkt->status)); + pkt->id = yahoo_get32(yid->rxqueue + pos); + pos += 4; + + yd->session_id = pkt->id; + + yahoo_packet_read(pkt, yid->rxqueue + pos, pktlen); + + yid->rxlen -= YAHOO_PACKET_HDRLEN + pktlen; + DEBUG_MSG(("rxlen == %d, rxqueue == %p", yid->rxlen, yid->rxqueue)); + if (yid->rxlen > 0) { + unsigned char *tmp = y_memdup(yid->rxqueue + YAHOO_PACKET_HDRLEN + + pktlen, yid->rxlen); + FREE(yid->rxqueue); + yid->rxqueue = tmp; + DEBUG_MSG(("new rxlen == %d, rxqueue == %p", yid->rxlen, + yid->rxqueue)); + } else { + DEBUG_MSG(("freed rxqueue == %p", yid->rxqueue)); + FREE(yid->rxqueue); + } + + return pkt; +} + +static struct yab *yahoo_yab_read(unsigned char *d, int len) +{ + char *st, *en; + char *data = (char *)d; + struct yab *yab = NULL; + + data[len] = '\0'; + + DEBUG_MSG(("Got yab: %s", data)); + st = en = strstr(data, "e0=\""); + if (st) { + yab = y_new0(struct yab, 1); + + st += strlen("e0=\""); + en = strchr(st, '"'); + *en++ = '\0'; + yab->email = yahoo_xmldecode(st); + } + + if (!en) + return NULL; + + st = strstr(en, "id=\""); + if (st) { + st += strlen("id=\""); + en = strchr(st, '"'); + *en++ = '\0'; + yab->yid = atoi(yahoo_xmldecode(st)); + } + + st = strstr(en, "fn=\""); + if (st) { + st += strlen("fn=\""); + en = strchr(st, '"'); + *en++ = '\0'; + yab->fname = yahoo_xmldecode(st); + } + + st = strstr(en, "ln=\""); + if (st) { + st += strlen("ln=\""); + en = strchr(st, '"'); + *en++ = '\0'; + yab->lname = yahoo_xmldecode(st); + } + + st = strstr(en, "nn=\""); + if (st) { + st += strlen("nn=\""); + en = strchr(st, '"'); + *en++ = '\0'; + yab->nname = yahoo_xmldecode(st); + } + + st = strstr(en, "yi=\""); + if (st) { + st += strlen("yi=\""); + en = strchr(st, '"'); + *en++ = '\0'; + yab->id = yahoo_xmldecode(st); + } + + st = strstr(en, "hphone=\""); + if (st) { + st += strlen("hphone=\""); + en = strchr(st, '"'); + *en++ = '\0'; + yab->hphone = yahoo_xmldecode(st); + } + + st = strstr(en, "wphone=\""); + if (st) { + st += strlen("wphone=\""); + en = strchr(st, '"'); + *en++ = '\0'; + yab->wphone = yahoo_xmldecode(st); + } + + st = strstr(en, "mphone=\""); + if (st) { + st += strlen("mphone=\""); + en = strchr(st, '"'); + *en++ = '\0'; + yab->mphone = yahoo_xmldecode(st); + } + + st = strstr(en, "dbid=\""); + if (st) { + st += strlen("dbid=\""); + en = strchr(st, '"'); + *en++ = '\0'; + yab->dbid = atoi(st); + } + + return yab; +} + +static struct yab *yahoo_getyab(struct yahoo_input_data *yid) +{ + struct yab *yab = NULL; + int pos = 0, end = 0; + struct yahoo_data *yd = yid->yd; + + if (!yd) + return NULL; + + do { + DEBUG_MSG(("rxlen is %d", yid->rxlen)); + + if (yid->rxlen <= strlen("<ct")) + return NULL; + + /* start with <ct */ + while (pos < yid->rxlen - strlen("<ct") + 1 + && memcmp(yid->rxqueue + pos, "<ct", strlen("<ct"))) + pos++; + + if (pos >= yid->rxlen - 1) + return NULL; + + end = pos + 2; + /* end with > */ + while (end < yid->rxlen - strlen(">") + && memcmp(yid->rxqueue + end, ">", strlen(">"))) + end++; + + if (end >= yid->rxlen - 1) + return NULL; + + yab = yahoo_yab_read(yid->rxqueue + pos, end + 2 - pos); + + yid->rxlen -= end + 1; + DEBUG_MSG(("rxlen == %d, rxqueue == %p", yid->rxlen, + yid->rxqueue)); + if (yid->rxlen > 0) { + unsigned char *tmp = + y_memdup(yid->rxqueue + end + 1, yid->rxlen); + FREE(yid->rxqueue); + yid->rxqueue = tmp; + DEBUG_MSG(("new rxlen == %d, rxqueue == %p", yid->rxlen, + yid->rxqueue)); + } else { + DEBUG_MSG(("freed rxqueue == %p", yid->rxqueue)); + FREE(yid->rxqueue); + } + + } while (!yab && end < yid->rxlen - 1); + + return yab; +} + +static char *yahoo_getwebcam_master(struct yahoo_input_data *yid) +{ + unsigned int pos = 0; + unsigned int len = 0; + unsigned int status = 0; + char *server = NULL; + struct yahoo_data *yd = yid->yd; + + if (!yid || !yd) + return NULL; + + DEBUG_MSG(("rxlen is %d", yid->rxlen)); + + len = yid->rxqueue[pos++]; + if (yid->rxlen < len) + return NULL; + + /* extract status (0 = ok, 6 = webcam not online) */ + status = yid->rxqueue[pos++]; + + if (status == 0) { + pos += 2; /* skip next 2 bytes */ + server = y_memdup(yid->rxqueue + pos, 16); + pos += 16; + } else if (status == 6) { + YAHOO_CALLBACK(ext_yahoo_webcam_closed) + (yd->client_id, yid->wcm->user, 4); + } + + /* skip rest of the data */ + + yid->rxlen -= len; + DEBUG_MSG(("rxlen == %d, rxqueue == %p", yid->rxlen, yid->rxqueue)); + if (yid->rxlen > 0) { + unsigned char *tmp = y_memdup(yid->rxqueue + pos, yid->rxlen); + FREE(yid->rxqueue); + yid->rxqueue = tmp; + DEBUG_MSG(("new rxlen == %d, rxqueue == %p", yid->rxlen, + yid->rxqueue)); + } else { + DEBUG_MSG(("freed rxqueue == %p", yid->rxqueue)); + FREE(yid->rxqueue); + } + + return server; +} + +static int yahoo_get_webcam_data(struct yahoo_input_data *yid) +{ + unsigned char reason = 0; + unsigned int pos = 0; + unsigned int begin = 0; + unsigned int end = 0; + unsigned int closed = 0; + unsigned char header_len = 0; + char *who; + int connect = 0; + struct yahoo_data *yd = yid->yd; + + if (!yd) + return -1; + + if (!yid->wcm || !yid->wcd || !yid->rxlen) + return -1; + + DEBUG_MSG(("rxlen is %d", yid->rxlen)); + + /* if we are not reading part of image then read header */ + if (!yid->wcd->to_read) { + header_len = yid->rxqueue[pos++]; + yid->wcd->packet_type = 0; + + if (yid->rxlen < header_len) + return 0; + + if (header_len >= 8) { + reason = yid->rxqueue[pos++]; + /* next 2 bytes should always be 05 00 */ + pos += 2; + yid->wcd->data_size = yahoo_get32(yid->rxqueue + pos); + pos += 4; + yid->wcd->to_read = yid->wcd->data_size; + } + if (header_len >= 13) { + yid->wcd->packet_type = yid->rxqueue[pos++]; + yid->wcd->timestamp = yahoo_get32(yid->rxqueue + pos); + pos += 4; + } + + /* skip rest of header */ + pos = header_len; + } + + begin = pos; + pos += yid->wcd->to_read; + if (pos > yid->rxlen) + pos = yid->rxlen; + + /* if it is not an image then make sure we have the whole packet */ + if (yid->wcd->packet_type != 0x02) { + if ((pos - begin) != yid->wcd->data_size) { + yid->wcd->to_read = 0; + return 0; + } else { + yahoo_packet_dump(yid->rxqueue + begin, pos - begin); + } + } + + DEBUG_MSG(("packet type %.2X, data length %d", yid->wcd->packet_type, + yid->wcd->data_size)); + + /* find out what kind of packet we got */ + switch (yid->wcd->packet_type) { + case 0x00: + /* user requests to view webcam (uploading) */ + if (yid->wcd->data_size && + yid->wcm->direction == YAHOO_WEBCAM_UPLOAD) { + end = begin; + while (end <= yid->rxlen && yid->rxqueue[end++] != 13) ; + if (end > begin) { + who = y_memdup(yid->rxqueue + begin, + end - begin); + who[end - begin - 1] = 0; + YAHOO_CALLBACK(ext_yahoo_webcam_viewer) (yd-> + client_id, who + 2, 2); + FREE(who); + } + } + + if (yid->wcm->direction == YAHOO_WEBCAM_DOWNLOAD) { + /* timestamp/status field */ + /* 0 = declined viewing permission */ + /* 1 = accepted viewing permission */ + if (yid->wcd->timestamp == 0) { + YAHOO_CALLBACK(ext_yahoo_webcam_closed) (yd-> + client_id, yid->wcm->user, 3); + } + } + break; + case 0x01: /* status packets?? */ + /* timestamp contains status info */ + /* 00 00 00 01 = we have data?? */ + break; + case 0x02: /* image data */ + YAHOO_CALLBACK(ext_yahoo_got_webcam_image) (yd->client_id, + yid->wcm->user, yid->rxqueue + begin, + yid->wcd->data_size, pos - begin, yid->wcd->timestamp); + break; + case 0x05: /* response packets when uploading */ + if (!yid->wcd->data_size) { + YAHOO_CALLBACK(ext_yahoo_webcam_data_request) (yd-> + client_id, yid->wcd->timestamp); + } + break; + case 0x07: /* connection is closing */ + switch (reason) { + case 0x01: /* user closed connection */ + closed = 1; + break; + case 0x0F: /* user cancelled permission */ + closed = 2; + break; + } + YAHOO_CALLBACK(ext_yahoo_webcam_closed) (yd->client_id, + yid->wcm->user, closed); + break; + case 0x0C: /* user connected */ + case 0x0D: /* user disconnected */ + if (yid->wcd->data_size) { + who = y_memdup(yid->rxqueue + begin, pos - begin + 1); + who[pos - begin] = 0; + if (yid->wcd->packet_type == 0x0C) + connect = 1; + else + connect = 0; + YAHOO_CALLBACK(ext_yahoo_webcam_viewer) (yd->client_id, + who, connect); + FREE(who); + } + break; + case 0x13: /* user data */ + /* i=user_ip (ip of the user we are viewing) */ + /* j=user_ext_ip (external ip of the user we */ + /* are viewing) */ + break; + case 0x17: /* ?? */ + break; + } + yid->wcd->to_read -= pos - begin; + + yid->rxlen -= pos; + DEBUG_MSG(("rxlen == %d, rxqueue == %p", yid->rxlen, yid->rxqueue)); + if (yid->rxlen > 0) { + unsigned char *tmp = y_memdup(yid->rxqueue + pos, yid->rxlen); + FREE(yid->rxqueue); + yid->rxqueue = tmp; + DEBUG_MSG(("new rxlen == %d, rxqueue == %p", yid->rxlen, + yid->rxqueue)); + } else { + DEBUG_MSG(("freed rxqueue == %p", yid->rxqueue)); + FREE(yid->rxqueue); + } + + /* If we read a complete packet return success */ + if (!yid->wcd->to_read) + return 1; + + return 0; +} + +int yahoo_write_ready(int id, void *fd, void *data) +{ + struct yahoo_input_data *yid = data; + int len; + struct data_queue *tx; + + LOG(("write callback: id=%d fd=%p data=%p", id, fd, data)); + if (!yid || !yid->txqueues) + return -2; + + tx = yid->txqueues->data; + LOG(("writing %d bytes", tx->len)); + len = yahoo_send_data(fd, tx->queue, MIN(1024, tx->len)); + + if (len == -1 && errno == EAGAIN) + return 1; + + if (len <= 0) { + int e = errno; + DEBUG_MSG(("len == %d (<= 0)", len)); + while (yid->txqueues) { + YList *l = yid->txqueues; + tx = l->data; + free(tx->queue); + free(tx); + yid->txqueues = + y_list_remove_link(yid->txqueues, + yid->txqueues); + y_list_free_1(l); + } + LOG(("yahoo_write_ready(%d, %p) len < 0", id, fd)); + YAHOO_CALLBACK(ext_yahoo_remove_handler) (id, yid->write_tag); + yid->write_tag = 0; + errno = e; + return 0; + } + + + tx->len -= len; + if (tx->len > 0) { + unsigned char *tmp = y_memdup(tx->queue + len, tx->len); + FREE(tx->queue); + tx->queue = tmp; + } else { + YList *l = yid->txqueues; + free(tx->queue); + free(tx); + yid->txqueues = + y_list_remove_link(yid->txqueues, yid->txqueues); + y_list_free_1(l); + /* + if(!yid->txqueues) + LOG(("yahoo_write_ready(%d, %d) !yxqueues", id, fd)); + */ + if (!yid->txqueues) { + LOG(("yahoo_write_ready(%d, %p) !txqueues", id, fd)); + YAHOO_CALLBACK(ext_yahoo_remove_handler) (id, + yid->write_tag); + yid->write_tag = 0; + } + } + + return 1; +} + +static void yahoo_process_pager_connection(struct yahoo_input_data *yid, + int over) +{ + struct yahoo_packet *pkt; + struct yahoo_data *yd = yid->yd; + int id = yd->client_id; + + if (over) + return; + + while (find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER) + && (pkt = yahoo_getdata(yid)) != NULL) { + + yahoo_packet_process(yid, pkt); + + yahoo_packet_free(pkt); + } +} + +static void yahoo_process_chatcat_connection(struct yahoo_input_data *yid, + int over) +{ + if (over) + return; + + if (strstr((char *)yid->rxqueue + (yid->rxlen - 20), "</content>")) { + YAHOO_CALLBACK(ext_yahoo_chat_cat_xml) (yid->yd->client_id, + (char *)yid->rxqueue); + } +} + +static void yahoo_process_yab_connection(struct yahoo_input_data *yid, int over) +{ + struct yahoo_data *yd = yid->yd; + struct yab *yab; + YList *buds; + int changed = 0; + int id = yd->client_id; + int yab_used = 0; + + LOG(("Got data for YAB")); + + if (over) + return; + + while (find_input_by_id_and_type(id, YAHOO_CONNECTION_YAB) + && (yab = yahoo_getyab(yid)) != NULL) { + if (!yab->id) + continue; + + changed = 1; + yab_used = 0; + for (buds = yd->buddies; buds; buds = buds->next) { + struct yahoo_buddy *bud = buds->data; + if (!strcmp(bud->id, yab->id)) { + yab_used = 1; + bud->yab_entry = yab; + if (yab->nname) { + bud->real_name = strdup(yab->nname); + } else if (yab->fname && yab->lname) { + bud->real_name = y_new0(char, + strlen(yab->fname) + + strlen(yab->lname) + 2); + sprintf(bud->real_name, "%s %s", + yab->fname, yab->lname); + } else if (yab->fname) { + bud->real_name = strdup(yab->fname); + } + break; /* for */ + } + } + + if (!yab_used) { + FREE(yab->fname); + FREE(yab->lname); + FREE(yab->nname); + FREE(yab->id); + FREE(yab->email); + FREE(yab->hphone); + FREE(yab->wphone); + FREE(yab->mphone); + FREE(yab); + } + + } + + if (changed) + YAHOO_CALLBACK(ext_yahoo_got_buddies) (yd->client_id, + yd->buddies); +} + +static void yahoo_process_search_connection(struct yahoo_input_data *yid, + int over) +{ + struct yahoo_found_contact *yct = NULL; + char *p = (char *)yid->rxqueue, *np, *cp; + int k, n; + int start = 0, found = 0, total = 0; + YList *contacts = NULL; + struct yahoo_input_data *pyid = + find_input_by_id_and_type(yid->yd->client_id, + YAHOO_CONNECTION_PAGER); + + if (!over || !pyid) + return; + + if (p && (p = strstr(p, "\r\n\r\n"))) { + p += 4; + + for (k = 0; (p = strchr(p, 4)) && (k < 4); k++) { + p++; + n = atoi(p); + switch (k) { + case 0: + found = pyid->ys->lsearch_nfound = n; + break; + case 2: + start = pyid->ys->lsearch_nstart = n; + break; + case 3: + total = pyid->ys->lsearch_ntotal = n; + break; + } + } + + if (p) + p++; + + k = 0; + while (p && *p) { + cp = p; + np = strchr(p, 4); + + if (!np) + break; + *np = 0; + p = np + 1; + + switch (k++) { + case 1: + if (strlen(cp) > 2 + && y_list_length(contacts) < total) { + yct = y_new0(struct yahoo_found_contact, + 1); + contacts = y_list_append(contacts, yct); + yct->id = cp + 2; + } else { + *p = 0; + } + break; + case 2: + yct->online = !strcmp(cp, "2") ? 1 : 0; + break; + case 3: + yct->gender = cp; + break; + case 4: + yct->age = atoi(cp); + break; + case 5: + /* not worth the context switch for strcmp */ + if (cp[0] != '\005' || cp[1] != '\000') + yct->location = cp; + k = 0; + break; + } + } + } + + YAHOO_CALLBACK(ext_yahoo_got_search_result) (yid->yd->client_id, found, + start, total, contacts); + + while (contacts) { + YList *node = contacts; + contacts = y_list_remove_link(contacts, node); + free(node->data); + y_list_free_1(node); + } +} + +static void _yahoo_webcam_connected(void *fd, int error, void *d) +{ + struct yahoo_input_data *yid = d; + struct yahoo_webcam *wcm = yid->wcm; + struct yahoo_data *yd = yid->yd; + char conn_type[100]; + char *data = NULL; + char *packet = NULL; + unsigned char magic_nr[] = { 1, 0, 0, 0, 1 }; + unsigned header_len = 0; + unsigned int len = 0; + unsigned int pos = 0; + + if (error || !fd) { + FREE(yid); + return; + } + + yid->fd = fd; + inputs = y_list_prepend(inputs, yid); + + LOG(("Connected")); + /* send initial packet */ + switch (wcm->direction) { + case YAHOO_WEBCAM_DOWNLOAD: + data = strdup("<REQIMG>"); + break; + case YAHOO_WEBCAM_UPLOAD: + data = strdup("<SNDIMG>"); + break; + default: + return; + } + yahoo_add_to_send_queue(yid, data, strlen(data)); + FREE(data); + + /* send data */ + switch (wcm->direction) { + case YAHOO_WEBCAM_DOWNLOAD: + header_len = 8; + data = strdup("a=2\r\nc=us\r\ne=21\r\nu="); + data = y_string_append(data, yd->user); + data = y_string_append(data, "\r\nt="); + data = y_string_append(data, wcm->key); + data = y_string_append(data, "\r\ni="); + data = y_string_append(data, wcm->my_ip); + data = y_string_append(data, "\r\ng="); + data = y_string_append(data, wcm->user); + data = y_string_append(data, "\r\no=w-2-5-1\r\np="); + snprintf(conn_type, sizeof(conn_type), "%d", wcm->conn_type); + data = y_string_append(data, conn_type); + data = y_string_append(data, "\r\n"); + break; + case YAHOO_WEBCAM_UPLOAD: + header_len = 13; + data = strdup("a=2\r\nc=us\r\nu="); + data = y_string_append(data, yd->user); + data = y_string_append(data, "\r\nt="); + data = y_string_append(data, wcm->key); + data = y_string_append(data, "\r\ni="); + data = y_string_append(data, wcm->my_ip); + data = y_string_append(data, "\r\no=w-2-5-1\r\np="); + snprintf(conn_type, sizeof(conn_type), "%d", wcm->conn_type); + data = y_string_append(data, conn_type); + data = y_string_append(data, "\r\nb="); + data = y_string_append(data, wcm->description); + data = y_string_append(data, "\r\n"); + break; + } + + len = strlen(data); + packet = y_new0(char, header_len + len); + packet[pos++] = header_len; + packet[pos++] = 0; + switch (wcm->direction) { + case YAHOO_WEBCAM_DOWNLOAD: + packet[pos++] = 1; + packet[pos++] = 0; + break; + case YAHOO_WEBCAM_UPLOAD: + packet[pos++] = 5; + packet[pos++] = 0; + break; + } + + pos += yahoo_put32(packet + pos, len); + if (wcm->direction == YAHOO_WEBCAM_UPLOAD) { + memcpy(packet + pos, magic_nr, sizeof(magic_nr)); + pos += sizeof(magic_nr); + } + memcpy(packet + pos, data, len); + yahoo_add_to_send_queue(yid, packet, header_len + len); + FREE(packet); + FREE(data); + + yid->read_tag = + YAHOO_CALLBACK(ext_yahoo_add_handler) (yid->yd->client_id, + yid->fd, YAHOO_INPUT_READ, yid); +} + +static void yahoo_webcam_connect(struct yahoo_input_data *y) +{ + struct yahoo_webcam *wcm = y->wcm; + struct yahoo_input_data *yid; + struct yahoo_server_settings *yss; + + if (!wcm || !wcm->server || !wcm->key) + return; + + yid = y_new0(struct yahoo_input_data, 1); + yid->type = YAHOO_CONNECTION_WEBCAM; + yid->yd = y->yd; + + /* copy webcam data to new connection */ + yid->wcm = y->wcm; + y->wcm = NULL; + + yss = y->yd->server_settings; + + yid->wcd = y_new0(struct yahoo_webcam_data, 1); + + LOG(("Connecting to: %s:%d", wcm->server, wcm->port)); + YAHOO_CALLBACK(ext_yahoo_connect_async) (y->yd->client_id, wcm->server, + wcm->port, _yahoo_webcam_connected, yid, 0); + +} + +static void yahoo_process_webcam_master_connection(struct yahoo_input_data *yid, + int over) +{ + char *server; + struct yahoo_server_settings *yss; + + if (over) + return; + + server = yahoo_getwebcam_master(yid); + + if (server) { + yss = yid->yd->server_settings; + yid->wcm->server = strdup(server); + yid->wcm->port = yss->webcam_port; + yid->wcm->conn_type = yss->conn_type; + yid->wcm->my_ip = strdup(yss->local_host); + if (yid->wcm->direction == YAHOO_WEBCAM_UPLOAD) + yid->wcm->description = strdup(yss->webcam_description); + yahoo_webcam_connect(yid); + FREE(server); + } +} + +static void yahoo_process_webcam_connection(struct yahoo_input_data *yid, + int over) +{ + int id = yid->yd->client_id; + void *fd = yid->fd; + + if (over) + return; + + /* as long as we still have packets available keep processing them */ + while (find_input_by_id_and_fd(id, fd) + && yahoo_get_webcam_data(yid) == 1) ; +} + +static void (*yahoo_process_connection[]) (struct yahoo_input_data *, + int over) = { +yahoo_process_pager_connection, yahoo_process_ft_connection, + yahoo_process_yab_connection, + yahoo_process_webcam_master_connection, + yahoo_process_webcam_connection, + yahoo_process_chatcat_connection, + yahoo_process_search_connection}; + +int yahoo_read_ready(int id, void *fd, void *data) +{ + struct yahoo_input_data *yid = data; + char buf[1024]; + int len; + + LOG(("read callback: id=%d fd=%p data=%p", id, fd, data)); + if (!yid) + return -2; + + do { + len = YAHOO_CALLBACK(ext_yahoo_read) (fd, buf, sizeof(buf)); + } while (len == -1 && errno == EINTR); + + if (len == -1 && (errno == EAGAIN || errno == EINTR)) /* we'll try again later */ + return 1; + + if (len <= 0) { + int e = errno; + DEBUG_MSG(("len == %d (<= 0)", len)); + + if (yid->type == YAHOO_CONNECTION_PAGER) { + YAHOO_CALLBACK(ext_yahoo_login_response) (yid->yd-> + client_id, YAHOO_LOGIN_SOCK, NULL); + } + + yahoo_process_connection[yid->type] (yid, 1); + yahoo_input_close(yid); + + /* no need to return an error, because we've already fixed it */ + if (len == 0) + return 1; + + errno = e; + LOG(("read error: %s", strerror(errno))); + return -1; + } + + yid->rxqueue = + y_renew(unsigned char, yid->rxqueue, len + yid->rxlen + 1); + memcpy(yid->rxqueue + yid->rxlen, buf, len); + yid->rxlen += len; + yid->rxqueue[yid->rxlen] = 0; + + yahoo_process_connection[yid->type] (yid, 0); + + return len; +} + +int yahoo_init_with_attributes(const char *username, const char *password, ...) +{ + va_list ap; + struct yahoo_data *yd; + + yd = y_new0(struct yahoo_data, 1); + + if (!yd) + return 0; + + yd->user = strdup(username); + yd->password = strdup(password); + + yd->initial_status = -1; + yd->current_status = -1; + + yd->client_id = ++last_id; + + add_to_list(yd); + + va_start(ap, password); + yd->server_settings = _yahoo_assign_server_settings(ap); + va_end(ap); + + return yd->client_id; +} + +int yahoo_init(const char *username, const char *password) +{ + return yahoo_init_with_attributes(username, password, NULL); +} + +static void yahoo_connected(void *fd, int error, void *data) +{ + struct connect_callback_data *ccd = data; + struct yahoo_data *yd = ccd->yd; + struct yahoo_packet *pkt; + struct yahoo_input_data *yid; + struct yahoo_server_settings *yss = yd->server_settings; + + if (error) { + int tag; + if (fallback_ports[ccd->i]) { + char *host = yss->pager_host; + + if (!host) + host = yss->pager_host_list[ccd->server_i]; + + yss->pager_port = fallback_ports[ccd->i++]; + tag = YAHOO_CALLBACK(ext_yahoo_connect_async) (yd-> + client_id, host, yss->pager_port, + yahoo_connected, ccd, 0); + + if (tag > 0) + ccd->tag = tag; + } else if (yss->pager_host_list + && yss->pager_host_list[ccd->server_i]) { + + /* Get back to the default port */ + yss->pager_port = pager_port; + ccd->server_i++; + LOG(("Fallback: Connecting to %s:%d", yss->pager_host_list[ccd->server_i], yss->pager_port)); + + ccd->i = 0; + tag = YAHOO_CALLBACK(ext_yahoo_connect_async) (yd->client_id, + yss->pager_host_list[ccd->server_i], yss->pager_port, + yahoo_connected, ccd, 0); + } else { + FREE(ccd); + YAHOO_CALLBACK(ext_yahoo_login_response) (yd->client_id, + YAHOO_LOGIN_SOCK, NULL); + } + return; + } + + FREE(ccd); + + /* fd == NULL && error == 0 means connect was cancelled */ + if (!fd) + return; + + pkt = yahoo_packet_new(YAHOO_SERVICE_AUTH, YPACKET_STATUS_DEFAULT, + yd->session_id); + NOTICE(("Sending initial packet")); + + yahoo_packet_hash(pkt, 1, yd->user); + + yid = find_input_by_id_and_type(yd->client_id, YAHOO_CONNECTION_PAGER); + yid->fd = fd; + + yahoo_send_packet(yid, pkt, 0); + + yahoo_packet_free(pkt); + + yid->read_tag = + YAHOO_CALLBACK(ext_yahoo_add_handler) (yid->yd->client_id, + yid->fd, YAHOO_INPUT_READ, yid); +} + +void *yahoo_get_fd(int id) +{ + struct yahoo_input_data *yid = + find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); + if (!yid) + return 0; + else + return yid->fd; +} + +void yahoo_send_buzz(int id, const char *from, const char *who) +{ + yahoo_send_im(id, from, who, "<ding>", 1, 0); +} + +void yahoo_send_im(int id, const char *from, const char *who, const char *what, + int utf8, int picture) +{ + struct yahoo_input_data *yid = + find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); + struct yahoo_packet *pkt = NULL; + struct yahoo_data *yd; + char pic_str[10]; + + if (!yid) + return; + + yd = yid->yd; + + pkt = yahoo_packet_new(YAHOO_SERVICE_MESSAGE, YAHOO_STATUS_OFFLINE, + yd->session_id); + + snprintf(pic_str, sizeof(pic_str), "%d", picture); + + if (from && strcmp(from, yd->user)) + yahoo_packet_hash(pkt, 0, yd->user); + yahoo_packet_hash(pkt, 1, from ? from : yd->user); + yahoo_packet_hash(pkt, 5, who); + yahoo_packet_hash(pkt, 14, what); + + if (utf8) + yahoo_packet_hash(pkt, 97, "1"); + + yahoo_packet_hash(pkt, 63, ";0"); /* imvironment name; or ;0 */ + yahoo_packet_hash(pkt, 64, "0"); + yahoo_packet_hash(pkt, 206, pic_str); + + yahoo_send_packet(yid, pkt, 0); + + yahoo_packet_free(pkt); +} + +void yahoo_send_typing(int id, const char *from, const char *who, int typ) +{ + struct yahoo_input_data *yid = + find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); + struct yahoo_data *yd; + struct yahoo_packet *pkt = NULL; + if (!yid) + return; + + yd = yid->yd; + pkt = yahoo_packet_new(YAHOO_SERVICE_NOTIFY, YPACKET_STATUS_NOTIFY, + yd->session_id); + + yahoo_packet_hash(pkt, 5, who); + yahoo_packet_hash(pkt, 1, from ? from : yd->user); + yahoo_packet_hash(pkt, 14, " "); + yahoo_packet_hash(pkt, 13, typ ? "1" : "0"); + yahoo_packet_hash(pkt, 49, "TYPING"); + + yahoo_send_packet(yid, pkt, 0); + + yahoo_packet_free(pkt); +} + +void yahoo_set_away(int id, enum yahoo_status state, const char *msg, int away) +{ + struct yahoo_input_data *yid = + find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); + struct yahoo_data *yd; + struct yahoo_packet *pkt = NULL; + int old_status; + char s[4]; + + if (!yid) + return; + + yd = yid->yd; + + old_status = yd->current_status; + yd->current_status = state; + + /* Thank you libpurple :) */ + if (yd->current_status == YAHOO_STATUS_INVISIBLE) { + pkt = yahoo_packet_new(YAHOO_SERVICE_Y6_VISIBLE_TOGGLE, + YAHOO_STATUS_AVAILABLE, 0); + yahoo_packet_hash(pkt, 13, "2"); + yahoo_send_packet(yid, pkt, 0); + yahoo_packet_free(pkt); + + return; + } + + pkt = yahoo_packet_new(YAHOO_SERVICE_Y6_STATUS_UPDATE, + yd->current_status, yd->session_id); + snprintf(s, sizeof(s), "%d", yd->current_status); + yahoo_packet_hash(pkt, 10, s); + yahoo_packet_hash(pkt, 19, msg && state == YAHOO_STATUS_CUSTOM ? msg : ""); + yahoo_packet_hash(pkt, 47, (away == 2)? "2": (away) ?"1":"0"); + yahoo_send_packet(yid, pkt, 0); + yahoo_packet_free(pkt); + + if (old_status == YAHOO_STATUS_INVISIBLE) { + pkt = yahoo_packet_new(YAHOO_SERVICE_Y6_VISIBLE_TOGGLE, + YAHOO_STATUS_AVAILABLE, 0); + yahoo_packet_hash(pkt, 13, "1"); + yahoo_send_packet(yid, pkt, 0); + yahoo_packet_free(pkt); + } +} + +void yahoo_logoff(int id) +{ + struct yahoo_input_data *yid = + find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); + struct yahoo_data *yd; + struct yahoo_packet *pkt = NULL; + + if (!yid) + return; + yd = yid->yd; + + LOG(("yahoo_logoff: current status: %d", yd->current_status)); + + if (yd->current_status != -1 && 0) { + /* Meh. Don't send this. The event handlers are not going to + get to do this so it'll just leak memory. And the TCP + connection reset will hopefully be clear enough. */ + pkt = yahoo_packet_new(YAHOO_SERVICE_LOGOFF, + YPACKET_STATUS_DEFAULT, yd->session_id); + yd->current_status = -1; + + if (pkt) { + yahoo_send_packet(yid, pkt, 0); + yahoo_packet_free(pkt); + } + } + +/* do { + yahoo_input_close(yid); + } while((yid = find_input_by_id(id)));*/ + +} + +void yahoo_get_list(int id) +{ + struct yahoo_input_data *yid = + find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); + struct yahoo_data *yd; + struct yahoo_packet *pkt = NULL; + + if (!yid) + return; + yd = yid->yd; + + pkt = yahoo_packet_new(YAHOO_SERVICE_LIST, YPACKET_STATUS_DEFAULT, + yd->session_id); + yahoo_packet_hash(pkt, 1, yd->user); + if (pkt) { + yahoo_send_packet(yid, pkt, 0); + yahoo_packet_free(pkt); + } +} + +static void _yahoo_http_connected(int id, void *fd, int error, void *data) +{ + struct yahoo_input_data *yid = data; + if (fd == NULL || error) { + inputs = y_list_remove(inputs, yid); + FREE(yid); + return; + } + + yid->fd = fd; + yid->read_tag = + YAHOO_CALLBACK(ext_yahoo_add_handler) (yid->yd->client_id, fd, + YAHOO_INPUT_READ, yid); +} + +/* FIXME Get address book from address.yahoo.com instead */ +void yahoo_get_yab(int id) +{ + struct yahoo_data *yd = find_conn_by_id(id); + struct yahoo_input_data *yid; + char url[1024]; + char buff[2048]; + + if (!yd) + return; + + yid = y_new0(struct yahoo_input_data, 1); + yid->yd = yd; + yid->type = YAHOO_CONNECTION_YAB; + + LOG(("Sending request for Address Book")); + + snprintf(url, 1024, + "http://address.yahoo.com/yab/us?v=XM&prog=ymsgr&.intl=us" + "&diffs=1&t=0&tags=short&rt=0&prog-ver=8.1.0.249&useutf8=1&legenc=codepage-1252"); + + snprintf(buff, sizeof(buff), "Y=%s; T=%s", yd->cookie_y, yd->cookie_t); + + inputs = y_list_prepend(inputs, yid); + + yahoo_http_get(yid->yd->client_id, url, buff, 0, 0, + _yahoo_http_connected, yid); +} + +struct yahoo_post_data { + struct yahoo_input_data *yid; + char *data; +}; + +static void _yahoo_http_post_connected(int id, void *fd, int error, void *data) +{ + struct yahoo_post_data *yad = data; + struct yahoo_input_data *yid = yad->yid; + char *buff = yad->data; + + if (!fd) { + inputs = y_list_remove(inputs, yid); + FREE(yid); + return; + } + + YAHOO_CALLBACK(ext_yahoo_write) (fd, buff, strlen(buff)); + + yid->fd = fd; + yid->read_tag = + YAHOO_CALLBACK(ext_yahoo_add_handler) (yid->yd->client_id, fd, + YAHOO_INPUT_READ, yid); + + FREE(buff); + FREE(yad); +} + +/* FIXME This is also likely affected */ +void yahoo_set_yab(int id, struct yab *yab) +{ + struct yahoo_post_data *yad = y_new0(struct yahoo_post_data, 1); + struct yahoo_data *yd = find_conn_by_id(id); + struct yahoo_input_data *yid; + char url[1024]; + char buff[1024]; + char post[1024]; + int size = 0; + + if (!yd) + return; + + yid = y_new0(struct yahoo_input_data, 1); + yid->type = YAHOO_CONNECTION_YAB; + yid->yd = yd; + + if(yab->yid) + size = snprintf(post, sizeof(post), "<?xml version=\"1.0\" encoding=\"utf-8\"?>" + "<ab k=\"%s\" cc=\"%d\">" + "<ct id=\"%d\" e=\"1\" yi=\"%s\" nn=\"%s\" />" + "</ab>", yd->user, 9, yab->yid, /* Don't know why */ + yab->id, yab->nname?yab->nname:""); + else + size = snprintf(post, sizeof(post), "<?xml version=\"1.0\" encoding=\"utf-8\"?>" + "<ab k=\"%s\" cc=\"%d\">" + "<ct a=\"1\" yi=\"%s\" nn=\"%s\" />" + "</ab>", yd->user, 1, /* Don't know why */ + yab->id, yab->nname?yab->nname:""); + + yad->yid = yid; + yad->data = strdup(post); + + strcpy(url, "http://address.yahoo.com/yab/us?v=XM&prog=ymsgr&.intl=us" + "&sync=1&tags=short&noclear=1&useutf8=1&legenc=codepage-1252"); + + snprintf(buff, sizeof(buff), "Y=%s; T=%s", yd->cookie_y, yd->cookie_t); + + inputs = y_list_prepend(inputs, yid); + + yahoo_http_post(yid->yd->client_id, url, buff, size, + _yahoo_http_post_connected, yad); +} + +void yahoo_set_identity_status(int id, const char *identity, int active) +{ + struct yahoo_input_data *yid = + find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); + struct yahoo_data *yd; + struct yahoo_packet *pkt = NULL; + + if (!yid) + return; + yd = yid->yd; + + pkt = yahoo_packet_new(active ? YAHOO_SERVICE_IDACT : + YAHOO_SERVICE_IDDEACT, YPACKET_STATUS_DEFAULT, yd->session_id); + yahoo_packet_hash(pkt, 3, identity); + if (pkt) { + yahoo_send_packet(yid, pkt, 0); + yahoo_packet_free(pkt); + } +} + +void yahoo_refresh(int id) +{ + struct yahoo_input_data *yid = + find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); + struct yahoo_data *yd; + struct yahoo_packet *pkt = NULL; + + if (!yid) + return; + yd = yid->yd; + + pkt = yahoo_packet_new(YAHOO_SERVICE_USERSTAT, YPACKET_STATUS_DEFAULT, + yd->session_id); + if (pkt) { + yahoo_send_packet(yid, pkt, 0); + yahoo_packet_free(pkt); + } +} + +void yahoo_keepalive(int id) +{ + struct yahoo_input_data *yid = + find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); + struct yahoo_data *yd; + struct yahoo_packet *pkt = NULL; + if (!yid) + return; + yd = yid->yd; + + pkt = yahoo_packet_new(YAHOO_SERVICE_PING, YPACKET_STATUS_DEFAULT, + yd->session_id); + yahoo_send_packet(yid, pkt, 0); + yahoo_packet_free(pkt); +} + +void yahoo_chat_keepalive(int id) +{ + struct yahoo_input_data *yid = + find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); + struct yahoo_data *yd; + struct yahoo_packet *pkt = NULL; + + if (!yid) + return; + + yd = yid->yd; + + pkt = yahoo_packet_new(YAHOO_SERVICE_CHATPING, YPACKET_STATUS_DEFAULT, + yd->session_id); + yahoo_send_packet(yid, pkt, 0); + yahoo_packet_free(pkt); +} + +void yahoo_add_buddy(int id, const char *who, const char *group, + const char *msg) +{ + struct yahoo_input_data *yid = + find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); + struct yahoo_data *yd; + struct yahoo_packet *pkt; + + if (!yid) + return; + yd = yid->yd; + + if (!yd->logged_in) + return; + + pkt = yahoo_packet_new(YAHOO_SERVICE_ADDBUDDY, YPACKET_STATUS_DEFAULT, + yd->session_id); + if (msg != NULL) /* add message/request "it's me add me" */ + yahoo_packet_hash(pkt, 14, msg); + else + yahoo_packet_hash(pkt, 14, ""); + yahoo_packet_hash(pkt, 65, group); + yahoo_packet_hash(pkt, 97, "1"); + yahoo_packet_hash(pkt, 1, yd->user); + yahoo_packet_hash(pkt, 302, "319"); + yahoo_packet_hash(pkt, 300, "319"); + yahoo_packet_hash(pkt, 7, who); + yahoo_packet_hash(pkt, 334, "0"); + yahoo_packet_hash(pkt, 301, "319"); + yahoo_packet_hash(pkt, 303, "319"); + yahoo_send_packet(yid, pkt, 0); + yahoo_packet_free(pkt); +} + +void yahoo_remove_buddy(int id, const char *who, const char *group) +{ + struct yahoo_input_data *yid = + find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); + struct yahoo_data *yd; + struct yahoo_packet *pkt = NULL; + + if (!yid) + return; + yd = yid->yd; + + pkt = yahoo_packet_new(YAHOO_SERVICE_REMBUDDY, YPACKET_STATUS_DEFAULT, + yd->session_id); + + yahoo_packet_hash(pkt, 1, yd->user); + yahoo_packet_hash(pkt, 7, who); + yahoo_packet_hash(pkt, 65, group); + yahoo_send_packet(yid, pkt, 0); + yahoo_packet_free(pkt); +} + +void yahoo_confirm_buddy(int id, const char *who, int reject, const char *msg) +{ + struct yahoo_input_data *yid = + find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); + struct yahoo_data *yd; + struct yahoo_packet *pkt; + + if (!yid) + return; + yd = yid->yd; + + if (!yd->logged_in) + return; + + pkt = yahoo_packet_new(YAHOO_SERVICE_Y7_AUTHORIZATION, + YPACKET_STATUS_DEFAULT, yd->session_id); + yahoo_packet_hash(pkt, 1, yd->user); + yahoo_packet_hash(pkt, 5, who); + if (reject) + yahoo_packet_hash(pkt, 13, "2"); + else { + yahoo_packet_hash(pkt, 241, "0"); + yahoo_packet_hash(pkt, 13, "1"); + } + + yahoo_packet_hash(pkt, 334, "0"); + + if (reject) { + yahoo_packet_hash(pkt, 14, msg ? msg : ""); + yahoo_packet_hash(pkt, 97, "1"); + } + + yahoo_send_packet(yid, pkt, 0); + yahoo_packet_free(pkt); +} + +void yahoo_ignore_buddy(int id, const char *who, int unignore) +{ + struct yahoo_input_data *yid = + find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); + struct yahoo_data *yd; + struct yahoo_packet *pkt; + + if (!yid) + return; + yd = yid->yd; + + if (!yd->logged_in) + return; + + pkt = yahoo_packet_new(YAHOO_SERVICE_IGNORECONTACT, + YPACKET_STATUS_DEFAULT, yd->session_id); + yahoo_packet_hash(pkt, 1, yd->user); + yahoo_packet_hash(pkt, 7, who); + yahoo_packet_hash(pkt, 13, unignore ? "2" : "1"); + yahoo_send_packet(yid, pkt, 0); + yahoo_packet_free(pkt); +} + +void yahoo_stealth_buddy(int id, const char *who, int unstealth) +{ + struct yahoo_input_data *yid = + find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); + struct yahoo_data *yd; + struct yahoo_packet *pkt; + + if (!yid) + return; + yd = yid->yd; + + if (!yd->logged_in) + return; + + pkt = yahoo_packet_new(YAHOO_SERVICE_STEALTH_PERM, + YPACKET_STATUS_DEFAULT, yd->session_id); + yahoo_packet_hash(pkt, 1, yd->user); + yahoo_packet_hash(pkt, 7, who); + yahoo_packet_hash(pkt, 31, unstealth ? "2" : "1"); + yahoo_packet_hash(pkt, 13, "2"); + yahoo_send_packet(yid, pkt, 0); + yahoo_packet_free(pkt); +} + +void yahoo_change_buddy_group(int id, const char *who, const char *old_group, + const char *new_group) +{ + struct yahoo_input_data *yid = + find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); + struct yahoo_data *yd; + struct yahoo_packet *pkt = NULL; + + if (!yid) + return; + yd = yid->yd; + + pkt = yahoo_packet_new(YAHOO_SERVICE_Y7_CHANGE_GROUP, + YPACKET_STATUS_DEFAULT, yd->session_id); + yahoo_packet_hash(pkt, 1, yd->user); + yahoo_packet_hash(pkt, 302, "240"); + yahoo_packet_hash(pkt, 300, "240"); + yahoo_packet_hash(pkt, 7, who); + yahoo_packet_hash(pkt, 224, old_group); + yahoo_packet_hash(pkt, 264, new_group); + yahoo_packet_hash(pkt, 301, "240"); + yahoo_packet_hash(pkt, 303, "240"); + + yahoo_send_packet(yid, pkt, 0); + yahoo_packet_free(pkt); +} + +void yahoo_group_rename(int id, const char *old_group, const char *new_group) +{ + struct yahoo_input_data *yid = + find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); + struct yahoo_data *yd; + struct yahoo_packet *pkt = NULL; + + if (!yid) + return; + yd = yid->yd; + + pkt = yahoo_packet_new(YAHOO_SERVICE_GROUPRENAME, + YPACKET_STATUS_DEFAULT, yd->session_id); + yahoo_packet_hash(pkt, 1, yd->user); + yahoo_packet_hash(pkt, 65, old_group); + yahoo_packet_hash(pkt, 67, new_group); + + yahoo_send_packet(yid, pkt, 0); + yahoo_packet_free(pkt); +} + +void yahoo_conference_addinvite(int id, const char *from, const char *who, + const char *room, const YList *members, const char *msg) +{ + struct yahoo_input_data *yid = + find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); + struct yahoo_data *yd; + struct yahoo_packet *pkt; + + if (!yid) + return; + yd = yid->yd; + + pkt = yahoo_packet_new(YAHOO_SERVICE_CONFADDINVITE, + YPACKET_STATUS_DEFAULT, yd->session_id); + + yahoo_packet_hash(pkt, 1, (from ? from : yd->user)); + yahoo_packet_hash(pkt, 51, who); + yahoo_packet_hash(pkt, 57, room); + yahoo_packet_hash(pkt, 58, msg); + yahoo_packet_hash(pkt, 13, "0"); + for (; members; members = members->next) { + yahoo_packet_hash(pkt, 52, (char *)members->data); + yahoo_packet_hash(pkt, 53, (char *)members->data); + } + /* 52, 53 -> other members? */ + + yahoo_send_packet(yid, pkt, 0); + + yahoo_packet_free(pkt); +} + +void yahoo_conference_invite(int id, const char *from, YList *who, + const char *room, const char *msg) +{ + struct yahoo_input_data *yid = + find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); + struct yahoo_data *yd; + struct yahoo_packet *pkt; + + if (!yid) + return; + yd = yid->yd; + + pkt = yahoo_packet_new(YAHOO_SERVICE_CONFINVITE, YPACKET_STATUS_DEFAULT, + yd->session_id); + + yahoo_packet_hash(pkt, 1, (from ? from : yd->user)); + yahoo_packet_hash(pkt, 50, yd->user); + for (; who; who = who->next) { + yahoo_packet_hash(pkt, 52, (char *)who->data); + } + yahoo_packet_hash(pkt, 57, room); + yahoo_packet_hash(pkt, 58, msg); + yahoo_packet_hash(pkt, 13, "0"); + + yahoo_send_packet(yid, pkt, 0); + + yahoo_packet_free(pkt); +} + +void yahoo_conference_logon(int id, const char *from, YList *who, + const char *room) +{ + struct yahoo_input_data *yid = + find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); + struct yahoo_data *yd; + struct yahoo_packet *pkt; + + if (!yid) + return; + yd = yid->yd; + + pkt = yahoo_packet_new(YAHOO_SERVICE_CONFLOGON, YPACKET_STATUS_DEFAULT, + yd->session_id); + + yahoo_packet_hash(pkt, 1, (from ? from : yd->user)); + yahoo_packet_hash(pkt, 3, (from ? from : yd->user)); + yahoo_packet_hash(pkt, 57, room); + for (; who; who = who->next) + yahoo_packet_hash(pkt, 3, (char *)who->data); + + yahoo_send_packet(yid, pkt, 0); + + yahoo_packet_free(pkt); +} + +void yahoo_conference_decline(int id, const char *from, YList *who, + const char *room, const char *msg) +{ + struct yahoo_input_data *yid = + find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); + struct yahoo_data *yd; + struct yahoo_packet *pkt; + + if (!yid) + return; + yd = yid->yd; + + pkt = yahoo_packet_new(YAHOO_SERVICE_CONFDECLINE, + YPACKET_STATUS_DEFAULT, yd->session_id); + + yahoo_packet_hash(pkt, 1, (from ? from : yd->user)); + yahoo_packet_hash(pkt, 3, (from ? from : yd->user)); + for (; who; who = who->next) + yahoo_packet_hash(pkt, 3, (char *)who->data); + yahoo_packet_hash(pkt, 57, room); + yahoo_packet_hash(pkt, 14, msg); + + yahoo_send_packet(yid, pkt, 0); + + yahoo_packet_free(pkt); +} + +void yahoo_conference_logoff(int id, const char *from, YList *who, + const char *room) +{ + struct yahoo_input_data *yid = + find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); + struct yahoo_data *yd; + struct yahoo_packet *pkt; + + if (!yid) + return; + yd = yid->yd; + + pkt = yahoo_packet_new(YAHOO_SERVICE_CONFLOGOFF, YPACKET_STATUS_DEFAULT, + yd->session_id); + + yahoo_packet_hash(pkt, 1, (from ? from : yd->user)); + yahoo_packet_hash(pkt, 3, (from ? from : yd->user)); + for (; who; who = who->next) + yahoo_packet_hash(pkt, 3, (char *)who->data); + + yahoo_packet_hash(pkt, 57, room); + + yahoo_send_packet(yid, pkt, 0); + + yahoo_packet_free(pkt); +} + +void yahoo_conference_message(int id, const char *from, YList *who, + const char *room, const char *msg, int utf8) +{ + struct yahoo_input_data *yid = + find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); + struct yahoo_data *yd; + struct yahoo_packet *pkt; + + if (!yid) + return; + yd = yid->yd; + + pkt = yahoo_packet_new(YAHOO_SERVICE_CONFMSG, YPACKET_STATUS_DEFAULT, + yd->session_id); + + yahoo_packet_hash(pkt, 1, (from ? from : yd->user)); + yahoo_packet_hash(pkt, 53, (from ? from : yd->user)); + for (; who; who = who->next) + yahoo_packet_hash(pkt, 53, (char *)who->data); + + yahoo_packet_hash(pkt, 57, room); + yahoo_packet_hash(pkt, 14, msg); + + if (utf8) + yahoo_packet_hash(pkt, 97, "1"); + + yahoo_send_packet(yid, pkt, 0); + + yahoo_packet_free(pkt); +} + +void yahoo_get_chatrooms(int id, int chatroomid) +{ + struct yahoo_data *yd = find_conn_by_id(id); + struct yahoo_input_data *yid; + char url[1024]; + char buff[1024]; + + if (!yd) + return; + + yid = y_new0(struct yahoo_input_data, 1); + yid->yd = yd; + yid->type = YAHOO_CONNECTION_CHATCAT; + + if (chatroomid == 0) { + snprintf(url, 1024, + "http://insider.msg.yahoo.com/ycontent/?chatcat=0"); + } else { + snprintf(url, 1024, + "http://insider.msg.yahoo.com/ycontent/?chatroom_%d=0", + chatroomid); + } + + snprintf(buff, sizeof(buff), "Y=%s; T=%s", yd->cookie_y, yd->cookie_t); + + inputs = y_list_prepend(inputs, yid); + + yahoo_http_get(yid->yd->client_id, url, buff, 0, 0, + _yahoo_http_connected, yid); +} + +void yahoo_chat_logon(int id, const char *from, const char *room, + const char *roomid) +{ + struct yahoo_input_data *yid = + find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); + struct yahoo_data *yd; + struct yahoo_packet *pkt; + + if (!yid) + return; + + yd = yid->yd; + + pkt = yahoo_packet_new(YAHOO_SERVICE_CHATONLINE, YPACKET_STATUS_DEFAULT, + yd->session_id); + + yahoo_packet_hash(pkt, 1, (from ? from : yd->user)); + yahoo_packet_hash(pkt, 109, yd->user); + yahoo_packet_hash(pkt, 6, "abcde"); + + yahoo_send_packet(yid, pkt, 0); + + yahoo_packet_free(pkt); + + pkt = yahoo_packet_new(YAHOO_SERVICE_CHATJOIN, YPACKET_STATUS_DEFAULT, + yd->session_id); + + yahoo_packet_hash(pkt, 1, (from ? from : yd->user)); + yahoo_packet_hash(pkt, 104, room); + yahoo_packet_hash(pkt, 129, roomid); + yahoo_packet_hash(pkt, 62, "2"); /* ??? */ + + yahoo_send_packet(yid, pkt, 0); + + yahoo_packet_free(pkt); +} + +void yahoo_chat_message(int id, const char *from, const char *room, + const char *msg, const int msgtype, const int utf8) +{ + struct yahoo_input_data *yid = + find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); + struct yahoo_data *yd; + struct yahoo_packet *pkt; + char buf[2]; + + if (!yid) + return; + + yd = yid->yd; + + pkt = yahoo_packet_new(YAHOO_SERVICE_COMMENT, YPACKET_STATUS_DEFAULT, + yd->session_id); + + yahoo_packet_hash(pkt, 1, (from ? from : yd->user)); + yahoo_packet_hash(pkt, 104, room); + yahoo_packet_hash(pkt, 117, msg); + + snprintf(buf, sizeof(buf), "%d", msgtype); + yahoo_packet_hash(pkt, 124, buf); + + if (utf8) + yahoo_packet_hash(pkt, 97, "1"); + + yahoo_send_packet(yid, pkt, 0); + + yahoo_packet_free(pkt); +} + +void yahoo_chat_logoff(int id, const char *from) +{ + struct yahoo_input_data *yid = + find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); + struct yahoo_data *yd; + struct yahoo_packet *pkt; + + if (!yid) + return; + + yd = yid->yd; + + pkt = yahoo_packet_new(YAHOO_SERVICE_CHATLOGOUT, YPACKET_STATUS_DEFAULT, + yd->session_id); + + yahoo_packet_hash(pkt, 1, (from ? from : yd->user)); + + yahoo_send_packet(yid, pkt, 0); + + yahoo_packet_free(pkt); +} + +void yahoo_buddyicon_request(int id, const char *who) +{ + struct yahoo_input_data *yid = + find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); + struct yahoo_data *yd; + struct yahoo_packet *pkt; + + if (!yid) + return; + + yd = yid->yd; + + pkt = yahoo_packet_new(YAHOO_SERVICE_PICTURE, YPACKET_STATUS_DEFAULT, + 0); + yahoo_packet_hash(pkt, 4, yd->user); + yahoo_packet_hash(pkt, 5, who); + yahoo_packet_hash(pkt, 13, "1"); + yahoo_send_packet(yid, pkt, 0); + + yahoo_packet_free(pkt); +} + +void yahoo_send_picture_info(int id, const char *who, const char *url, + int checksum) +{ + struct yahoo_input_data *yid = + find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); + struct yahoo_data *yd; + struct yahoo_packet *pkt; + char checksum_str[10]; + + if (!yid) + return; + + yd = yid->yd; + + snprintf(checksum_str, sizeof(checksum_str), "%d", checksum); + + pkt = yahoo_packet_new(YAHOO_SERVICE_PICTURE, YPACKET_STATUS_DEFAULT, + 0); + yahoo_packet_hash(pkt, 1, yd->user); + yahoo_packet_hash(pkt, 4, yd->user); + yahoo_packet_hash(pkt, 5, who); + yahoo_packet_hash(pkt, 13, "2"); + yahoo_packet_hash(pkt, 20, url); + yahoo_packet_hash(pkt, 192, checksum_str); + yahoo_send_packet(yid, pkt, 0); + + yahoo_packet_free(pkt); +} + +void yahoo_send_picture_update(int id, const char *who, int type) +{ + struct yahoo_input_data *yid = + find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); + struct yahoo_data *yd; + struct yahoo_packet *pkt; + char type_str[10]; + + if (!yid) + return; + + yd = yid->yd; + + snprintf(type_str, sizeof(type_str), "%d", type); + + pkt = yahoo_packet_new(YAHOO_SERVICE_PICTURE_UPDATE, + YPACKET_STATUS_DEFAULT, 0); + yahoo_packet_hash(pkt, 1, yd->user); + yahoo_packet_hash(pkt, 5, who); + yahoo_packet_hash(pkt, 206, type_str); + yahoo_send_packet(yid, pkt, 0); + + yahoo_packet_free(pkt); +} + +void yahoo_send_picture_checksum(int id, const char *who, int checksum) +{ + struct yahoo_input_data *yid = + find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); + struct yahoo_data *yd; + struct yahoo_packet *pkt; + char checksum_str[10]; + + if (!yid) + return; + + yd = yid->yd; + + snprintf(checksum_str, sizeof(checksum_str), "%d", checksum); + + pkt = yahoo_packet_new(YAHOO_SERVICE_PICTURE_CHECKSUM, + YPACKET_STATUS_DEFAULT, 0); + yahoo_packet_hash(pkt, 1, yd->user); + if (who != 0) + yahoo_packet_hash(pkt, 5, who); + yahoo_packet_hash(pkt, 192, checksum_str); + yahoo_packet_hash(pkt, 212, "1"); + yahoo_send_packet(yid, pkt, 0); + + yahoo_packet_free(pkt); +} + +void yahoo_webcam_close_feed(int id, const char *who) +{ + struct yahoo_input_data *yid = + find_input_by_id_and_webcam_user(id, who); + + if (yid) + yahoo_input_close(yid); +} + +void yahoo_webcam_get_feed(int id, const char *who) +{ + struct yahoo_input_data *yid = + find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); + struct yahoo_data *yd; + struct yahoo_packet *pkt; + + if (!yid) + return; + + /* + * add the user to the queue. this is a dirty hack, since + * the yahoo server doesn't tell us who's key it's returning, + * we have to just hope that it sends back keys in the same + * order that we request them. + * The queue is popped in yahoo_process_webcam_key + */ + webcam_queue = y_list_append(webcam_queue, who ? strdup(who) : NULL); + + yd = yid->yd; + + pkt = yahoo_packet_new(YAHOO_SERVICE_WEBCAM, YPACKET_STATUS_DEFAULT, + yd->session_id); + + yahoo_packet_hash(pkt, 1, yd->user); + if (who != NULL) + yahoo_packet_hash(pkt, 5, who); + yahoo_send_packet(yid, pkt, 0); + + yahoo_packet_free(pkt); +} + +void yahoo_webcam_send_image(int id, unsigned char *image, unsigned int length, + unsigned int timestamp) +{ + struct yahoo_input_data *yid = + find_input_by_id_and_type(id, YAHOO_CONNECTION_WEBCAM); + unsigned char *packet; + unsigned char header_len = 13; + unsigned int pos = 0; + + if (!yid) + return; + + packet = y_new0(unsigned char, header_len); + + packet[pos++] = header_len; + packet[pos++] = 0; + packet[pos++] = 5; /* version byte?? */ + packet[pos++] = 0; + pos += yahoo_put32(packet + pos, length); + packet[pos++] = 2; /* packet type, image */ + pos += yahoo_put32(packet + pos, timestamp); + yahoo_add_to_send_queue(yid, packet, header_len); + FREE(packet); + + if (length) + yahoo_add_to_send_queue(yid, image, length); +} + +void yahoo_webcam_accept_viewer(int id, const char *who, int accept) +{ + struct yahoo_input_data *yid = + find_input_by_id_and_type(id, YAHOO_CONNECTION_WEBCAM); + char *packet = NULL; + char *data = NULL; + unsigned char header_len = 13; + unsigned int pos = 0; + unsigned int len = 0; + + if (!yid) + return; + + data = strdup("u="); + data = y_string_append(data, (char *)who); + data = y_string_append(data, "\r\n"); + len = strlen(data); + + packet = y_new0(char, header_len + len); + packet[pos++] = header_len; + packet[pos++] = 0; + packet[pos++] = 5; /* version byte?? */ + packet[pos++] = 0; + pos += yahoo_put32(packet + pos, len); + packet[pos++] = 0; /* packet type */ + pos += yahoo_put32(packet + pos, accept); + memcpy(packet + pos, data, len); + FREE(data); + yahoo_add_to_send_queue(yid, packet, header_len + len); + FREE(packet); +} + +void yahoo_webcam_invite(int id, const char *who) +{ + struct yahoo_input_data *yid = + find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); + struct yahoo_packet *pkt; + + if (!yid) + return; + + pkt = yahoo_packet_new(YAHOO_SERVICE_NOTIFY, YPACKET_STATUS_NOTIFY, + yid->yd->session_id); + + yahoo_packet_hash(pkt, 49, "WEBCAMINVITE"); + yahoo_packet_hash(pkt, 14, " "); + yahoo_packet_hash(pkt, 13, "0"); + yahoo_packet_hash(pkt, 1, yid->yd->user); + yahoo_packet_hash(pkt, 5, who); + yahoo_send_packet(yid, pkt, 0); + + yahoo_packet_free(pkt); +} + +static void yahoo_search_internal(int id, int t, const char *text, int g, + int ar, int photo, int yahoo_only, int startpos, int total) +{ + struct yahoo_data *yd = find_conn_by_id(id); + struct yahoo_input_data *yid; + char url[1024]; + char buff[1024]; + char *ctext, *p; + + if (!yd) + return; + + yid = y_new0(struct yahoo_input_data, 1); + yid->yd = yd; + yid->type = YAHOO_CONNECTION_SEARCH; + + /* + age range + .ar=1 - 13-18, 2 - 18-25, 3 - 25-35, 4 - 35-50, 5 - 50-70, 6 - 70+ + */ + + snprintf(buff, sizeof(buff), "&.sq=%%20&.tt=%d&.ss=%d", total, + startpos); + + ctext = strdup(text); + while ((p = strchr(ctext, ' '))) + *p = '+'; + + snprintf(url, 1024, + "http://members.yahoo.com/interests?.oc=m&.kw=%s&.sb=%d&.g=%d&.ar=0%s%s%s", + ctext, t, g, photo ? "&.p=y" : "", yahoo_only ? "&.pg=y" : "", + startpos ? buff : ""); + + FREE(ctext); + + snprintf(buff, sizeof(buff), "Y=%s; T=%s", yd->cookie_y, yd->cookie_t); + + inputs = y_list_prepend(inputs, yid); + yahoo_http_get(yid->yd->client_id, url, buff, 0, 0, + _yahoo_http_connected, yid); +} + +void yahoo_search(int id, enum yahoo_search_type t, const char *text, + enum yahoo_search_gender g, enum yahoo_search_agerange ar, int photo, + int yahoo_only) +{ + struct yahoo_input_data *yid = + find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); + struct yahoo_search_state *yss; + + if (!yid) + return; + + if (!yid->ys) + yid->ys = y_new0(struct yahoo_search_state, 1); + + yss = yid->ys; + + FREE(yss->lsearch_text); + yss->lsearch_type = t; + yss->lsearch_text = strdup(text); + yss->lsearch_gender = g; + yss->lsearch_agerange = ar; + yss->lsearch_photo = photo; + yss->lsearch_yahoo_only = yahoo_only; + + yahoo_search_internal(id, t, text, g, ar, photo, yahoo_only, 0, 0); +} + +void yahoo_search_again(int id, int start) +{ + struct yahoo_input_data *yid = + find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); + struct yahoo_search_state *yss; + + if (!yid || !yid->ys) + return; + + yss = yid->ys; + + if (start == -1) + start = yss->lsearch_nstart + yss->lsearch_nfound; + + yahoo_search_internal(id, yss->lsearch_type, yss->lsearch_text, + yss->lsearch_gender, yss->lsearch_agerange, + yss->lsearch_photo, yss->lsearch_yahoo_only, + start, yss->lsearch_ntotal); +} + +void yahoo_send_picture(int id, const char *name, unsigned long size, + yahoo_get_fd_callback callback, void *data) +{ + /* Not Implemented */ +} + +/* File Transfer */ +static YList *active_file_transfers = NULL; + +enum { + FT_STATE_HEAD = 1, + FT_STATE_RECV, + FT_STATE_RECV_START, + FT_STATE_SEND +}; + +struct send_file_data { + int client_id; + char *id; + char *who; + char *filename; + char *ip_addr; + char *token; + int size; + + struct yahoo_input_data *yid; + int state; + + yahoo_get_fd_callback callback; + void *data; +}; + +static char *yahoo_get_random(void) +{ + int i = 0; + int r = 0; + int c = 0; + char out[25]; + + out[24] = '\0'; + out[23] = '$'; + out[22] = '$'; + + for (i = 0; i < 22; i++) { + if(r == 0) + r = rand(); + + c = r%61; + + if(c<26) + out[i] = c + 'a'; + else if (c<52) + out[i] = c - 26 + 'A'; + else + out[i] = c - 52 + '0'; + + r /= 61; + } + + return strdup(out); +} + +static int _are_same_id(const void *sfd1, const void *id) +{ + return strcmp(((struct send_file_data *)sfd1)->id, (char *)id); +} + +static int _are_same_yid(const void *sfd1, const void *yid) +{ + if(((struct send_file_data *)sfd1)->yid == yid) + return 0; + else + return 1; +} + +static struct send_file_data *yahoo_get_active_transfer(char *id) +{ + YList *l = y_list_find_custom(active_file_transfers, id, + _are_same_id); + + if(l) + return (struct send_file_data *)l->data; + + return NULL; +} + +static struct send_file_data *yahoo_get_active_transfer_with_yid(void *yid) +{ + YList *l = y_list_find_custom(active_file_transfers, yid, + _are_same_yid); + + if(l) + return (struct send_file_data *)l->data; + + return NULL; +} + +static void yahoo_add_active_transfer(struct send_file_data *sfd) +{ + active_file_transfers = y_list_prepend(active_file_transfers, sfd); +} + +static void yahoo_remove_active_transfer(struct send_file_data *sfd) +{ + if (sfd == NULL) + return; + + active_file_transfers = y_list_remove(active_file_transfers, sfd); + free(sfd->id); + free(sfd->who); + free(sfd->filename); + free(sfd->ip_addr); + FREE(sfd); +} + +static void _yahoo_ft_upload_connected(int id, void *fd, int error, void *data) +{ + struct send_file_data *sfd = data; + struct yahoo_input_data *yid = sfd->yid; + + if (!fd) { + inputs = y_list_remove(inputs, yid); + FREE(yid); + return; + } + + sfd->callback(id, fd, error, sfd->data); + + yid->fd = fd; + yid->read_tag = + YAHOO_CALLBACK(ext_yahoo_add_handler) (yid->yd->client_id, fd, + YAHOO_INPUT_READ, yid); +} + +static void yahoo_file_transfer_upload(struct yahoo_data *yd, + struct send_file_data *sfd) +{ + char url[256]; + char buff[4096]; + char *sender_enc = NULL, *recv_enc = NULL, *token_enc = NULL; + + struct yahoo_input_data *yid = y_new0(struct yahoo_input_data, 1); + + yid->yd = yd; + yid->type = YAHOO_CONNECTION_FT; + + inputs = y_list_prepend(inputs, yid); + sfd->yid = yid; + sfd->state = FT_STATE_SEND; + + token_enc = yahoo_urlencode(sfd->token); + sender_enc = yahoo_urlencode(yd->user); + recv_enc = yahoo_urlencode(sfd->who); + + snprintf(url, sizeof(url), + "http://%s/relay?token=%s&sender=%s&recver=%s", sfd->ip_addr, + token_enc, sender_enc, recv_enc); + + snprintf(buff, sizeof(buff), "T=%s; Y=%s", yd->cookie_t, yd->cookie_y); + + yahoo_http_post(yd->client_id, url, buff, sfd->size, + _yahoo_ft_upload_connected, sfd); + + FREE(token_enc); + FREE(sender_enc); + FREE(recv_enc); +} + +static void yahoo_init_ft_recv(struct yahoo_data *yd, + struct send_file_data *sfd) +{ + char url[256]; + char buff[1024]; + char *sender_enc = NULL, *recv_enc = NULL, *token_enc = NULL; + + struct yahoo_input_data *yid = y_new0(struct yahoo_input_data, 1); + + yid->yd = yd; + yid->type = YAHOO_CONNECTION_FT; + + inputs = y_list_prepend(inputs, yid); + sfd->yid = yid; + sfd->state = FT_STATE_HEAD; + + token_enc = yahoo_urlencode(sfd->token); + sender_enc = yahoo_urlencode(sfd->who); + recv_enc = yahoo_urlencode(yd->user); + + snprintf(url, sizeof(url), + "http://%s/relay?token=%s&sender=%s&recver=%s", sfd->ip_addr, + token_enc, sender_enc, recv_enc); + + snprintf(buff, sizeof(buff), "Y=%s; T=%s", yd->cookie_y, yd->cookie_t); + + yahoo_http_head(yid->yd->client_id, url, buff, 0, NULL, + _yahoo_http_connected, yid); + + FREE(token_enc); + FREE(sender_enc); + FREE(recv_enc); +} + +static void yahoo_file_transfer_accept(struct yahoo_input_data *yid, + struct send_file_data *sfd) +{ + struct yahoo_packet *pkt; + + pkt = yahoo_packet_new(YAHOO_SERVICE_Y7_FILETRANSFERACCEPT, + YPACKET_STATUS_DEFAULT, yid->yd->session_id); + + yahoo_packet_hash(pkt, 1, yid->yd->user); + yahoo_packet_hash(pkt, 5, sfd->who); + yahoo_packet_hash(pkt, 265, sfd->id); + yahoo_packet_hash(pkt, 27, sfd->filename); + yahoo_packet_hash(pkt, 249, "3"); + yahoo_packet_hash(pkt, 251, sfd->token); + + yahoo_send_packet(yid, pkt, 0); + + yahoo_packet_free(pkt); + + yahoo_init_ft_recv(yid->yd, sfd); +} + +static void yahoo_process_filetransferaccept(struct yahoo_input_data *yid, + struct yahoo_packet *pkt) +{ + YList *l; + struct send_file_data *sfd; + char *who = NULL; + char *filename = NULL; + char *id = NULL; + char *token = NULL; + + for (l = pkt->hash; l; l = l->next) { + struct yahoo_pair *pair = l->data; + switch (pair->key) { + case 4: + who = pair->value; + break; + case 5: + /* Me... don't care */ + break; + case 249: + break; + case 265: + id = pair->value; + break; + case 251: + token = pair->value; + break; + case 27: + filename = pair->value; + break; + } + } + + sfd = yahoo_get_active_transfer(id); + + if (sfd) { + sfd->token = strdup(token); + + yahoo_file_transfer_upload(yid->yd, sfd); + } + else { + YAHOO_CALLBACK(ext_yahoo_file_transfer_done) + (yid->yd->client_id, YAHOO_FILE_TRANSFER_UNKNOWN, + sfd ? sfd->data : NULL); + + yahoo_remove_active_transfer(sfd); + } +} + +static void yahoo_process_filetransferinfo(struct yahoo_input_data *yid, + struct yahoo_packet *pkt) +{ + YList *l; + char *who = NULL; + char *filename = NULL; + char *id = NULL; + char *token = NULL; + char *ip_addr = NULL; + + struct send_file_data *sfd; + + for (l = pkt->hash; l; l = l->next) { + struct yahoo_pair *pair = l->data; + switch (pair->key) { + case 1: + case 4: + who = pair->value; + break; + case 5: + /* Me... don't care */ + break; + case 249: + break; + case 265: + id = pair->value; + break; + case 250: + ip_addr = pair->value; + break; + case 251: + token = pair->value; + break; + case 27: + filename = pair->value; + break; + } + } + + sfd = yahoo_get_active_transfer(id); + + if (sfd) { + sfd->token = strdup(token); + sfd->ip_addr = strdup(ip_addr); + + yahoo_file_transfer_accept(yid, sfd); + } + else { + YAHOO_CALLBACK(ext_yahoo_file_transfer_done) + (yid->yd->client_id, YAHOO_FILE_TRANSFER_UNKNOWN, + sfd ? sfd->data : NULL); + + yahoo_remove_active_transfer(sfd); + } +} + +static void yahoo_send_filetransferinfo(struct yahoo_data *yd, + struct send_file_data *sfd) +{ + struct yahoo_input_data *yid; + struct yahoo_packet *pkt; + + yid = find_input_by_id_and_type(yd->client_id, YAHOO_CONNECTION_PAGER); + sfd->ip_addr = YAHOO_CALLBACK(ext_yahoo_get_ip_addr)("relay.yahoo.com"); + + if (!sfd->ip_addr) { + YAHOO_CALLBACK(ext_yahoo_file_transfer_done) + (yd->client_id, YAHOO_FILE_TRANSFER_RELAY, sfd->data); + + yahoo_remove_active_transfer(sfd); + + return; + } + + pkt = yahoo_packet_new(YAHOO_SERVICE_Y7_FILETRANSFERINFO, + YPACKET_STATUS_DEFAULT, yd->session_id); + + yahoo_packet_hash(pkt, 1, yd->user); + yahoo_packet_hash(pkt, 5, sfd->who); + yahoo_packet_hash(pkt, 265, sfd->id); + yahoo_packet_hash(pkt, 27, sfd->filename); + yahoo_packet_hash(pkt, 249, "3"); + yahoo_packet_hash(pkt, 250, sfd->ip_addr); + + yahoo_send_packet(yid, pkt, 0); + + yahoo_packet_free(pkt); +} + +static void yahoo_process_filetransfer(struct yahoo_input_data *yid, + struct yahoo_packet *pkt) +{ + YList *l; + char *who = NULL; + char *filename = NULL; + char *msg = NULL; + char *id = NULL; + int action = 0; + int size = 0; + struct yahoo_data *yd = yid->yd; + + struct send_file_data *sfd; + + for (l = pkt->hash; l; l = l->next) { + struct yahoo_pair *pair = l->data; + switch (pair->key) { + case 4: + who = pair->value; + break; + case 5: + /* Me... don't care */ + break; + case 222: + action = atoi(pair->value); + break; + case 265: + id = pair->value; + break; + case 266: /* Don't know */ + break; + case 302: /* Start Data? */ + break; + case 300: + break; + case 27: + filename = pair->value; + break; + case 28: + size = atoi(pair->value); + break; + case 14: + msg = pair->value; + case 301: /* End Data? */ + break; + case 303: + break; + + } + } + + if (action == YAHOO_FILE_TRANSFER_INIT) { + /* Received a FT request from buddy */ + sfd = y_new0(struct send_file_data, 1); + + sfd->client_id = yd->client_id; + sfd->id = strdup(id); + sfd->who = strdup(who); + sfd->filename = strdup(filename); + sfd->size = size; + + yahoo_add_active_transfer(sfd); + + YAHOO_CALLBACK(ext_yahoo_got_file) (yd->client_id, yd->user, + who, msg, filename, size, sfd->id); + } + else { + /* Response to our request */ + sfd = yahoo_get_active_transfer(id); + + if (sfd && action == YAHOO_FILE_TRANSFER_ACCEPT) { + yahoo_send_filetransferinfo(yd, sfd); + } + else if (!sfd || action == YAHOO_FILE_TRANSFER_REJECT) { + YAHOO_CALLBACK(ext_yahoo_file_transfer_done) + (yd->client_id, YAHOO_FILE_TRANSFER_REJECT, + sfd ? sfd->data : NULL); + + yahoo_remove_active_transfer(sfd); + } + } +} + +void yahoo_send_file(int id, const char *who, const char *msg, + const char *name, unsigned long size, + yahoo_get_fd_callback callback, void *data) +{ + struct yahoo_packet *pkt = NULL; + char size_str[10]; + struct yahoo_input_data *yid; + struct yahoo_data *yd; + struct send_file_data *sfd; + + yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); + yd = find_conn_by_id(id); + sfd = y_new0(struct send_file_data, 1); + + sfd->client_id = id; + sfd->id = yahoo_get_random(); + sfd->who = strdup(who); + sfd->filename = strdup(name); + sfd->size = size; + sfd->callback = callback; + sfd->data = data; + + yahoo_add_active_transfer(sfd); + + if (!yd) + return; + + pkt = yahoo_packet_new(YAHOO_SERVICE_Y7_FILETRANSFER, + YPACKET_STATUS_DEFAULT, yd->session_id); + + snprintf(size_str, sizeof(size_str), "%ld", size); + + yahoo_packet_hash(pkt, 1, yd->user); + yahoo_packet_hash(pkt, 5, who); + yahoo_packet_hash(pkt, 265, sfd->id); + yahoo_packet_hash(pkt, 222, "1"); + yahoo_packet_hash(pkt, 266, "1"); + yahoo_packet_hash(pkt, 302, "268"); + yahoo_packet_hash(pkt, 300, "268"); + yahoo_packet_hash(pkt, 27, name); + yahoo_packet_hash(pkt, 28, size_str); + yahoo_packet_hash(pkt, 301, "268"); + yahoo_packet_hash(pkt, 303, "268"); + + yahoo_send_packet(yid, pkt, 0); + + yahoo_packet_free(pkt); +} + +void yahoo_send_file_transfer_response(int client_id, int response, char *id, void *data) +{ + struct yahoo_packet *pkt = NULL; + char resp[2]; + struct yahoo_input_data *yid; + + struct send_file_data *sfd = yahoo_get_active_transfer(id); + + sfd->data = data; + + yid = find_input_by_id_and_type(client_id, YAHOO_CONNECTION_PAGER); + + pkt = yahoo_packet_new(YAHOO_SERVICE_Y7_FILETRANSFER, + YPACKET_STATUS_DEFAULT, yid->yd->session_id); + + snprintf(resp, sizeof(resp), "%d", response); + + yahoo_packet_hash(pkt, 1, yid->yd->user); + yahoo_packet_hash(pkt, 5, sfd->who); + yahoo_packet_hash(pkt, 265, sfd->id); + yahoo_packet_hash(pkt, 222, resp); + + yahoo_send_packet(yid, pkt, 0); + + yahoo_packet_free(pkt); + + if(response == YAHOO_FILE_TRANSFER_REJECT) + yahoo_remove_active_transfer(sfd); +} + +static void yahoo_process_ft_connection(struct yahoo_input_data *yid, int over) +{ + struct send_file_data *sfd; + struct yahoo_data *yd = yid->yd; + + sfd = yahoo_get_active_transfer_with_yid(yid); + + if (!sfd) { + LOG(("Something funny happened. yid %p has no sfd.\n", yid)); + return; + } + + /* + * We want to handle only the complete data with HEAD since we don't + * want a situation where both the GET and HEAD are active. + * With SEND, we really can't do much with partial response + */ + if ((sfd->state == FT_STATE_HEAD || sfd->state == FT_STATE_SEND) + && !over) + return; + + if (sfd->state == FT_STATE_HEAD) { + /* Do a GET */ + char url[256]; + char buff[1024]; + char *sender_enc = NULL, *recv_enc = NULL, *token_enc = NULL; + + struct yahoo_input_data *yid_ft = + y_new0(struct yahoo_input_data, 1); + + yid_ft->yd = yid->yd; + yid_ft->type = YAHOO_CONNECTION_FT; + + inputs = y_list_prepend(inputs, yid_ft); + sfd->yid = yid_ft; + sfd->state = FT_STATE_RECV; + + token_enc = yahoo_urlencode(sfd->token); + sender_enc = yahoo_urlencode(sfd->who); + recv_enc = yahoo_urlencode(yd->user); + + snprintf(url, sizeof(url), + "http://%s/relay?token=%s&sender=%s&recver=%s", sfd->ip_addr, + token_enc, sender_enc, recv_enc); + + snprintf(buff, sizeof(buff), "Y=%s; T=%s", yd->cookie_y, + yd->cookie_t); + + + yahoo_http_get(yd->client_id, url, buff, 1, 1, + _yahoo_http_connected, yid_ft); + + FREE(token_enc); + FREE(sender_enc); + FREE(recv_enc); + } + else if (sfd->state == FT_STATE_RECV || + sfd->state == FT_STATE_RECV_START) { + + unsigned char *data_begin = NULL; + + if (yid->rxlen == 0) + yahoo_remove_active_transfer(sfd); + + if (sfd->state != FT_STATE_RECV_START && + (data_begin = + (unsigned char *)strstr((char *)yid->rxqueue, + "\r\n\r\n"))) { + + sfd->state = FT_STATE_RECV_START; + + yid->rxlen -= 4+(data_begin-yid->rxqueue)/sizeof(char); + data_begin += 4; + + if (yid->rxlen > 0) + YAHOO_CALLBACK(ext_yahoo_got_ft_data) + (yd->client_id, data_begin, + yid->rxlen, sfd->data); + } + else if (sfd->state == FT_STATE_RECV_START) + YAHOO_CALLBACK(ext_yahoo_got_ft_data) (yd->client_id, + yid->rxqueue, yid->rxlen, sfd->data); + + FREE(yid->rxqueue); + yid->rxqueue = NULL; + yid->rxlen = 0; + } + else if (sfd->state == FT_STATE_SEND) { + /* Sent file completed */ + int len = 0; + char *off = strstr((char *)yid->rxqueue, "Content-Length: "); + + if (off) { + off += 16; + len = atoi(off); + } + + if (len < sfd->size) + YAHOO_CALLBACK(ext_yahoo_file_transfer_done) + (yd->client_id, + YAHOO_FILE_TRANSFER_FAILED, sfd->data); + else + YAHOO_CALLBACK(ext_yahoo_file_transfer_done) + (yd->client_id, + YAHOO_FILE_TRANSFER_DONE, sfd->data); + + yahoo_remove_active_transfer(sfd); + } +} + +/* End File Transfer */ + +enum yahoo_status yahoo_current_status(int id) +{ + struct yahoo_data *yd = find_conn_by_id(id); + if (!yd) + return YAHOO_STATUS_OFFLINE; + return yd->current_status; +} + +const YList *yahoo_get_buddylist(int id) +{ + struct yahoo_data *yd = find_conn_by_id(id); + if (!yd) + return NULL; + return yd->buddies; +} + +const YList *yahoo_get_ignorelist(int id) +{ + struct yahoo_data *yd = find_conn_by_id(id); + if (!yd) + return NULL; + return yd->ignore; +} + +const YList *yahoo_get_identities(int id) +{ + struct yahoo_data *yd = find_conn_by_id(id); + if (!yd) + return NULL; + return yd->identities; +} + +const char *yahoo_get_cookie(int id, const char *which) +{ + struct yahoo_data *yd = find_conn_by_id(id); + if (!yd) + return NULL; + if (!strncasecmp(which, "y", 1)) + return yd->cookie_y; + if (!strncasecmp(which, "b", 1)) + return yd->cookie_b; + if (!strncasecmp(which, "t", 1)) + return yd->cookie_t; + if (!strncasecmp(which, "c", 1)) + return yd->cookie_c; + if (!strncasecmp(which, "login", 5)) + return yd->login_cookie; + return NULL; +} + +const char *yahoo_get_profile_url(void) +{ + return profile_url; +} |