source: protocols/yahoo/libyahoo2.c @ 386042c

Last change on this file since 386042c was 386042c, checked in by Wilmer van der Gaast <wilmer@…>, at 2012-10-19T23:16:52Z

Removing some more cruft now that I've (re)discovered ld garbage collection.
Mostly stuff related to obsolete Y!MSG login obfuscation schemes.

  • Property mode set to 100644
File size: 125.2 KB
Line 
1/*
2 * libyahoo2: libyahoo2.c
3 *
4 * Some code copyright (C) 2002-2004, Philip S Tellis <philip.tellis AT gmx.net>
5 * YMSG16 code copyright (C) 2009,
6 *              Siddhesh Poyarekar <siddhesh dot poyarekar at gmail dot com>
7 *
8 * Yahoo Search copyright (C) 2003, Konstantin Klyagin <konst AT konst.org.ua>
9 *
10 * Much of this code was taken and adapted from the yahoo module for
11 * gaim released under the GNU GPL.  This code is also released under the
12 * GNU GPL.
13 *
14 * This code is derivitive of Gaim <http://gaim.sourceforge.net>
15 * copyright (C) 1998-1999, Mark Spencer <markster@marko.net>
16 *             1998-1999, Adam Fritzler <afritz@marko.net>
17 *             1998-2002, Rob Flynn <rob@marko.net>
18 *             2000-2002, Eric Warmenhoven <eric@warmenhoven.org>
19 *             2001-2002, Brian Macke <macke@strangelove.net>
20 *                  2001, Anand Biligiri S <abiligiri@users.sf.net>
21 *                  2001, Valdis Kletnieks
22 *                  2002, Sean Egan <bj91704@binghamton.edu>
23 *                  2002, Toby Gray <toby.gray@ntlworld.com>
24 *
25 * This library also uses code from other libraries, namely:
26 *     Portions from libfaim copyright 1998, 1999 Adam Fritzler
27 *     <afritz@auk.cx>
28 *     Portions of Sylpheed copyright 2000-2002 Hiroyuki Yamamoto
29 *     <hiro-y@kcn.ne.jp>
30 *
31 * YMSG16 authentication code based mostly on write-up at:
32 *      http://www.carbonize.co.uk/ymsg16.html
33 *
34 * This program is free software; you can redistribute it and/or modify
35 * it under the terms of the GNU General Public License as published by
36 * the Free Software Foundation; either version 2 of the License, or
37 * (at your option) any later version.
38 *
39 * This program is distributed in the hope that it will be useful,
40 * but WITHOUT ANY WARRANTY; without even the implied warranty of
41 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
42 * GNU General Public License for more details.
43 *
44 * You should have received a copy of the GNU General Public License
45 * along with this program; if not, write to the Free Software
46 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
47 *
48 */
49
50#ifndef _WIN32
51#include <unistd.h>
52#endif
53#include <errno.h>
54#include <stdio.h>
55#include <stdarg.h>
56
57#if STDC_HEADERS
58# include <string.h>
59#else
60# if !HAVE_STRCHR
61#  define strchr index
62#  define strrchr rindex
63# endif
64char *strchr (), *strrchr ();
65# if !HAVE_MEMCPY
66#  define memcpy(d, s, n) bcopy ((s), (d), (n))
67#  define memmove(d, s, n) bcopy ((s), (d), (n))
68# endif
69#endif
70
71#include <sys/types.h>
72
73#ifdef __MINGW32__
74# include <winsock2.h>
75#endif
76
77#include <stdlib.h>
78#include <ctype.h>
79
80#include "sha1.h"
81#include "md5.h"
82#include "yahoo2.h"
83#include "yahoo_httplib.h"
84#include "yahoo_util.h"
85
86#include "yahoo2_callbacks.h"
87#include "yahoo_debug.h"
88#if defined(__MINGW32__) && !defined(HAVE_GLIB)
89#define snprintf _snprintf
90#define vsnprintf _vsnprintf
91#endif
92
93#include "base64.h"
94#include "http_client.h"
95
96#ifdef USE_STRUCT_CALLBACKS
97struct yahoo_callbacks *yc = NULL;
98
99void yahoo_register_callbacks(struct yahoo_callbacks *tyc)
100{
101        yc = tyc;
102}
103
104#define YAHOO_CALLBACK(x)       yc->x
105#else
106#define YAHOO_CALLBACK(x)       x
107#endif
108
109static int yahoo_send_data(void *fd, void *data, int len);
110static void _yahoo_http_connected(int id, void *fd, int error, void *data);
111static void yahoo_connected(void *fd, int error, void *data);
112
113int yahoo_log_message(char *fmt, ...)
114{
115        char out[1024];
116        va_list ap;
117        va_start(ap, fmt);
118        vsnprintf(out, sizeof(out), fmt, ap);
119        va_end(ap);
120        return YAHOO_CALLBACK(ext_yahoo_log) ("%s", out);
121}
122
123int yahoo_connect(char *host, int port)
124{
125        return YAHOO_CALLBACK(ext_yahoo_connect) (host, port);
126}
127
128static enum yahoo_log_level log_level = YAHOO_LOG_NONE;
129
130enum yahoo_log_level yahoo_get_log_level()
131{
132        return log_level;
133}
134
135int yahoo_set_log_level(enum yahoo_log_level level)
136{
137        enum yahoo_log_level l = log_level;
138        log_level = level;
139        return l;
140}
141
142/* default values for servers */
143static char *default_pager_hosts[] = {  "scs.msg.yahoo.com",
144                                        "scsa.msg.yahoo.com",
145                                        "scsb.msg.yahoo.com",
146                                        "scsc.msg.yahoo.com",
147                                        NULL};
148
149static int pager_port = 5050;
150static int fallback_ports[] = { 23, 25, 80, 20, 119, 8001, 8002, 5050, 0 };
151
152static char filetransfer_host[] = "filetransfer.msg.yahoo.com";
153static int filetransfer_port = 80;
154static char webcam_host[] = "webcam.yahoo.com";
155static int webcam_port = 5100;
156static char webcam_description[] = "";
157static char local_host[] = "";
158static int conn_type = Y_WCM_DSL;
159
160static char profile_url[] = "http://profiles.yahoo.com/";
161
162struct connect_callback_data {
163        struct yahoo_data *yd;
164        int tag;
165        int i;
166        int server_i;
167};
168
169struct yahoo_pair {
170        int key;
171        char *value;
172};
173
174struct yahoo_packet {
175        unsigned short int service;
176        unsigned int status;
177        unsigned int id;
178        YList *hash;
179};
180
181struct yahoo_search_state {
182        int lsearch_type;
183        char *lsearch_text;
184        int lsearch_gender;
185        int lsearch_agerange;
186        int lsearch_photo;
187        int lsearch_yahoo_only;
188        int lsearch_nstart;
189        int lsearch_nfound;
190        int lsearch_ntotal;
191};
192
193struct data_queue {
194        unsigned char *queue;
195        int len;
196};
197
198struct yahoo_input_data {
199        struct yahoo_data *yd;
200        struct yahoo_webcam *wcm;
201        struct yahoo_webcam_data *wcd;
202        struct yahoo_search_state *ys;
203
204        void *fd;
205        enum yahoo_connection_type type;
206
207        unsigned char *rxqueue;
208        int rxlen;
209        int read_tag;
210
211        YList *txqueues;
212        int write_tag;
213};
214
215struct yahoo_server_settings {
216        char *pager_host;
217        int pager_port;
218        char *filetransfer_host;
219        int filetransfer_port;
220        char *webcam_host;
221        int webcam_port;
222        char *webcam_description;
223        char *local_host;
224        int conn_type;
225        char **pager_host_list;
226};
227
228static void yahoo_process_ft_connection(struct yahoo_input_data *yid, int over);
229
230static void yahoo_process_filetransfer(struct yahoo_input_data *yid,
231        struct yahoo_packet *pkt);
232static void yahoo_process_filetransferinfo(struct yahoo_input_data *yid,
233        struct yahoo_packet *pkt);
234static void yahoo_process_filetransferaccept(struct yahoo_input_data *yid,
235        struct yahoo_packet *pkt);
236
237static void yahoo_https_auth(struct yahoo_input_data *yid, const char *seed, const char *sn);
238
239static void *_yahoo_default_server_settings()
240{
241        struct yahoo_server_settings *yss =
242                y_new0(struct yahoo_server_settings, 1);
243
244        /* Give preference to the default host list
245         * Make sure that only one of the two is set at any time
246         */
247        yss->pager_host = NULL;
248        yss->pager_host_list = default_pager_hosts;
249
250        yss->pager_port = pager_port;
251        yss->filetransfer_host = strdup(filetransfer_host);
252        yss->filetransfer_port = filetransfer_port;
253        yss->webcam_host = strdup(webcam_host);
254        yss->webcam_port = webcam_port;
255        yss->webcam_description = strdup(webcam_description);
256        yss->local_host = strdup(local_host);
257        yss->conn_type = conn_type;
258
259        return yss;
260}
261
262static void *_yahoo_assign_server_settings(va_list ap)
263{
264        struct yahoo_server_settings *yss = _yahoo_default_server_settings();
265        char *key;
266        char *svalue;
267        int nvalue;
268        char **pvalue;
269
270        while (1) {
271                key = va_arg(ap, char *);
272                if (key == NULL)
273                        break;
274
275                if (!strcmp(key, "pager_host")) {
276                        svalue = va_arg(ap, char *);
277                        free(yss->pager_host);
278                        yss->pager_host = strdup(svalue);
279                        yss->pager_host_list = NULL;
280                } else if (!strcmp(key, "pager_host_list")) {
281                        pvalue = va_arg(ap, char **);
282                        yss->pager_host_list = pvalue;
283                        free(yss->pager_host);
284                        yss->pager_host = NULL;
285                } else if (!strcmp(key, "pager_port")) {
286                        nvalue = va_arg(ap, int);
287                        yss->pager_port = nvalue;
288                } else if (!strcmp(key, "filetransfer_host")) {
289                        svalue = va_arg(ap, char *);
290                        free(yss->filetransfer_host);
291                        yss->filetransfer_host = strdup(svalue);
292                } else if (!strcmp(key, "filetransfer_port")) {
293                        nvalue = va_arg(ap, int);
294                        yss->filetransfer_port = nvalue;
295                } else if (!strcmp(key, "webcam_host")) {
296                        svalue = va_arg(ap, char *);
297                        free(yss->webcam_host);
298                        yss->webcam_host = strdup(svalue);
299                } else if (!strcmp(key, "webcam_port")) {
300                        nvalue = va_arg(ap, int);
301                        yss->webcam_port = nvalue;
302                } else if (!strcmp(key, "webcam_description")) {
303                        svalue = va_arg(ap, char *);
304                        free(yss->webcam_description);
305                        yss->webcam_description = strdup(svalue);
306                } else if (!strcmp(key, "local_host")) {
307                        svalue = va_arg(ap, char *);
308                        free(yss->local_host);
309                        yss->local_host = strdup(svalue);
310                } else if (!strcmp(key, "conn_type")) {
311                        nvalue = va_arg(ap, int);
312                        yss->conn_type = nvalue;
313                } else {
314                        WARNING(("Unknown key passed to yahoo_init, "
315                                        "perhaps you didn't terminate the list "
316                                        "with NULL"));
317                }
318        }
319
320        return yss;
321}
322
323static void yahoo_free_server_settings(struct yahoo_server_settings *yss)
324{
325        if (!yss)
326                return;
327
328        free(yss->pager_host);
329        free(yss->filetransfer_host);
330        free(yss->webcam_host);
331        free(yss->webcam_description);
332        free(yss->local_host);
333
334        free(yss);
335}
336
337static YList *conns = NULL;
338static YList *inputs = NULL;
339static int last_id = 0;
340
341static void add_to_list(struct yahoo_data *yd)
342{
343        conns = y_list_prepend(conns, yd);
344}
345
346static struct yahoo_data *find_conn_by_id(int id)
347{
348        YList *l;
349        for (l = conns; l; l = y_list_next(l)) {
350                struct yahoo_data *yd = l->data;
351                if (yd->client_id == id)
352                        return yd;
353        }
354        return NULL;
355}
356
357static void del_from_list(struct yahoo_data *yd)
358{
359        conns = y_list_remove(conns, yd);
360}
361
362/* call repeatedly to get the next one */
363/*
364static struct yahoo_input_data * find_input_by_id(int id)
365{
366        YList *l;
367        for(l = inputs; l; l = y_list_next(l)) {
368                struct yahoo_input_data *yid = l->data;
369                if(yid->yd->client_id == id)
370                        return yid;
371        }
372        return NULL;
373}
374*/
375
376static struct yahoo_input_data *find_input_by_id_and_webcam_user(int id,
377        const char *who)
378{
379        YList *l;
380        LOG(("find_input_by_id_and_webcam_user"));
381        for (l = inputs; l; l = y_list_next(l)) {
382                struct yahoo_input_data *yid = l->data;
383                if (yid->type == YAHOO_CONNECTION_WEBCAM
384                        && yid->yd->client_id == id && yid->wcm && ((who
385                                        && yid->wcm->user
386                                        && !strcmp(who, yid->wcm->user))
387                                || !(yid->wcm->user && !who)))
388                        return yid;
389        }
390        return NULL;
391}
392
393static struct yahoo_input_data *find_input_by_id_and_type(int id,
394        enum yahoo_connection_type type)
395{
396        YList *l;
397        LOG(("find_input_by_id_and_type"));
398        for (l = inputs; l; l = y_list_next(l)) {
399                struct yahoo_input_data *yid = l->data;
400                if (yid->type == type && yid->yd->client_id == id)
401                        return yid;
402        }
403        return NULL;
404}
405
406static struct yahoo_input_data *find_input_by_id_and_fd(int id, void *fd)
407{
408        YList *l;
409        LOG(("find_input_by_id_and_fd"));
410        for (l = inputs; l; l = y_list_next(l)) {
411                struct yahoo_input_data *yid = l->data;
412                if (yid->fd == fd && yid->yd->client_id == id)
413                        return yid;
414        }
415        return NULL;
416}
417
418static int count_inputs_with_id(int id)
419{
420        int c = 0;
421        YList *l;
422        LOG(("counting %d", id));
423        for (l = inputs; l; l = y_list_next(l)) {
424                struct yahoo_input_data *yid = l->data;
425                if (yid->yd->client_id == id)
426                        c++;
427        }
428        LOG(("%d", c));
429        return c;
430}
431
432/* Free a buddy list */
433static void yahoo_free_buddies(YList *list)
434{
435        YList *l;
436
437        for (l = list; l; l = l->next) {
438                struct yahoo_buddy *bud = l->data;
439                if (!bud)
440                        continue;
441
442                FREE(bud->group);
443                FREE(bud->id);
444                FREE(bud->real_name);
445                if (bud->yab_entry) {
446                        FREE(bud->yab_entry->fname);
447                        FREE(bud->yab_entry->lname);
448                        FREE(bud->yab_entry->nname);
449                        FREE(bud->yab_entry->id);
450                        FREE(bud->yab_entry->email);
451                        FREE(bud->yab_entry->hphone);
452                        FREE(bud->yab_entry->wphone);
453                        FREE(bud->yab_entry->mphone);
454                        FREE(bud->yab_entry);
455                }
456                FREE(bud);
457                l->data = bud = NULL;
458        }
459
460        y_list_free(list);
461}
462
463/* Free an identities list */
464static void yahoo_free_identities(YList *list)
465{
466        while (list) {
467                YList *n = list;
468                FREE(list->data);
469                list = y_list_remove_link(list, list);
470                y_list_free_1(n);
471        }
472}
473
474/* Free webcam data */
475static void yahoo_free_webcam(struct yahoo_webcam *wcm)
476{
477        if (wcm) {
478                FREE(wcm->user);
479                FREE(wcm->server);
480                FREE(wcm->key);
481                FREE(wcm->description);
482                FREE(wcm->my_ip);
483        }
484        FREE(wcm);
485}
486
487static void yahoo_free_data(struct yahoo_data *yd)
488{
489        FREE(yd->user);
490        FREE(yd->password);
491        FREE(yd->cookie_y);
492        FREE(yd->cookie_t);
493        FREE(yd->cookie_b);
494        FREE(yd->cookie_c);
495        FREE(yd->login_cookie);
496        FREE(yd->login_id);
497
498        yahoo_free_buddies(yd->buddies);
499        yahoo_free_buddies(yd->ignore);
500        yahoo_free_identities(yd->identities);
501
502        yahoo_free_server_settings(yd->server_settings);
503
504        FREE(yd);
505}
506
507#define YAHOO_PACKET_HDRLEN (4 + 2 + 2 + 2 + 2 + 4 + 4)
508
509static struct yahoo_packet *yahoo_packet_new(enum yahoo_service service,
510        enum ypacket_status status, int id)
511{
512        struct yahoo_packet *pkt = y_new0(struct yahoo_packet, 1);
513
514        pkt->service = service;
515        pkt->status = status;
516        pkt->id = id;
517
518        return pkt;
519}
520
521static void yahoo_packet_hash(struct yahoo_packet *pkt, int key,
522        const char *value)
523{
524        struct yahoo_pair *pair = y_new0(struct yahoo_pair, 1);
525        pair->key = key;
526        pair->value = strdup(value);
527        pkt->hash = y_list_append(pkt->hash, pair);
528}
529
530static int yahoo_packet_length(struct yahoo_packet *pkt)
531{
532        YList *l;
533
534        int len = 0;
535
536        for (l = pkt->hash; l; l = l->next) {
537                struct yahoo_pair *pair = l->data;
538                int tmp = pair->key;
539                do {
540                        tmp /= 10;
541                        len++;
542                } while (tmp);
543                len += 2;
544                len += strlen(pair->value);
545                len += 2;
546        }
547
548        return len;
549}
550
551#define yahoo_put16(buf, data) ( \
552                (*(buf) = (unsigned char)((data)>>8)&0xff), \
553                (*((buf)+1) = (unsigned char)(data)&0xff),  \
554                2)
555#define yahoo_get16(buf) ((((*(buf))&0xff)<<8) + ((*((buf)+1)) & 0xff))
556#define yahoo_put32(buf, data) ( \
557                (*((buf)) = (unsigned char)((data)>>24)&0xff), \
558                (*((buf)+1) = (unsigned char)((data)>>16)&0xff), \
559                (*((buf)+2) = (unsigned char)((data)>>8)&0xff), \
560                (*((buf)+3) = (unsigned char)(data)&0xff), \
561                4)
562#define yahoo_get32(buf) ((((*(buf)   )&0xff)<<24) + \
563                         (((*((buf)+1))&0xff)<<16) + \
564                         (((*((buf)+2))&0xff)<< 8) + \
565                         (((*((buf)+3))&0xff)))
566
567static void yahoo_packet_read(struct yahoo_packet *pkt, unsigned char *data,
568        int len)
569{
570        int pos = 0;
571
572        while (pos + 1 < len) {
573                char *key, *value = NULL;
574                int accept;
575                int x;
576
577                struct yahoo_pair *pair = y_new0(struct yahoo_pair, 1);
578
579                key = malloc(len + 1);
580                x = 0;
581                while (pos + 1 < len) {
582                        if (data[pos] == 0xc0 && data[pos + 1] == 0x80)
583                                break;
584                        key[x++] = data[pos++];
585                }
586                key[x] = 0;
587                pos += 2;
588                pair->key = strtol(key, NULL, 10);
589                free(key);
590               
591                /* Libyahoo2 developer(s) don't seem to have the time to fix
592                   this problem, so for now try to work around it:
593                   
594                   Sometimes we receive an invalid packet with not any more
595                   data at this point. I don't know how to handle this in a
596                   clean way, but let's hope this is clean enough: */
597               
598                if (pos + 1 < len) {
599                        accept = x; 
600                        /* if x is 0 there was no key, so don't accept it */
601                        if (accept)
602                                value = malloc(len - pos + 1);
603                        x = 0;
604                        while (pos + 1 < len) {
605                                if (data[pos] == 0xc0 && data[pos + 1] == 0x80)
606                                        break;
607                                if (accept)
608                                        value[x++] = data[pos++];
609                        }
610                        if (accept)
611                                value[x] = 0;
612                        pos += 2;
613                } else {
614                        accept = 0;
615                }
616               
617                if (accept) {
618                        pair->value = strdup(value);
619                        FREE(value);
620                        pkt->hash = y_list_append(pkt->hash, pair);
621                        DEBUG_MSG(("Key: %d  \tValue: %s", pair->key,
622                                        pair->value));
623                } else {
624                        FREE(pair);
625                }
626        }
627}
628
629static void yahoo_packet_write(struct yahoo_packet *pkt, unsigned char *data)
630{
631        YList *l;
632        int pos = 0;
633
634        for (l = pkt->hash; l; l = l->next) {
635                struct yahoo_pair *pair = l->data;
636                unsigned char buf[100];
637
638                snprintf((char *)buf, sizeof(buf), "%d", pair->key);
639                strcpy((char *)data + pos, (char *)buf);
640                pos += strlen((char *)buf);
641                data[pos++] = 0xc0;
642                data[pos++] = 0x80;
643
644                strcpy((char *)data + pos, pair->value);
645                pos += strlen(pair->value);
646                data[pos++] = 0xc0;
647                data[pos++] = 0x80;
648        }
649}
650
651static void yahoo_dump_unhandled(struct yahoo_packet *pkt)
652{
653        YList *l;
654
655        NOTICE(("Service: 0x%02x\tStatus: %d", pkt->service, pkt->status));
656        for (l = pkt->hash; l; l = l->next) {
657                struct yahoo_pair *pair = l->data;
658                NOTICE(("\t%d => %s", pair->key, pair->value));
659        }
660}
661
662static void yahoo_packet_dump(unsigned char *data, int len)
663{
664        if (yahoo_get_log_level() >= YAHOO_LOG_DEBUG) {
665                int i;
666                for (i = 0; i < len; i++) {
667                        if ((i % 8 == 0) && i)
668                                YAHOO_CALLBACK(ext_yahoo_log) (" ");
669                        if ((i % 16 == 0) && i)
670                                YAHOO_CALLBACK(ext_yahoo_log) ("\n");
671                        YAHOO_CALLBACK(ext_yahoo_log) ("%02x ", data[i]);
672                }
673                YAHOO_CALLBACK(ext_yahoo_log) ("\n");
674                for (i = 0; i < len; i++) {
675                        if ((i % 8 == 0) && i)
676                                YAHOO_CALLBACK(ext_yahoo_log) (" ");
677                        if ((i % 16 == 0) && i)
678                                YAHOO_CALLBACK(ext_yahoo_log) ("\n");
679                        if (isprint(data[i]))
680                                YAHOO_CALLBACK(ext_yahoo_log) (" %c ", data[i]);
681                        else
682                                YAHOO_CALLBACK(ext_yahoo_log) (" . ");
683                }
684                YAHOO_CALLBACK(ext_yahoo_log) ("\n");
685        }
686}
687
688/* raw bytes in quasi-big-endian order to base 64 string (NUL-terminated) */
689static void to_y64(unsigned char *out, const unsigned char *in, int inlen)
690{
691        base64_encode_real(in, inlen, out, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789._-");
692}
693
694static void yahoo_add_to_send_queue(struct yahoo_input_data *yid, void *data,
695        int length)
696{
697        struct data_queue *tx = y_new0(struct data_queue, 1);
698        tx->queue = y_new0(unsigned char, length);
699        tx->len = length;
700        memcpy(tx->queue, data, length);
701
702        yid->txqueues = y_list_append(yid->txqueues, tx);
703
704        if (!yid->write_tag)
705                yid->write_tag =
706                        YAHOO_CALLBACK(ext_yahoo_add_handler) (yid->yd->
707                        client_id, yid->fd, YAHOO_INPUT_WRITE, yid);
708}
709
710static void yahoo_send_packet(struct yahoo_input_data *yid,
711        struct yahoo_packet *pkt, int extra_pad)
712{
713        int pktlen = yahoo_packet_length(pkt);
714        int len = YAHOO_PACKET_HDRLEN + pktlen;
715        unsigned char *data;
716        int pos = 0;
717
718        if (yid->fd < 0)
719                return;
720
721        data = y_new0(unsigned char, len + 1);
722
723        memcpy(data + pos, "YMSG", 4);
724        pos += 4;
725        pos += yahoo_put16(data + pos, YAHOO_PROTO_VER);        /* version [latest 12 0x000c] */
726        pos += yahoo_put16(data + pos, 0x0000); /* HIWORD pkt length??? */
727        pos += yahoo_put16(data + pos, pktlen + extra_pad);     /* LOWORD pkt length? */
728        pos += yahoo_put16(data + pos, pkt->service);   /* service */
729        pos += yahoo_put32(data + pos, pkt->status);    /* status [4bytes] */
730        pos += yahoo_put32(data + pos, pkt->id);        /* session [4bytes] */
731
732        yahoo_packet_write(pkt, data + pos);
733
734        yahoo_packet_dump(data, len);
735
736        if (yid->type == YAHOO_CONNECTION_FT)
737                yahoo_send_data(yid->fd, data, len);
738        else
739                yahoo_add_to_send_queue(yid, data, len);
740        FREE(data);
741}
742
743static void yahoo_packet_free(struct yahoo_packet *pkt)
744{
745        while (pkt->hash) {
746                struct yahoo_pair *pair = pkt->hash->data;
747                YList *tmp;
748                FREE(pair->value);
749                FREE(pair);
750                tmp = pkt->hash;
751                pkt->hash = y_list_remove_link(pkt->hash, pkt->hash);
752                y_list_free_1(tmp);
753        }
754        FREE(pkt);
755}
756
757static int yahoo_send_data(void *fd, void *data, int len)
758{
759        int ret;
760        int e;
761
762        if (fd == NULL)
763                return -1;
764
765        yahoo_packet_dump(data, len);
766
767        do {
768                ret = YAHOO_CALLBACK(ext_yahoo_write) (fd, data, len);
769        } while (ret == -1 && errno == EINTR);
770        e = errno;
771
772        if (ret == -1) {
773                LOG(("wrote data: ERR %s", strerror(errno)));
774        } else {
775                LOG(("wrote data: OK"));
776        }
777
778        errno = e;
779        return ret;
780}
781
782void yahoo_close(int id)
783{
784        struct yahoo_data *yd = find_conn_by_id(id);
785        if (!yd)
786                return;
787
788        del_from_list(yd);
789
790        yahoo_free_data(yd);
791        if (id == last_id)
792                last_id--;
793}
794
795static void yahoo_input_close(struct yahoo_input_data *yid)
796{
797        inputs = y_list_remove(inputs, yid);
798
799        LOG(("yahoo_input_close(read)"));
800        YAHOO_CALLBACK(ext_yahoo_remove_handler) (yid->yd->client_id,
801                yid->read_tag);
802        LOG(("yahoo_input_close(write)"));
803        YAHOO_CALLBACK(ext_yahoo_remove_handler) (yid->yd->client_id,
804                yid->write_tag);
805        yid->read_tag = yid->write_tag = 0;
806        if (yid->fd)
807                YAHOO_CALLBACK(ext_yahoo_close) (yid->fd);
808        yid->fd = 0;
809        FREE(yid->rxqueue);
810        if (count_inputs_with_id(yid->yd->client_id) == 0) {
811                LOG(("closing %d", yid->yd->client_id));
812                yahoo_close(yid->yd->client_id);
813        }
814        yahoo_free_webcam(yid->wcm);
815        if (yid->wcd)
816                FREE(yid->wcd);
817        if (yid->ys) {
818                FREE(yid->ys->lsearch_text);
819                FREE(yid->ys);
820        }
821        FREE(yid);
822}
823
824static int is_same_bud(const void *a, const void *b)
825{
826        const struct yahoo_buddy *subject = a;
827        const struct yahoo_buddy *object = b;
828
829        return strcmp(subject->id, object->id);
830}
831
832static char *getcookie(char *rawcookie)
833{
834        char *cookie = NULL;
835        char *tmpcookie;
836        char *cookieend;
837
838        if (strlen(rawcookie) < 2)
839                return NULL;
840
841        tmpcookie = strdup(rawcookie + 2);
842        cookieend = strchr(tmpcookie, ';');
843
844        if (cookieend)
845                *cookieend = '\0';
846
847        cookie = strdup(tmpcookie);
848        FREE(tmpcookie);
849        /* cookieend=NULL;  not sure why this was there since the value is not preserved in the stack -dd */
850
851        return cookie;
852}
853
854static char *getlcookie(char *cookie)
855{
856        char *tmp;
857        char *tmpend;
858        char *login_cookie = NULL;
859
860        tmpend = strstr(cookie, "n=");
861        if (tmpend) {
862                tmp = strdup(tmpend + 2);
863                tmpend = strchr(tmp, '&');
864                if (tmpend)
865                        *tmpend = '\0';
866                login_cookie = strdup(tmp);
867                FREE(tmp);
868        }
869
870        return login_cookie;
871}
872
873static void yahoo_process_notify(struct yahoo_input_data *yid,
874        struct yahoo_packet *pkt)
875{
876        struct yahoo_data *yd = yid->yd;
877        char *msg = NULL;
878        char *from = NULL;
879        char *to = NULL;
880        int stat = 0;
881        int accept = 0;
882        char *ind = NULL;
883        YList *l;
884        for (l = pkt->hash; l; l = l->next) {
885                struct yahoo_pair *pair = l->data;
886                if (pair->key == 4)
887                        from = pair->value;
888                if (pair->key == 5)
889                        to = pair->value;
890                if (pair->key == 49)
891                        msg = pair->value;
892                if (pair->key == 13)
893                        stat = atoi(pair->value);
894                if (pair->key == 14)
895                        ind = pair->value;
896                if (pair->key == 16) {  /* status == -1 */
897                        NOTICE((pair->value));
898                        return;
899                }
900
901        }
902
903        if (!msg)
904                return;
905
906        if (!strncasecmp(msg, "TYPING", strlen("TYPING")))
907                YAHOO_CALLBACK(ext_yahoo_typing_notify) (yd->client_id, to,
908                        from, stat);
909        else if (!strncasecmp(msg, "GAME", strlen("GAME")))
910                YAHOO_CALLBACK(ext_yahoo_game_notify) (yd->client_id, to, from,
911                        stat, ind);
912        else if (!strncasecmp(msg, "WEBCAMINVITE", strlen("WEBCAMINVITE"))) {
913                if (!strcmp(ind, " ")) {
914                        YAHOO_CALLBACK(ext_yahoo_webcam_invite) (yd->client_id,
915                                to, from);
916                } else {
917                        accept = atoi(ind);
918                        /* accept the invitation (-1 = deny 1 = accept) */
919                        if (accept < 0)
920                                accept = 0;
921                        YAHOO_CALLBACK(ext_yahoo_webcam_invite_reply) (yd->
922                                client_id, to, from, accept);
923                }
924        } else
925                LOG(("Got unknown notification: %s", msg));
926}
927
928static void yahoo_process_conference(struct yahoo_input_data *yid,
929        struct yahoo_packet *pkt)
930{
931        struct yahoo_data *yd = yid->yd;
932        char *msg = NULL;
933        char *host = NULL;
934        char *who = NULL;
935        char *room = NULL;
936        char *id = NULL;
937        int utf8 = 0;
938        YList *members = NULL;
939        YList *l;
940
941        for (l = pkt->hash; l; l = l->next) {
942                struct yahoo_pair *pair = l->data;
943                if (pair->key == 50)
944                        host = pair->value;
945
946                if (pair->key == 52) {  /* invite */
947                        members = y_list_append(members, strdup(pair->value));
948                }
949                if (pair->key == 53)    /* logon */
950                        who = pair->value;
951                if (pair->key == 54)    /* decline */
952                        who = pair->value;
953                if (pair->key == 55)    /* unavailable (status == 2) */
954                        who = pair->value;
955                if (pair->key == 56)    /* logoff */
956                        who = pair->value;
957
958                if (pair->key == 57)
959                        room = pair->value;
960
961                if (pair->key == 58)    /* join message */
962                        msg = pair->value;
963                if (pair->key == 14)    /* decline/conf message */
964                        msg = pair->value;
965
966                if (pair->key == 13) ;
967                if (pair->key == 16)    /* error */
968                        msg = pair->value;
969
970                if (pair->key == 1)     /* my id */
971                        id = pair->value;
972                if (pair->key == 3)     /* message sender */
973                        who = pair->value;
974
975                if (pair->key == 97)
976                        utf8 = atoi(pair->value);
977        }
978
979        if (!room)
980                return;
981
982        if (host) {
983                for (l = members; l; l = l->next) {
984                        char *w = l->data;
985                        if (!strcmp(w, host))
986                                break;
987                }
988                if (!l)
989                        members = y_list_append(members, strdup(host));
990        }
991        /* invite, decline, join, left, message -> status == 1 */
992
993        switch (pkt->service) {
994        case YAHOO_SERVICE_CONFINVITE:
995                if (pkt->status == 2) ;
996                else if (members)
997                        YAHOO_CALLBACK(ext_yahoo_got_conf_invite) (yd->
998                                client_id, id, host, room, msg, members);
999                else if (msg)
1000                        YAHOO_CALLBACK(ext_yahoo_error) (yd->client_id, msg, 0,
1001                                E_CONFNOTAVAIL);
1002                break;
1003        case YAHOO_SERVICE_CONFADDINVITE:
1004                if (pkt->status == 1)
1005                        YAHOO_CALLBACK(ext_yahoo_got_conf_invite) (yd->
1006                                client_id, id, host, room, msg, members);
1007                break;
1008        case YAHOO_SERVICE_CONFDECLINE:
1009                if (who)
1010                        YAHOO_CALLBACK(ext_yahoo_conf_userdecline) (yd->
1011                                client_id, id, who, room, msg);
1012                break;
1013        case YAHOO_SERVICE_CONFLOGON:
1014                if (who)
1015                        YAHOO_CALLBACK(ext_yahoo_conf_userjoin) (yd->client_id,
1016                                id, who, room);
1017                break;
1018        case YAHOO_SERVICE_CONFLOGOFF:
1019                if (who)
1020                        YAHOO_CALLBACK(ext_yahoo_conf_userleave) (yd->client_id,
1021                                id, who, room);
1022                break;
1023        case YAHOO_SERVICE_CONFMSG:
1024                if (who)
1025                        YAHOO_CALLBACK(ext_yahoo_conf_message) (yd->client_id,
1026                                id, who, room, msg, utf8);
1027                break;
1028        }
1029}
1030
1031static void yahoo_process_chat(struct yahoo_input_data *yid,
1032        struct yahoo_packet *pkt)
1033{
1034        char *msg = NULL;
1035        char *id = NULL;
1036        char *who = NULL;
1037        char *room = NULL;
1038        char *topic = NULL;
1039        YList *members = NULL;
1040        struct yahoo_chat_member *currentmember = NULL;
1041        int msgtype = 1;
1042        int utf8 = 0;
1043        int firstjoin = 0;
1044        int membercount = 0;
1045        int chaterr = 0;
1046        YList *l;
1047
1048        yahoo_dump_unhandled(pkt);
1049        for (l = pkt->hash; l; l = l->next) {
1050                struct yahoo_pair *pair = l->data;
1051
1052                if (pair->key == 1) {
1053                        /* My identity */
1054                        id = pair->value;
1055                }
1056
1057                if (pair->key == 104) {
1058                        /* Room name */
1059                        room = pair->value;
1060                }
1061
1062                if (pair->key == 105) {
1063                        /* Room topic */
1064                        topic = pair->value;
1065                }
1066
1067                if (pair->key == 108) {
1068                        /* Number of members in this packet */
1069                        membercount = atoi(pair->value);
1070                }
1071
1072                if (pair->key == 109) {
1073                        /* message sender */
1074                        who = pair->value;
1075
1076                        if (pkt->service == YAHOO_SERVICE_CHATJOIN) {
1077                                currentmember =
1078                                        y_new0(struct yahoo_chat_member, 1);
1079                                currentmember->id = strdup(pair->value);
1080                                members = y_list_append(members, currentmember);
1081                        }
1082                }
1083
1084                if (pair->key == 110) {
1085                        /* age */
1086                        if (pkt->service == YAHOO_SERVICE_CHATJOIN)
1087                                currentmember->age = atoi(pair->value);
1088                }
1089
1090                if (pair->key == 113) {
1091                        /* attribs */
1092                        if (pkt->service == YAHOO_SERVICE_CHATJOIN)
1093                                currentmember->attribs = atoi(pair->value);
1094                }
1095
1096                if (pair->key == 141) {
1097                        /* alias */
1098                        if (pkt->service == YAHOO_SERVICE_CHATJOIN)
1099                                currentmember->alias = strdup(pair->value);
1100                }
1101
1102                if (pair->key == 142) {
1103                        /* location */
1104                        if (pkt->service == YAHOO_SERVICE_CHATJOIN)
1105                                currentmember->location = strdup(pair->value);
1106                }
1107
1108                if (pair->key == 130) {
1109                        /* first join */
1110                        firstjoin = 1;
1111                }
1112
1113                if (pair->key == 117) {
1114                        /* message */
1115                        msg = pair->value;
1116                }
1117
1118                if (pair->key == 124) {
1119                        /* Message type */
1120                        msgtype = atoi(pair->value);
1121                }
1122                if (pair->key == 114) {
1123                        /* message error not sure what all the pair values mean */
1124                        /* but -1 means no session in room */
1125                        chaterr = atoi(pair->value);
1126                }
1127        }
1128
1129        if (!room) {
1130                if (pkt->service == YAHOO_SERVICE_CHATLOGOUT) { /* yahoo originated chat logout */
1131                        YAHOO_CALLBACK(ext_yahoo_chat_yahoologout) (yid->yd->
1132                                client_id, id);
1133                        return;
1134                }
1135                if (pkt->service == YAHOO_SERVICE_COMMENT && chaterr) {
1136                        YAHOO_CALLBACK(ext_yahoo_chat_yahooerror) (yid->yd->
1137                                client_id, id);
1138                        return;
1139                }
1140
1141                WARNING(("We didn't get a room name, ignoring packet"));
1142                return;
1143        }
1144
1145        switch (pkt->service) {
1146        case YAHOO_SERVICE_CHATJOIN:
1147                if (y_list_length(members) != membercount) {
1148                        WARNING(("Count of members doesn't match No. of members we got"));
1149                }
1150                if (firstjoin && members) {
1151                        YAHOO_CALLBACK(ext_yahoo_chat_join) (yid->yd->client_id,
1152                                id, room, topic, members, yid->fd);
1153                } else if (who) {
1154                        if (y_list_length(members) != 1) {
1155                                WARNING(("Got more than 1 member on a normal join"));
1156                        }
1157                        /* this should only ever have one, but just in case */
1158                        while (members) {
1159                                YList *n = members->next;
1160                                currentmember = members->data;
1161                                YAHOO_CALLBACK(ext_yahoo_chat_userjoin) (yid->
1162                                        yd->client_id, id, room, currentmember);
1163                                y_list_free_1(members);
1164                                members = n;
1165                        }
1166                }
1167                break;
1168        case YAHOO_SERVICE_CHATEXIT:
1169                if (who) {
1170                        YAHOO_CALLBACK(ext_yahoo_chat_userleave) (yid->yd->
1171                                client_id, id, room, who);
1172                }
1173                break;
1174        case YAHOO_SERVICE_COMMENT:
1175                if (who) {
1176                        YAHOO_CALLBACK(ext_yahoo_chat_message) (yid->yd->
1177                                client_id, id, who, room, msg, msgtype, utf8);
1178                }
1179                break;
1180        }
1181}
1182
1183static void yahoo_process_message(struct yahoo_input_data *yid,
1184        struct yahoo_packet *pkt)
1185{
1186        struct yahoo_data *yd = yid->yd;
1187        YList *l;
1188        YList *messages = NULL;
1189
1190        struct m {
1191                int i_31;
1192                int i_32;
1193                char *to;
1194                char *from;
1195                long tm;
1196                char *msg;
1197                int utf8;
1198                char *gunk;
1199        } *message = y_new0(struct m, 1);
1200
1201        for (l = pkt->hash; l; l = l->next) {
1202                struct yahoo_pair *pair = l->data;
1203                if (pair->key == 1 || pair->key == 4) {
1204                        if (!message->from)
1205                                message->from = pair->value;
1206                } else if (pair->key == 5)
1207                        message->to = pair->value;
1208                else if (pair->key == 15)
1209                        message->tm = strtol(pair->value, NULL, 10);
1210                else if (pair->key == 97)
1211                        message->utf8 = atoi(pair->value);
1212                /* This comes when the official client sends us a message */
1213                else if (pair->key == 429)
1214                        message->gunk = pair->value;
1215                /* user message *//* sys message */
1216                else if (pair->key == 14 || pair->key == 16)
1217                        message->msg = pair->value;
1218                else if (pair->key == 31) {
1219                        if (message->i_31) {
1220                                messages = y_list_append(messages, message);
1221                                message = y_new0(struct m, 1);
1222                        }
1223                        message->i_31 = atoi(pair->value);
1224                } else if (pair->key == 32)
1225                        message->i_32 = atoi(pair->value);
1226                else
1227                        LOG(("yahoo_process_message: status: %d, key: %d, value: %s", pkt->status, pair->key, pair->value));
1228        }
1229
1230        messages = y_list_append(messages, message);
1231
1232        for (l = messages; l; l = l->next) {
1233                message = l->data;
1234                if (pkt->service == YAHOO_SERVICE_SYSMESSAGE) {
1235                        YAHOO_CALLBACK(ext_yahoo_system_message) (yd->client_id,
1236                                message->to, message->from, message->msg);
1237                } else if (pkt->status <= 2 || pkt->status == 5) {
1238                        /* Confirm message receipt if we got the gunk */
1239                        if(message->gunk) {
1240                                struct yahoo_packet *outpkt;
1241                       
1242                                outpkt = yahoo_packet_new(YAHOO_SERVICE_MESSAGE_CONFIRM,
1243                                        YPACKET_STATUS_DEFAULT, 0);
1244                                yahoo_packet_hash(outpkt, 1, yd->user);
1245                                yahoo_packet_hash(outpkt, 5, message->from);
1246                                yahoo_packet_hash(outpkt, 302, "430");
1247                                yahoo_packet_hash(outpkt, 430, message->gunk);
1248                                yahoo_packet_hash(outpkt, 303, "430");
1249                                yahoo_packet_hash(outpkt, 450, "0");
1250                                yahoo_send_packet(yid, outpkt, 0);
1251                       
1252                                yahoo_packet_free(outpkt);
1253                        }
1254
1255                        if (!strcmp(message->msg, "<ding>"))
1256                                YAHOO_CALLBACK(ext_yahoo_got_buzz) (yd->client_id,
1257                                        message->to, message->from, message->tm);
1258                        else
1259                                YAHOO_CALLBACK(ext_yahoo_got_im) (yd->client_id,
1260                                        message->to, message->from, message->msg,
1261                                        message->tm, pkt->status, message->utf8);
1262                } else if (pkt->status == 0xffffffff) {
1263                        YAHOO_CALLBACK(ext_yahoo_error) (yd->client_id,
1264                                message->msg, 0, E_SYSTEM);
1265                }
1266                FREE(message);
1267        }
1268
1269        y_list_free(messages);
1270}
1271
1272/*
1273 * Here's what multi-level packets look like. Data in brackets is the value.
1274 *
1275 * 3 level:
1276 * =======
1277 *
1278 * 302 (318) - Beginning level 1
1279 *      300 (318) - Begin level 2
1280 *      302 (319) - End level 2 header
1281 *              300 (319) - Begin level 3
1282 *              301 (319) - End level 3
1283 *      303 (319) - End level 2
1284 * 303 (318) - End level 1
1285 *
1286 * 2 level:
1287 * =======
1288 *
1289 * 302 (315) - Beginning level 1
1290 *      300 (315) - Begin level 2
1291 *      301 (315) - End level 2
1292 * 303 (315) - End level 1
1293 *
1294 */
1295static void yahoo_process_status(struct yahoo_input_data *yid,
1296        struct yahoo_packet *pkt)
1297{
1298        YList *l;
1299        struct yahoo_data *yd = yid->yd;
1300
1301        struct yahoo_process_status_entry *u;
1302
1303        YList *users = 0;
1304
1305        if (pkt->service == YAHOO_SERVICE_LOGOFF && pkt->status == -1) {
1306                YAHOO_CALLBACK(ext_yahoo_login_response) (yd->client_id,
1307                        YAHOO_LOGIN_DUPL, NULL);
1308                return;
1309        }
1310
1311        /*
1312         * Status updates may be spread accross multiple packets and not
1313         * even on buddy boundaries, so keeping some state is important.
1314         * So, continue where we left off, and only add a user entry to
1315         * the list once it's complete (301-315 End buddy).
1316         */
1317        u = yd->half_user;
1318
1319        for (l = pkt->hash; l; l = l->next) {
1320                struct yahoo_pair *pair = l->data;
1321
1322                switch (pair->key) {
1323                case 300:       /* Begin buddy */
1324                        if (!strcmp(pair->value, "315") && !u) {
1325                                u = yd->half_user = y_new0(struct yahoo_process_status_entry, 1);
1326                        }
1327                        break;
1328                case 301:       /* End buddy */
1329                        if (!strcmp(pair->value, "315") && u) {
1330                                /* Sometimes user info comes in an odd format with no
1331                                   "begin buddy" but *with* an "end buddy". Don't add
1332                                   it twice. */
1333                                if (!y_list_find(users, u))
1334                                        users = y_list_prepend(users, u);
1335                                u = yd->half_user = NULL;
1336                        }
1337                        break;
1338                case 0: /* we won't actually do anything with this */
1339                        NOTICE(("key %d:%s", pair->key, pair->value));
1340                        break;
1341                case 1: /* we don't get the full buddy list here. */
1342                        if (!yd->logged_in) {
1343                                yd->logged_in = 1;
1344                                if (yd->current_status < 0)
1345                                        yd->current_status = yd->initial_status;
1346                                YAHOO_CALLBACK(ext_yahoo_login_response) (yd->
1347                                        client_id, YAHOO_LOGIN_OK, NULL);
1348                        }
1349                        break;
1350                case 8: /* how many online buddies we have */
1351                        NOTICE(("key %d:%s", pair->key, pair->value));
1352                        break;
1353                case 7: /* the current buddy */
1354                        if (!u) {
1355                                /* This will only happen in case of a single level message */
1356                                u = y_new0(struct yahoo_process_status_entry, 1);
1357                                users = y_list_prepend(users, u);
1358                        }
1359                        u->name = pair->value;
1360                        break;
1361                case 10:        /* state */
1362                        u->state = strtol(pair->value, NULL, 10);
1363                        break;
1364                case 19:        /* custom status message */
1365                        u->msg = pair->value;
1366                        break;
1367                case 47:        /* is it an away message or not. Not applicable for YMSG16 anymore */
1368                        u->away = atoi(pair->value);
1369                        break;
1370                case 137:       /* seconds idle */
1371                        u->idle = atoi(pair->value);
1372                        break;
1373                case 11:        /* this is the buddy's session id */
1374                        u->buddy_session = atoi(pair->value);
1375                        break;
1376                case 17:        /* in chat? */
1377                        u->f17 = atoi(pair->value);
1378                        break;
1379                case 13:        /* bitmask, bit 0 = pager, bit 1 = chat, bit 2 = game */
1380                        u->flags = atoi(pair->value);
1381                        break;
1382                case 60:        /* SMS -> 1 MOBILE USER */
1383                        /* sometimes going offline makes this 2, but invisible never sends it */
1384                        u->mobile = atoi(pair->value);
1385                        break;
1386                case 138:
1387                        u->f138 = atoi(pair->value);
1388                        break;
1389                case 184:
1390                        u->f184 = pair->value;
1391                        break;
1392                case 192:
1393                        u->f192 = atoi(pair->value);
1394                        break;
1395                case 10001:
1396                        u->f10001 = atoi(pair->value);
1397                        break;
1398                case 10002:
1399                        u->f10002 = atoi(pair->value);
1400                        break;
1401                case 198:
1402                        u->f198 = atoi(pair->value);
1403                        break;
1404                case 197:
1405                        u->f197 = pair->value;
1406                        break;
1407                case 205:
1408                        u->f205 = pair->value;
1409                        break;
1410                case 213:
1411                        u->f213 = atoi(pair->value);
1412                        break;
1413                case 16:        /* Custom error message */
1414                        YAHOO_CALLBACK(ext_yahoo_error) (yd->client_id,
1415                                pair->value, 0, E_CUSTOM);
1416                        break;
1417                default:
1418                        WARNING(("unknown status key %d:%s", pair->key,
1419                                        pair->value));
1420                        break;
1421                }
1422        }
1423
1424        while (users) {
1425                YList *t = users;
1426                struct yahoo_process_status_entry *u = users->data;
1427
1428                if (u->name != NULL) {
1429                        if (pkt->service ==
1430                                YAHOO_SERVICE_LOGOFF
1431                                /*|| u->flags == 0 No flags for YMSG16 */ ) {
1432                                YAHOO_CALLBACK(ext_yahoo_status_changed) (yd->
1433                                        client_id, u->name,
1434                                        YAHOO_STATUS_OFFLINE, NULL, 1, 0, 0);
1435                        } else {
1436                                /* Key 47 always seems to be 1 for YMSG16 */
1437                                if (!u->state)
1438                                        u->away = 0;
1439                                else
1440                                        u->away = 1;
1441
1442                                YAHOO_CALLBACK(ext_yahoo_status_changed) (yd->
1443                                        client_id, u->name, u->state, u->msg,
1444                                        u->away, u->idle, u->mobile);
1445                        }
1446                }
1447
1448                users = y_list_remove_link(users, users);
1449                y_list_free_1(t);
1450                FREE(u);
1451        }
1452}
1453
1454static void yahoo_process_buddy_list(struct yahoo_input_data *yid,
1455        struct yahoo_packet *pkt)
1456{
1457        struct yahoo_data *yd = yid->yd;
1458        YList *l;
1459        int last_packet = 0;
1460        char *cur_group = NULL;
1461        struct yahoo_buddy *newbud = NULL;
1462
1463        /* we could be getting multiple packets here */
1464        for (l = pkt->hash; l; l = l->next) {
1465                struct yahoo_pair *pair = l->data;
1466
1467                switch (pair->key) {
1468                case 300:
1469                case 301:
1470                case 302:
1471                        break;  /* Separators. Our logic does not need them */
1472                case 303:
1473                        if (318 == atoi(pair->value))
1474                                last_packet = 1;
1475                        break;
1476                case 65:
1477                        cur_group = strdup(pair->value);
1478                        break;
1479                case 7:
1480                        newbud = y_new0(struct yahoo_buddy, 1);
1481                        newbud->id = strdup(pair->value);
1482                        if (cur_group)
1483                                newbud->group = strdup(cur_group);
1484                        else if (yd->buddies) {
1485                                struct yahoo_buddy *lastbud =
1486                                        (struct yahoo_buddy *)y_list_nth(yd->
1487                                        buddies,
1488                                        y_list_length(yd->buddies) - 1)->data;
1489                                newbud->group = strdup(lastbud->group);
1490                        } else
1491                                newbud->group = strdup("Buddies");
1492
1493                        yd->buddies = y_list_append(yd->buddies, newbud);
1494
1495                        break;
1496                }
1497        }
1498
1499        /* we could be getting multiple packets here */
1500        if (pkt->hash && !last_packet)
1501                return;
1502
1503        YAHOO_CALLBACK(ext_yahoo_got_buddies) (yd->client_id, yd->buddies);
1504
1505        /* Logged in */
1506        if (!yd->logged_in) {
1507                yd->logged_in = 1;
1508                if (yd->current_status < 0)
1509                        yd->current_status = yd->initial_status;
1510                YAHOO_CALLBACK(ext_yahoo_login_response) (yd->client_id,
1511                        YAHOO_LOGIN_OK, NULL);
1512
1513                /*
1514                yahoo_set_away(yd->client_id, yd->initial_status, NULL,
1515                        (yd->initial_status == YAHOO_STATUS_AVAILABLE) ? 0 : 1);
1516
1517                yahoo_get_yab(yd->client_id);
1518                */
1519        }
1520
1521}
1522
1523static void yahoo_process_list(struct yahoo_input_data *yid,
1524        struct yahoo_packet *pkt)
1525{
1526        struct yahoo_data *yd = yid->yd;
1527        YList *l;
1528
1529        /* we could be getting multiple packets here */
1530        for (l = pkt->hash; l; l = l->next) {
1531                struct yahoo_pair *pair = l->data;
1532
1533                switch (pair->key) {
1534                case 89:        /* identities */
1535                        {
1536                                char **identities =
1537                                        y_strsplit(pair->value, ",", -1);
1538                                int i;
1539                                for (i = 0; identities[i]; i++)
1540                                        yd->identities =
1541                                                y_list_append(yd->identities,
1542                                                strdup(identities[i]));
1543                                y_strfreev(identities);
1544                        }
1545                        YAHOO_CALLBACK(ext_yahoo_got_identities) (yd->client_id,
1546                                yd->identities);
1547                        break;
1548                case 59:        /* cookies */
1549                        if (pair->value[0] == 'Y') {
1550                                FREE(yd->cookie_y);
1551                                FREE(yd->login_cookie);
1552
1553                                yd->cookie_y = getcookie(pair->value);
1554                                yd->login_cookie = getlcookie(yd->cookie_y);
1555
1556                        } else if (pair->value[0] == 'T') {
1557                                FREE(yd->cookie_t);
1558                                yd->cookie_t = getcookie(pair->value);
1559
1560                        } else if (pair->value[0] == 'C') {
1561                                FREE(yd->cookie_c);
1562                                yd->cookie_c = getcookie(pair->value);
1563                        }
1564
1565                        break;
1566                case 3: /* my id */
1567                case 90:        /* 1 */
1568                case 100:       /* 0 */
1569                case 101:       /* NULL */
1570                case 102:       /* NULL */
1571                case 93:        /* 86400/1440 */
1572                        break;
1573                }
1574        }
1575
1576        if (yd->cookie_y && yd->cookie_t)       /* We don't get cookie_c anymore */
1577                YAHOO_CALLBACK(ext_yahoo_got_cookies) (yd->client_id);
1578}
1579
1580static void yahoo_process_verify(struct yahoo_input_data *yid,
1581        struct yahoo_packet *pkt)
1582{
1583        struct yahoo_data *yd = yid->yd;
1584
1585        if (pkt->status != 0x01) {
1586                DEBUG_MSG(("expected status: 0x01, got: %d", pkt->status));
1587                YAHOO_CALLBACK(ext_yahoo_login_response) (yd->client_id,
1588                        YAHOO_LOGIN_LOCK, "");
1589                return;
1590        }
1591
1592        pkt = yahoo_packet_new(YAHOO_SERVICE_AUTH, YPACKET_STATUS_DEFAULT,
1593                yd->session_id);
1594
1595        yahoo_packet_hash(pkt, 1, yd->user);
1596        yahoo_send_packet(yid, pkt, 0);
1597
1598        yahoo_packet_free(pkt);
1599
1600}
1601
1602static void yahoo_process_picture_checksum(struct yahoo_input_data *yid,
1603        struct yahoo_packet *pkt)
1604{
1605        struct yahoo_data *yd = yid->yd;
1606        char *from = NULL;
1607        char *to = NULL;
1608        int checksum = 0;
1609        YList *l;
1610
1611        for (l = pkt->hash; l; l = l->next) {
1612                struct yahoo_pair *pair = l->data;
1613
1614                switch (pair->key) {
1615                case 1:
1616                case 4:
1617                        from = pair->value;
1618                case 5:
1619                        to = pair->value;
1620                        break;
1621                case 212:
1622                        break;
1623                case 192:
1624                        checksum = atoi(pair->value);
1625                        break;
1626                }
1627        }
1628
1629        YAHOO_CALLBACK(ext_yahoo_got_buddyicon_checksum) (yd->client_id, to,
1630                from, checksum);
1631}
1632
1633static void yahoo_process_picture(struct yahoo_input_data *yid,
1634        struct yahoo_packet *pkt)
1635{
1636        struct yahoo_data *yd = yid->yd;
1637        char *url = NULL;
1638        char *from = NULL;
1639        char *to = NULL;
1640        int status = 0;
1641        int checksum = 0;
1642        YList *l;
1643
1644        for (l = pkt->hash; l; l = l->next) {
1645                struct yahoo_pair *pair = l->data;
1646
1647                switch (pair->key) {
1648                case 1:
1649                case 4: /* sender */
1650                        from = pair->value;
1651                        break;
1652                case 5: /* we */
1653                        to = pair->value;
1654                        break;
1655                case 13:        /* request / sending */
1656                        status = atoi(pair->value);
1657                        break;
1658                case 20:        /* url */
1659                        url = pair->value;
1660                        break;
1661                case 192:       /*checksum */
1662                        checksum = atoi(pair->value);
1663                        break;
1664                }
1665        }
1666
1667        switch (status) {
1668        case 1:         /* this is a request, ignore for now */
1669                YAHOO_CALLBACK(ext_yahoo_got_buddyicon_request) (yd->client_id,
1670                        to, from);
1671                break;
1672        case 2:         /* this is cool - we get a picture :) */
1673                YAHOO_CALLBACK(ext_yahoo_got_buddyicon) (yd->client_id, to,
1674                        from, url, checksum);
1675                break;
1676        }
1677}
1678
1679static void yahoo_process_picture_upload(struct yahoo_input_data *yid,
1680        struct yahoo_packet *pkt)
1681{
1682        struct yahoo_data *yd = yid->yd;
1683        YList *l;
1684        char *url = NULL;
1685
1686        if (pkt->status != 1)
1687                return;         /* something went wrong */
1688
1689        for (l = pkt->hash; l; l = l->next) {
1690                struct yahoo_pair *pair = l->data;
1691
1692                switch (pair->key) {
1693                case 5: /* we */
1694                        break;
1695                case 20:        /* url */
1696                        url = pair->value;
1697                        break;
1698                case 27:        /* local filename */
1699                        break;
1700                case 38:        /* time */
1701                        break;
1702                }
1703        }
1704
1705        YAHOO_CALLBACK(ext_yahoo_buddyicon_uploaded) (yd->client_id, url);
1706}
1707
1708void yahoo_login(int id, int initial)
1709{
1710        struct yahoo_data *yd = find_conn_by_id(id);
1711        struct connect_callback_data *ccd;
1712        struct yahoo_server_settings *yss;
1713        int tag;
1714
1715        char *host;
1716
1717        struct yahoo_input_data *yid = y_new0(struct yahoo_input_data, 1);
1718        yid->yd = yd;
1719        yid->type = YAHOO_CONNECTION_PAGER;
1720        inputs = y_list_prepend(inputs, yid);
1721
1722        yd->initial_status = initial;
1723        yss = yd->server_settings;
1724
1725        ccd = y_new0(struct connect_callback_data, 1);
1726        ccd->yd = yd;
1727
1728        host = yss->pager_host;
1729
1730        if (!host)
1731                host = yss->pager_host_list[0];
1732
1733        tag = YAHOO_CALLBACK(ext_yahoo_connect_async) (yd->client_id,
1734                host, yss->pager_port, yahoo_connected, ccd, 0);
1735
1736        /*
1737         * if tag <= 0, then callback has already been called
1738         * so ccd will have been freed
1739         */
1740        if (tag > 0)
1741                ccd->tag = tag;
1742        else if (tag < 0)
1743                YAHOO_CALLBACK(ext_yahoo_login_response) (yd->client_id,
1744                        YAHOO_LOGIN_SOCK, NULL);
1745}
1746
1747struct yahoo_https_auth_data
1748{
1749        struct yahoo_input_data *yid;
1750        char *token;
1751        char *chal;
1752};
1753
1754static void yahoo_https_auth_token_init(struct yahoo_https_auth_data *had);
1755static void yahoo_https_auth_token_finish(struct http_request *req);
1756static void yahoo_https_auth_init(struct yahoo_https_auth_data *had);
1757static void yahoo_https_auth_finish(struct http_request *req);
1758
1759/* Extract a value from a login.yahoo.com response. Assume CRLF-linebreaks
1760   and FAIL miserably if they're not there... */
1761static char *yahoo_ha_find_key(char *response, char *key)
1762{
1763        char *s, *end;
1764        int len = strlen(key);
1765       
1766        s = response;
1767        do {
1768                if (strncmp(s, key, len) == 0 && s[len] == '=') {
1769                        s += len + 1;
1770                        if ((end = strchr(s, '\r')))
1771                                return g_strndup(s, end - s);
1772                        else
1773                                return g_strdup(s);
1774                }
1775               
1776                if ((s = strchr(s, '\n')))
1777                        s ++;
1778        } while (s && *s);
1779       
1780        return NULL;
1781}
1782
1783static enum yahoo_status yahoo_https_status_parse(int code)
1784{
1785        switch (code)
1786        {
1787                case 1212: return YAHOO_LOGIN_PASSWD;
1788                case 1213: return YAHOO_LOGIN_LOCK;
1789                case 1235: return YAHOO_LOGIN_UNAME;
1790                default: return (enum yahoo_status) code;
1791        }
1792}
1793
1794static void yahoo_https_auth(struct yahoo_input_data *yid, const char *seed, const char *sn)
1795{
1796        struct yahoo_https_auth_data *had = g_new0(struct yahoo_https_auth_data, 1);
1797       
1798        had->yid = yid;
1799        had->chal = g_strdup(seed);
1800       
1801        yahoo_https_auth_token_init(had);
1802}
1803
1804static void yahoo_https_auth_token_init(struct yahoo_https_auth_data *had)
1805{
1806        struct yahoo_input_data *yid = had->yid;
1807        struct yahoo_data *yd = yid->yd;
1808        char *login, *passwd, *chal;
1809        char *url;
1810       
1811        login = g_strndup(yd->user, 3 * strlen(yd->user));
1812        http_encode(login);
1813        passwd = g_strndup(yd->password, 3 * strlen(yd->password));
1814        http_encode(passwd);
1815        chal = g_strndup(had->chal, 3 * strlen(had->chal));
1816        http_encode(chal);
1817       
1818        url = g_strdup_printf("https://login.yahoo.com/config/pwtoken_get?src=ymsgr&ts=%d&login=%s&passwd=%s&chal=%s",
1819                               (int) time(NULL), login, passwd, chal);
1820       
1821        http_dorequest_url(url, yahoo_https_auth_token_finish, had);
1822       
1823        g_free(url);
1824        g_free(chal);
1825        g_free(passwd);
1826        g_free(login);
1827}
1828
1829static void yahoo_https_auth_token_finish(struct http_request *req)
1830{
1831        struct yahoo_https_auth_data *had = req->data;
1832        struct yahoo_input_data *yid;
1833        struct yahoo_data *yd;
1834        int st;
1835       
1836        if (y_list_find(inputs, had->yid) == NULL)
1837                return;
1838       
1839        yid = had->yid;
1840        yd = yid->yd;
1841       
1842        if (req->status_code != 200) {
1843                YAHOO_CALLBACK(ext_yahoo_login_response) (yd->client_id, 2000 + req->status_code, NULL);
1844                goto fail;
1845        }
1846       
1847        if (sscanf(req->reply_body, "%d", &st) != 1 || st != 0) {
1848                YAHOO_CALLBACK(ext_yahoo_login_response) (yd->client_id, yahoo_https_status_parse(st), NULL);
1849                goto fail;
1850        }
1851       
1852        if ((had->token = yahoo_ha_find_key(req->reply_body, "ymsgr")) == NULL) {
1853                YAHOO_CALLBACK(ext_yahoo_login_response) (yd->client_id, 3001, NULL);
1854                goto fail;
1855        }
1856       
1857        yahoo_https_auth_init(had);
1858        return;
1859       
1860fail:
1861        g_free(had->token);
1862        g_free(had->chal);
1863        g_free(had);
1864}
1865
1866static void yahoo_https_auth_init(struct yahoo_https_auth_data *had)
1867{
1868        char *url;
1869       
1870        url = g_strdup_printf("https://login.yahoo.com/config/pwtoken_login?src=ymsgr&ts=%d&token=%s",
1871                              (int) time(NULL), had->token);
1872       
1873        http_dorequest_url(url, yahoo_https_auth_finish, had);
1874       
1875        g_free(url);
1876}
1877
1878static void yahoo_https_auth_finish(struct http_request *req)
1879{
1880        struct yahoo_https_auth_data *had = req->data;
1881        struct yahoo_input_data *yid;
1882        struct yahoo_data *yd;
1883        struct yahoo_packet *pack;
1884        char *crumb = NULL;
1885        int st;
1886       
1887        if (y_list_find(inputs, had->yid) == NULL)
1888                return;
1889       
1890        yid = had->yid;
1891        yd = yid->yd;
1892       
1893        md5_byte_t result[16];
1894        md5_state_t ctx;
1895       
1896        unsigned char yhash[32];
1897
1898        if (req->status_code != 200) {
1899                YAHOO_CALLBACK(ext_yahoo_login_response) (yd->client_id, 2000 + req->status_code, NULL);
1900                goto fail;
1901        }
1902       
1903        if (sscanf(req->reply_body, "%d", &st) != 1 || st != 0) {
1904                YAHOO_CALLBACK(ext_yahoo_login_response) (yd->client_id, yahoo_https_status_parse(st), NULL);
1905                goto fail;
1906        }
1907       
1908        if ((yd->cookie_y = yahoo_ha_find_key(req->reply_body, "Y")) == NULL ||
1909            (yd->cookie_t = yahoo_ha_find_key(req->reply_body, "T")) == NULL ||
1910            (crumb = yahoo_ha_find_key(req->reply_body, "crumb")) == NULL) {
1911                YAHOO_CALLBACK(ext_yahoo_login_response) (yd->client_id, 3002, NULL);
1912                goto fail;
1913        }
1914       
1915        md5_init(&ctx); 
1916        md5_append(&ctx, (unsigned char*) crumb, 11);
1917        md5_append(&ctx, (unsigned char*) had->chal, strlen(had->chal));
1918        md5_finish(&ctx, result);
1919        to_y64(yhash, result, 16);
1920
1921        pack = yahoo_packet_new(YAHOO_SERVICE_AUTHRESP, yd->initial_status, yd->session_id);
1922        yahoo_packet_hash(pack, 1, yd->user);
1923        yahoo_packet_hash(pack, 0, yd->user);
1924        yahoo_packet_hash(pack, 277, yd->cookie_y);
1925        yahoo_packet_hash(pack, 278, yd->cookie_t);
1926        yahoo_packet_hash(pack, 307, (char*) yhash);
1927        yahoo_packet_hash(pack, 244, "524223");
1928        yahoo_packet_hash(pack, 2, yd->user);
1929        yahoo_packet_hash(pack, 2, "1");
1930        yahoo_packet_hash(pack, 98, "us");
1931        yahoo_packet_hash(pack, 135, "7.5.0.647");
1932       
1933        yahoo_send_packet(yid, pack, 0);
1934               
1935        yahoo_packet_free(pack);
1936       
1937fail:
1938        g_free(crumb);
1939        g_free(had->token);
1940        g_free(had->chal);
1941        g_free(had);
1942}
1943
1944static void yahoo_process_auth(struct yahoo_input_data *yid,
1945        struct yahoo_packet *pkt)
1946{
1947        char *seed = NULL;
1948        char *sn = NULL;
1949        YList *l = pkt->hash;
1950        int m = 0;
1951        struct yahoo_data *yd = yid->yd;
1952
1953        while (l) {
1954                struct yahoo_pair *pair = l->data;
1955
1956                switch (pair->key) {
1957                case 94:
1958                        seed = pair->value;
1959                        break;
1960                case 1:
1961                        sn = pair->value;
1962                        break;
1963                case 13:
1964                        m = atoi(pair->value);
1965                        break;
1966                }
1967                l = l->next;
1968        }
1969
1970        if (!seed)
1971                return;
1972
1973        if (m==2)
1974                yahoo_https_auth(yid, seed, sn);
1975        else {
1976                /* call error */
1977                WARNING(("unknown auth type %d", m));
1978                YAHOO_CALLBACK(ext_yahoo_login_response) (yd->client_id,
1979                        YAHOO_LOGIN_UNKNOWN, NULL);
1980        }
1981}
1982
1983static void yahoo_process_auth_resp(struct yahoo_input_data *yid,
1984        struct yahoo_packet *pkt)
1985{
1986        struct yahoo_data *yd = yid->yd;
1987        char *url = NULL;
1988        int login_status = -1;
1989
1990        YList *l;
1991
1992        for (l = pkt->hash; l; l = l->next) {
1993                struct yahoo_pair *pair = l->data;
1994                if (pair->key == 0)
1995                        ; /* login_id */
1996                else if (pair->key == 1)
1997                        ; /* handle */
1998                else if (pair->key == 20)
1999                        url = pair->value;
2000                else if (pair->key == 66)
2001                        login_status = atoi(pair->value);
2002        }
2003
2004        if (pkt->status == YPACKET_STATUS_DISCONNECTED) {
2005                YAHOO_CALLBACK(ext_yahoo_login_response) (yd->client_id,
2006                        login_status, url);
2007                /*      yahoo_logoff(yd->client_id); */
2008        }
2009}
2010
2011static void yahoo_process_mail(struct yahoo_input_data *yid,
2012        struct yahoo_packet *pkt)
2013{
2014        struct yahoo_data *yd = yid->yd;
2015        char *who = NULL;
2016        char *email = NULL;
2017        char *subj = NULL;
2018        int count = 0;
2019        YList *l;
2020
2021        for (l = pkt->hash; l; l = l->next) {
2022                struct yahoo_pair *pair = l->data;
2023                if (pair->key == 9)
2024                        count = strtol(pair->value, NULL, 10);
2025                else if (pair->key == 43)
2026                        who = pair->value;
2027                else if (pair->key == 42)
2028                        email = pair->value;
2029                else if (pair->key == 18)
2030                        subj = pair->value;
2031                else
2032                        LOG(("key: %d => value: %s", pair->key, pair->value));
2033        }
2034
2035        if (who && email && subj) {
2036                char from[1024];
2037                snprintf(from, sizeof(from), "%s (%s)", who, email);
2038                YAHOO_CALLBACK(ext_yahoo_mail_notify) (yd->client_id, from,
2039                        subj, count);
2040        } else if (count > 0)
2041                YAHOO_CALLBACK(ext_yahoo_mail_notify) (yd->client_id, NULL,
2042                        NULL, count);
2043}
2044
2045static void yahoo_process_new_contact(struct yahoo_input_data *yid,
2046        struct yahoo_packet *pkt)
2047{
2048        struct yahoo_data *yd = yid->yd;
2049        char *me = NULL;
2050        char *who = NULL;
2051        char *msg = NULL;
2052        int online = -1;
2053
2054        YList *l;
2055
2056        for (l = pkt->hash; l; l = l->next) {
2057                struct yahoo_pair *pair = l->data;
2058                if (pair->key == 4)
2059                        who = pair->value;
2060                else if (pair->key == 5)
2061                        me = pair->value;
2062                else if (pair->key == 14)
2063                        msg = pair->value;
2064                else if (pair->key == 13)
2065                        online = strtol(pair->value, NULL, 10);
2066        }
2067
2068        if (who && online < 0)
2069                YAHOO_CALLBACK(ext_yahoo_contact_added) (yd->client_id, me, who,
2070                        msg);
2071        else if (online == 2)
2072                YAHOO_CALLBACK(ext_yahoo_rejected) (yd->client_id, who, msg);
2073}
2074
2075/* UNUSED? */
2076static void yahoo_process_contact(struct yahoo_input_data *yid,
2077        struct yahoo_packet *pkt)
2078{
2079        struct yahoo_data *yd = yid->yd;
2080        char *id = NULL;
2081        char *who = NULL;
2082        char *msg = NULL;
2083        char *name = NULL;
2084        int state = YAHOO_STATUS_AVAILABLE;
2085        int away = 0;
2086        int idle = 0;
2087        int mobile = 0;
2088
2089        YList *l;
2090
2091        for (l = pkt->hash; l; l = l->next) {
2092                struct yahoo_pair *pair = l->data;
2093                if (pair->key == 1)
2094                        id = pair->value;
2095                else if (pair->key == 3)
2096                        who = pair->value;
2097                else if (pair->key == 14)
2098                        msg = pair->value;
2099                else if (pair->key == 7)
2100                        name = pair->value;
2101                else if (pair->key == 10)
2102                        state = strtol(pair->value, NULL, 10);
2103                else if (pair->key == 15)
2104                        ; /* tm */
2105                else if (pair->key == 13)
2106                        ; /* online */
2107                else if (pair->key == 47)
2108                        away = strtol(pair->value, NULL, 10);
2109                else if (pair->key == 137)
2110                        idle = strtol(pair->value, NULL, 10);
2111                else if (pair->key == 60)
2112                        mobile = strtol(pair->value, NULL, 10);
2113
2114        }
2115
2116        if (id)
2117                YAHOO_CALLBACK(ext_yahoo_contact_added) (yd->client_id, id, who,
2118                        msg);
2119        else if (name)
2120                YAHOO_CALLBACK(ext_yahoo_status_changed) (yd->client_id, name,
2121                        state, msg, away, idle, mobile);
2122        else if (pkt->status == 0x07)
2123                YAHOO_CALLBACK(ext_yahoo_rejected) (yd->client_id, who, msg);
2124}
2125
2126static void yahoo_process_buddyadd(struct yahoo_input_data *yid,
2127        struct yahoo_packet *pkt)
2128{
2129        struct yahoo_data *yd = yid->yd;
2130        char *who = NULL;
2131        char *where = NULL;
2132        int status = 0;
2133
2134        struct yahoo_buddy *bud = NULL;
2135
2136        YList *l;
2137        for (l = pkt->hash; l; l = l->next) {
2138                struct yahoo_pair *pair = l->data;
2139                if (pair->key == 1)
2140                        ; /* Me... don't care */
2141                if (pair->key == 7)
2142                        who = pair->value;
2143                if (pair->key == 65)
2144                        where = pair->value;
2145                if (pair->key == 66)
2146                        status = strtol(pair->value, NULL, 10);
2147        }
2148
2149        if (!who)
2150                return;
2151        if (!where)
2152                where = "Unknown";
2153
2154        bud = y_new0(struct yahoo_buddy, 1);
2155        bud->id = strdup(who);
2156        bud->group = strdup(where);
2157        bud->real_name = NULL;
2158
2159        yd->buddies = y_list_append(yd->buddies, bud);
2160
2161#if 0
2162        /* BitlBee: This seems to be wrong in my experience. I think:
2163           status = 0: Success
2164           status = 2: Already on list
2165           status = 3: Doesn't exist
2166           status = 42: Invalid handle (possibly banned/reserved, I get it for
2167                        handles like joe or jjjjjj)
2168           Haven't seen others yet. But whenever the add is successful, there
2169           will be a separate "went online" packet when the auth. request is
2170           accepted. Couldn't find any test account that doesn't require auth.
2171           unfortunately (if there is even such a thing?) */
2172           
2173        /* A non-zero status (i've seen 2) seems to mean the buddy is already
2174         * added and is online */
2175        if (status) {
2176                LOG(("Setting online see packet for info"));
2177                yahoo_dump_unhandled(pkt);
2178                YAHOO_CALLBACK(ext_yahoo_status_changed) (yd->client_id, who,
2179                        YAHOO_STATUS_AVAILABLE, NULL, 0, 0, 0);
2180        }
2181#endif
2182        /* BitlBee: Need ACK of added buddy, if it was successful. */
2183        if (status == 0) {
2184                YList *tmp = y_list_append(NULL, bud);
2185                YAHOO_CALLBACK(ext_yahoo_got_buddies) (yd->client_id, tmp);
2186                y_list_free(tmp);
2187        }
2188}
2189
2190static void yahoo_process_buddydel(struct yahoo_input_data *yid,
2191        struct yahoo_packet *pkt)
2192{
2193        struct yahoo_data *yd = yid->yd;
2194        char *who = NULL;
2195        char *where = NULL;
2196        struct yahoo_buddy *bud;
2197
2198        YList *buddy;
2199
2200        YList *l;
2201        for (l = pkt->hash; l; l = l->next) {
2202                struct yahoo_pair *pair = l->data;
2203                if (pair->key == 1)
2204                        ; /* Me... don't care */
2205                else if (pair->key == 7)
2206                        who = pair->value;
2207                else if (pair->key == 65)
2208                        where = pair->value;
2209                else if (pair->key == 66)
2210                        ; /* unk_66 */
2211                else
2212                        DEBUG_MSG(("unknown key: %d = %s", pair->key,
2213                                        pair->value));
2214        }
2215
2216        if (!who || !where)
2217                return;
2218
2219        bud = y_new0(struct yahoo_buddy, 1);
2220        bud->id = strdup(who);
2221        bud->group = strdup(where);
2222
2223        buddy = y_list_find_custom(yd->buddies, bud, is_same_bud);
2224
2225        FREE(bud->id);
2226        FREE(bud->group);
2227        FREE(bud);
2228
2229        if (buddy) {
2230                bud = buddy->data;
2231                yd->buddies = y_list_remove_link(yd->buddies, buddy);
2232                y_list_free_1(buddy);
2233
2234                FREE(bud->id);
2235                FREE(bud->group);
2236                FREE(bud->real_name);
2237                FREE(bud);
2238
2239                bud = NULL;
2240        }
2241}
2242
2243static void yahoo_process_ignore(struct yahoo_input_data *yid,
2244        struct yahoo_packet *pkt)
2245{
2246        YList *l;
2247        for (l = pkt->hash; l; l = l->next) {
2248                struct yahoo_pair *pair = l->data;
2249                if (pair->key == 0)
2250                        ; /* who */
2251                if (pair->key == 1)
2252                        ; /* Me... don't care */
2253                if (pair->key == 13)    /* 1 == ignore, 2 == unignore */
2254                        ;
2255                if (pair->key == 66)
2256                        ; /* status */
2257        }
2258
2259        /*
2260         * status
2261         *      0  - ok
2262         *      2  - already in ignore list, could not add
2263         *      3  - not in ignore list, could not delete
2264         *      12 - is a buddy, could not add
2265         */
2266
2267/*      if(status)
2268                YAHOO_CALLBACK(ext_yahoo_error)(yd->client_id, who, 0, status);
2269*/
2270}
2271
2272static void yahoo_process_voicechat(struct yahoo_input_data *yid,
2273        struct yahoo_packet *pkt)
2274{
2275        char *who = NULL;
2276        char *me = NULL;
2277        char *room = NULL;
2278
2279        YList *l;
2280        for (l = pkt->hash; l; l = l->next) {
2281                struct yahoo_pair *pair = l->data;
2282                if (pair->key == 4)
2283                        who = pair->value;
2284                if (pair->key == 5)
2285                        me = pair->value;
2286                if (pair->key == 13)
2287                        ; /* voice room */
2288                if (pair->key == 57)
2289                        room = pair->value;
2290        }
2291
2292        NOTICE(("got voice chat invite from %s in %s to identity %s", who, room,
2293                        me));
2294        /*
2295         * send: s:0 1:me 5:who 57:room 13:1
2296         * ????  s:4 5:who 10:99 19:-1615114531
2297         * gotr: s:4 5:who 10:99 19:-1615114615
2298         * ????  s:1 5:me 4:who 57:room 13:3room
2299         * got:  s:1 5:me 4:who 57:room 13:1room
2300         * rej:  s:0 1:me 5:who 57:room 13:3
2301         * rejr: s:4 5:who 10:99 19:-1617114599
2302         */
2303}
2304
2305static void yahoo_process_ping(struct yahoo_input_data *yid,
2306        struct yahoo_packet *pkt)
2307{
2308        char *errormsg = NULL;
2309
2310        YList *l;
2311        for (l = pkt->hash; l; l = l->next) {
2312                struct yahoo_pair *pair = l->data;
2313                if (pair->key == 16)
2314                        errormsg = pair->value;
2315        }
2316
2317        NOTICE(("got ping packet"));
2318        YAHOO_CALLBACK(ext_yahoo_got_ping) (yid->yd->client_id, errormsg);
2319}
2320
2321static void yahoo_process_buddy_change_group(struct yahoo_input_data *yid,
2322        struct yahoo_packet *pkt)
2323{
2324        YList *l;
2325        char *me = NULL;
2326        char *who = NULL;
2327        char *old_group = NULL;
2328        char *new_group = NULL;
2329
2330        for (l = pkt->hash; l; l = l->next) {
2331                struct yahoo_pair *pair = l->data;
2332                if (pair->key == 1)
2333                        me = pair->value;
2334                if (pair->key == 7)
2335                        who = pair->value;
2336                if (pair->key == 224)
2337                        old_group = pair->value;
2338                if (pair->key == 264)
2339                        new_group = pair->value;
2340        }
2341
2342        YAHOO_CALLBACK(ext_yahoo_got_buddy_change_group) (yid->yd->client_id,
2343                me, who, old_group, new_group);
2344}
2345
2346static void _yahoo_webcam_get_server_connected(void *fd, int error, void *d)
2347{
2348        struct yahoo_input_data *yid = d;
2349        char *who = yid->wcm->user;
2350        char *data = NULL;
2351        char *packet = NULL;
2352        unsigned char magic_nr[] = { 0, 1, 0 };
2353        unsigned char header_len = 8;
2354        unsigned int len = 0;
2355        unsigned int pos = 0;
2356
2357        if (error || !fd) {
2358                FREE(who);
2359                FREE(yid);
2360                return;
2361        }
2362
2363        yid->fd = fd;
2364        inputs = y_list_prepend(inputs, yid);
2365
2366        /* send initial packet */
2367        if (who)
2368                data = strdup("<RVWCFG>");
2369        else
2370                data = strdup("<RUPCFG>");
2371        yahoo_add_to_send_queue(yid, data, strlen(data));
2372        FREE(data);
2373
2374        /* send data */
2375        if (who) {
2376                data = strdup("g=");
2377                data = y_string_append(data, who);
2378                data = y_string_append(data, "\r\n");
2379        } else {
2380                data = strdup("f=1\r\n");
2381        }
2382        len = strlen(data);
2383        packet = y_new0(char, header_len + len);
2384        packet[pos++] = header_len;
2385        memcpy(packet + pos, magic_nr, sizeof(magic_nr));
2386        pos += sizeof(magic_nr);
2387        pos += yahoo_put32(packet + pos, len);
2388        memcpy(packet + pos, data, len);
2389        pos += len;
2390        yahoo_add_to_send_queue(yid, packet, pos);
2391        FREE(packet);
2392        FREE(data);
2393
2394        yid->read_tag =
2395                YAHOO_CALLBACK(ext_yahoo_add_handler) (yid->yd->client_id, fd,
2396                YAHOO_INPUT_READ, yid);
2397}
2398
2399static void yahoo_webcam_get_server(struct yahoo_input_data *y, char *who,
2400        char *key)
2401{
2402        struct yahoo_input_data *yid = y_new0(struct yahoo_input_data, 1);
2403        struct yahoo_server_settings *yss = y->yd->server_settings;
2404
2405        yid->type = YAHOO_CONNECTION_WEBCAM_MASTER;
2406        yid->yd = y->yd;
2407        yid->wcm = y_new0(struct yahoo_webcam, 1);
2408        yid->wcm->user = who ? strdup(who) : NULL;
2409        yid->wcm->direction = who ? YAHOO_WEBCAM_DOWNLOAD : YAHOO_WEBCAM_UPLOAD;
2410        yid->wcm->key = strdup(key);
2411
2412        YAHOO_CALLBACK(ext_yahoo_connect_async) (yid->yd->client_id,
2413                yss->webcam_host, yss->webcam_port,
2414                _yahoo_webcam_get_server_connected, yid, 0);
2415
2416}
2417
2418static YList *webcam_queue = NULL;
2419static void yahoo_process_webcam_key(struct yahoo_input_data *yid,
2420        struct yahoo_packet *pkt)
2421{
2422        char *key = NULL;
2423        char *who = NULL;
2424
2425        YList *l;
2426        yahoo_dump_unhandled(pkt);
2427        for (l = pkt->hash; l; l = l->next) {
2428                struct yahoo_pair *pair = l->data;
2429                if (pair->key == 5)
2430                        ; /* me */
2431                if (pair->key == 61)
2432                        key = pair->value;
2433        }
2434
2435        l = webcam_queue;
2436        if (!l)
2437                return;
2438        who = l->data;
2439        webcam_queue = y_list_remove_link(webcam_queue, webcam_queue);
2440        y_list_free_1(l);
2441        yahoo_webcam_get_server(yid, who, key);
2442        FREE(who);
2443}
2444
2445static void yahoo_packet_process(struct yahoo_input_data *yid,
2446        struct yahoo_packet *pkt)
2447{
2448        DEBUG_MSG(("yahoo_packet_process: 0x%02x", pkt->service));
2449        switch (pkt->service) {
2450        case YAHOO_SERVICE_USERSTAT:
2451        case YAHOO_SERVICE_LOGON:
2452        case YAHOO_SERVICE_LOGOFF:
2453        case YAHOO_SERVICE_ISAWAY:
2454        case YAHOO_SERVICE_ISBACK:
2455        case YAHOO_SERVICE_GAMELOGON:
2456        case YAHOO_SERVICE_GAMELOGOFF:
2457        case YAHOO_SERVICE_IDACT:
2458        case YAHOO_SERVICE_IDDEACT:
2459        case YAHOO_SERVICE_Y6_STATUS_UPDATE:
2460        case YAHOO_SERVICE_Y8_STATUS:
2461                yahoo_process_status(yid, pkt);
2462                break;
2463        case YAHOO_SERVICE_NOTIFY:
2464                yahoo_process_notify(yid, pkt);
2465                break;
2466        case YAHOO_SERVICE_MESSAGE:
2467        case YAHOO_SERVICE_GAMEMSG:
2468        case YAHOO_SERVICE_SYSMESSAGE:
2469                yahoo_process_message(yid, pkt);
2470                break;
2471        case YAHOO_SERVICE_NEWMAIL:
2472                yahoo_process_mail(yid, pkt);
2473                break;
2474        case YAHOO_SERVICE_Y7_AUTHORIZATION:
2475                yahoo_process_new_contact(yid, pkt);
2476                break;
2477        case YAHOO_SERVICE_NEWCONTACT:
2478                yahoo_process_contact(yid, pkt);
2479                break;
2480        case YAHOO_SERVICE_LIST:
2481                yahoo_process_list(yid, pkt);
2482                break;
2483        case YAHOO_SERVICE_VERIFY:
2484                yahoo_process_verify(yid, pkt);
2485                break;
2486        case YAHOO_SERVICE_AUTH:
2487                yahoo_process_auth(yid, pkt);
2488                break;
2489        case YAHOO_SERVICE_AUTHRESP:
2490                yahoo_process_auth_resp(yid, pkt);
2491                break;
2492        case YAHOO_SERVICE_CONFINVITE:
2493        case YAHOO_SERVICE_CONFADDINVITE:
2494        case YAHOO_SERVICE_CONFDECLINE:
2495        case YAHOO_SERVICE_CONFLOGON:
2496        case YAHOO_SERVICE_CONFLOGOFF:
2497        case YAHOO_SERVICE_CONFMSG:
2498                yahoo_process_conference(yid, pkt);
2499                break;
2500        case YAHOO_SERVICE_CHATONLINE:
2501        case YAHOO_SERVICE_CHATGOTO:
2502        case YAHOO_SERVICE_CHATJOIN:
2503        case YAHOO_SERVICE_CHATLEAVE:
2504        case YAHOO_SERVICE_CHATEXIT:
2505        case YAHOO_SERVICE_CHATLOGOUT:
2506        case YAHOO_SERVICE_CHATPING:
2507        case YAHOO_SERVICE_COMMENT:
2508                yahoo_process_chat(yid, pkt);
2509                break;
2510        case YAHOO_SERVICE_P2PFILEXFER:
2511        case YAHOO_SERVICE_Y7_FILETRANSFER:
2512                yahoo_process_filetransfer(yid, pkt);
2513                break;
2514        case YAHOO_SERVICE_Y7_FILETRANSFERINFO:
2515                yahoo_process_filetransferinfo(yid, pkt);
2516                break;
2517        case YAHOO_SERVICE_Y7_FILETRANSFERACCEPT:
2518                yahoo_process_filetransferaccept(yid, pkt);
2519                break;
2520        case YAHOO_SERVICE_ADDBUDDY:
2521                yahoo_process_buddyadd(yid, pkt);
2522                break;
2523        case YAHOO_SERVICE_REMBUDDY:
2524                yahoo_process_buddydel(yid, pkt);
2525                break;
2526        case YAHOO_SERVICE_IGNORECONTACT:
2527                yahoo_process_ignore(yid, pkt);
2528                break;
2529        case YAHOO_SERVICE_VOICECHAT:
2530                yahoo_process_voicechat(yid, pkt);
2531                break;
2532        case YAHOO_SERVICE_WEBCAM:
2533                yahoo_process_webcam_key(yid, pkt);
2534                break;
2535        case YAHOO_SERVICE_PING:
2536                yahoo_process_ping(yid, pkt);
2537                break;
2538        case YAHOO_SERVICE_Y7_CHANGE_GROUP:
2539                yahoo_process_buddy_change_group(yid, pkt);
2540                break;
2541        case YAHOO_SERVICE_IDLE:
2542        case YAHOO_SERVICE_MAILSTAT:
2543        case YAHOO_SERVICE_CHATINVITE:
2544        case YAHOO_SERVICE_CALENDAR:
2545        case YAHOO_SERVICE_NEWPERSONALMAIL:
2546        case YAHOO_SERVICE_ADDIDENT:
2547        case YAHOO_SERVICE_ADDIGNORE:
2548        case YAHOO_SERVICE_GOTGROUPRENAME:
2549        case YAHOO_SERVICE_GROUPRENAME:
2550        case YAHOO_SERVICE_PASSTHROUGH2:
2551        case YAHOO_SERVICE_CHATLOGON:
2552        case YAHOO_SERVICE_CHATLOGOFF:
2553        case YAHOO_SERVICE_CHATMSG:
2554        case YAHOO_SERVICE_REJECTCONTACT:
2555        case YAHOO_SERVICE_PEERTOPEER:
2556                WARNING(("unhandled service 0x%02x", pkt->service));
2557                yahoo_dump_unhandled(pkt);
2558                break;
2559        case YAHOO_SERVICE_PICTURE:
2560                yahoo_process_picture(yid, pkt);
2561                break;
2562        case YAHOO_SERVICE_PICTURE_CHECKSUM:
2563                yahoo_process_picture_checksum(yid, pkt);
2564                break;
2565        case YAHOO_SERVICE_PICTURE_UPLOAD:
2566                yahoo_process_picture_upload(yid, pkt);
2567                break;
2568        case YAHOO_SERVICE_Y8_LIST:     /* Buddy List */
2569                yahoo_process_buddy_list(yid, pkt);
2570                break;
2571        default:
2572                WARNING(("unknown service 0x%02x", pkt->service));
2573                yahoo_dump_unhandled(pkt);
2574                break;
2575        }
2576}
2577
2578static struct yahoo_packet *yahoo_getdata(struct yahoo_input_data *yid)
2579{
2580        struct yahoo_packet *pkt;
2581        struct yahoo_data *yd = yid->yd;
2582        int pos = 0;
2583        int pktlen;
2584
2585        if (!yd)
2586                return NULL;
2587
2588        DEBUG_MSG(("rxlen is %d", yid->rxlen));
2589        if (yid->rxlen < YAHOO_PACKET_HDRLEN) {
2590                DEBUG_MSG(("len < YAHOO_PACKET_HDRLEN"));
2591                return NULL;
2592        }
2593
2594        pos += 4;               /* YMSG */
2595        pos += 2;
2596        pos += 2;
2597
2598        pktlen = yahoo_get16(yid->rxqueue + pos);
2599        pos += 2;
2600        DEBUG_MSG(("%d bytes to read, rxlen is %d", pktlen, yid->rxlen));
2601
2602        if (yid->rxlen < (YAHOO_PACKET_HDRLEN + pktlen)) {
2603                DEBUG_MSG(("len < YAHOO_PACKET_HDRLEN + pktlen"));
2604                return NULL;
2605        }
2606
2607        LOG(("reading packet"));
2608        yahoo_packet_dump(yid->rxqueue, YAHOO_PACKET_HDRLEN + pktlen);
2609
2610        pkt = yahoo_packet_new(0, 0, 0);
2611
2612        pkt->service = yahoo_get16(yid->rxqueue + pos);
2613        pos += 2;
2614        pkt->status = yahoo_get32(yid->rxqueue + pos);
2615        pos += 4;
2616        DEBUG_MSG(("Yahoo Service: 0x%02x Status: %d", pkt->service,
2617                        pkt->status));
2618        pkt->id = yahoo_get32(yid->rxqueue + pos);
2619        pos += 4;
2620
2621        yd->session_id = pkt->id;
2622
2623        yahoo_packet_read(pkt, yid->rxqueue + pos, pktlen);
2624
2625        yid->rxlen -= YAHOO_PACKET_HDRLEN + pktlen;
2626        DEBUG_MSG(("rxlen == %d, rxqueue == %p", yid->rxlen, yid->rxqueue));
2627        if (yid->rxlen > 0) {
2628                unsigned char *tmp = y_memdup(yid->rxqueue + YAHOO_PACKET_HDRLEN
2629                        + pktlen, yid->rxlen);
2630                FREE(yid->rxqueue);
2631                yid->rxqueue = tmp;
2632                DEBUG_MSG(("new rxlen == %d, rxqueue == %p", yid->rxlen,
2633                                yid->rxqueue));
2634        } else {
2635                DEBUG_MSG(("freed rxqueue == %p", yid->rxqueue));
2636                FREE(yid->rxqueue);
2637        }
2638
2639        return pkt;
2640}
2641
2642static struct yab *yahoo_yab_read(unsigned char *d, int len)
2643{
2644        char *st, *en;
2645        char *data = (char *)d;
2646        struct yab *yab = NULL;
2647
2648        data[len] = '\0';
2649
2650        DEBUG_MSG(("Got yab: %s", data));
2651        st = en = strstr(data, "e0=\"");
2652        if (st) {
2653                yab = y_new0(struct yab, 1);
2654
2655                st += strlen("e0=\"");
2656                en = strchr(st, '"');
2657                *en++ = '\0';
2658                yab->email = yahoo_xmldecode(st);
2659        }
2660
2661        if (!en)
2662                return NULL;
2663
2664        st = strstr(en, "id=\"");
2665        if (st) {
2666                st += strlen("id=\"");
2667                en = strchr(st, '"');
2668                *en++ = '\0';
2669                yab->yid = atoi(yahoo_xmldecode(st));
2670        }
2671
2672        st = strstr(en, "fn=\"");
2673        if (st) {
2674                st += strlen("fn=\"");
2675                en = strchr(st, '"');
2676                *en++ = '\0';
2677                yab->fname = yahoo_xmldecode(st);
2678        }
2679
2680        st = strstr(en, "ln=\"");
2681        if (st) {
2682                st += strlen("ln=\"");
2683                en = strchr(st, '"');
2684                *en++ = '\0';
2685                yab->lname = yahoo_xmldecode(st);
2686        }
2687
2688        st = strstr(en, "nn=\"");
2689        if (st) {
2690                st += strlen("nn=\"");
2691                en = strchr(st, '"');
2692                *en++ = '\0';
2693                yab->nname = yahoo_xmldecode(st);
2694        }
2695
2696        st = strstr(en, "yi=\"");
2697        if (st) {
2698                st += strlen("yi=\"");
2699                en = strchr(st, '"');
2700                *en++ = '\0';
2701                yab->id = yahoo_xmldecode(st);
2702        }
2703
2704        st = strstr(en, "hphone=\"");
2705        if (st) {
2706                st += strlen("hphone=\"");
2707                en = strchr(st, '"');
2708                *en++ = '\0';
2709                yab->hphone = yahoo_xmldecode(st);
2710        }
2711
2712        st = strstr(en, "wphone=\"");
2713        if (st) {
2714                st += strlen("wphone=\"");
2715                en = strchr(st, '"');
2716                *en++ = '\0';
2717                yab->wphone = yahoo_xmldecode(st);
2718        }
2719
2720        st = strstr(en, "mphone=\"");
2721        if (st) {
2722                st += strlen("mphone=\"");
2723                en = strchr(st, '"');
2724                *en++ = '\0';
2725                yab->mphone = yahoo_xmldecode(st);
2726        }
2727
2728        st = strstr(en, "dbid=\"");
2729        if (st) {
2730                st += strlen("dbid=\"");
2731                en = strchr(st, '"');
2732                *en++ = '\0';
2733                yab->dbid = atoi(st);
2734        }
2735
2736        return yab;
2737}
2738
2739static struct yab *yahoo_getyab(struct yahoo_input_data *yid)
2740{
2741        struct yab *yab = NULL;
2742        int pos = 0, end = 0;
2743        struct yahoo_data *yd = yid->yd;
2744
2745        if (!yd)
2746                return NULL;
2747
2748        do {
2749                DEBUG_MSG(("rxlen is %d", yid->rxlen));
2750
2751                if (yid->rxlen <= strlen("<ct"))
2752                        return NULL;
2753
2754                /* start with <ct */
2755                while (pos < yid->rxlen - strlen("<ct") + 1
2756                        && memcmp(yid->rxqueue + pos, "<ct", strlen("<ct")))
2757                        pos++;
2758
2759                if (pos >= yid->rxlen - 1)
2760                        return NULL;
2761
2762                end = pos + 2;
2763                /* end with > */
2764                while (end < yid->rxlen - strlen(">")
2765                        && memcmp(yid->rxqueue + end, ">", strlen(">")))
2766                        end++;
2767
2768                if (end >= yid->rxlen - 1)
2769                        return NULL;
2770
2771                yab = yahoo_yab_read(yid->rxqueue + pos, end + 2 - pos);
2772
2773                yid->rxlen -= end + 1;
2774                DEBUG_MSG(("rxlen == %d, rxqueue == %p", yid->rxlen,
2775                                yid->rxqueue));
2776                if (yid->rxlen > 0) {
2777                        unsigned char *tmp =
2778                                y_memdup(yid->rxqueue + end + 1, yid->rxlen);
2779                        FREE(yid->rxqueue);
2780                        yid->rxqueue = tmp;
2781                        DEBUG_MSG(("new rxlen == %d, rxqueue == %p", yid->rxlen,
2782                                        yid->rxqueue));
2783                } else {
2784                        DEBUG_MSG(("freed rxqueue == %p", yid->rxqueue));
2785                        FREE(yid->rxqueue);
2786                }
2787
2788        } while (!yab && end < yid->rxlen - 1);
2789
2790        return yab;
2791}
2792
2793static char *yahoo_getwebcam_master(struct yahoo_input_data *yid)
2794{
2795        unsigned int pos = 0;
2796        unsigned int len = 0;
2797        unsigned int status = 0;
2798        char *server = NULL;
2799        struct yahoo_data *yd = yid->yd;
2800
2801        if (!yid || !yd)
2802                return NULL;
2803
2804        DEBUG_MSG(("rxlen is %d", yid->rxlen));
2805
2806        len = yid->rxqueue[pos++];
2807        if (yid->rxlen < len)
2808                return NULL;
2809
2810        /* extract status (0 = ok, 6 = webcam not online) */
2811        status = yid->rxqueue[pos++];
2812
2813        if (status == 0) {
2814                pos += 2;       /* skip next 2 bytes */
2815                server = y_memdup(yid->rxqueue + pos, 16);
2816                pos += 16;
2817        } else if (status == 6) {
2818                YAHOO_CALLBACK(ext_yahoo_webcam_closed)
2819                        (yd->client_id, yid->wcm->user, 4);
2820        }
2821
2822        /* skip rest of the data */
2823
2824        yid->rxlen -= len;
2825        DEBUG_MSG(("rxlen == %d, rxqueue == %p", yid->rxlen, yid->rxqueue));
2826        if (yid->rxlen > 0) {
2827                unsigned char *tmp = y_memdup(yid->rxqueue + pos, yid->rxlen);
2828                FREE(yid->rxqueue);
2829                yid->rxqueue = tmp;
2830                DEBUG_MSG(("new rxlen == %d, rxqueue == %p", yid->rxlen,
2831                                yid->rxqueue));
2832        } else {
2833                DEBUG_MSG(("freed rxqueue == %p", yid->rxqueue));
2834                FREE(yid->rxqueue);
2835        }
2836
2837        return server;
2838}
2839
2840static int yahoo_get_webcam_data(struct yahoo_input_data *yid)
2841{
2842        unsigned char reason = 0;
2843        unsigned int pos = 0;
2844        unsigned int begin = 0;
2845        unsigned int end = 0;
2846        unsigned int closed = 0;
2847        unsigned char header_len = 0;
2848        char *who;
2849        int connect = 0;
2850        struct yahoo_data *yd = yid->yd;
2851
2852        if (!yd)
2853                return -1;
2854
2855        if (!yid->wcm || !yid->wcd || !yid->rxlen)
2856                return -1;
2857
2858        DEBUG_MSG(("rxlen is %d", yid->rxlen));
2859
2860        /* if we are not reading part of image then read header */
2861        if (!yid->wcd->to_read) {
2862                header_len = yid->rxqueue[pos++];
2863                yid->wcd->packet_type = 0;
2864
2865                if (yid->rxlen < header_len)
2866                        return 0;
2867
2868                if (header_len >= 8) {
2869                        reason = yid->rxqueue[pos++];
2870                        /* next 2 bytes should always be 05 00 */
2871                        pos += 2;
2872                        yid->wcd->data_size = yahoo_get32(yid->rxqueue + pos);
2873                        pos += 4;
2874                        yid->wcd->to_read = yid->wcd->data_size;
2875                }
2876                if (header_len >= 13) {
2877                        yid->wcd->packet_type = yid->rxqueue[pos++];
2878                        yid->wcd->timestamp = yahoo_get32(yid->rxqueue + pos);
2879                        pos += 4;
2880                }
2881
2882                /* skip rest of header */
2883                pos = header_len;
2884        }
2885
2886        begin = pos;
2887        pos += yid->wcd->to_read;
2888        if (pos > yid->rxlen)
2889                pos = yid->rxlen;
2890
2891        /* if it is not an image then make sure we have the whole packet */
2892        if (yid->wcd->packet_type != 0x02) {
2893                if ((pos - begin) != yid->wcd->data_size) {
2894                        yid->wcd->to_read = 0;
2895                        return 0;
2896                } else {
2897                        yahoo_packet_dump(yid->rxqueue + begin, pos - begin);
2898                }
2899        }
2900
2901        DEBUG_MSG(("packet type %.2X, data length %d", yid->wcd->packet_type,
2902                        yid->wcd->data_size));
2903
2904        /* find out what kind of packet we got */
2905        switch (yid->wcd->packet_type) {
2906        case 0x00:
2907                /* user requests to view webcam (uploading) */
2908                if (yid->wcd->data_size &&
2909                        yid->wcm->direction == YAHOO_WEBCAM_UPLOAD) {
2910                        end = begin;
2911                        while (end <= yid->rxlen && yid->rxqueue[end++] != 13) ;
2912                        if (end > begin) {
2913                                who = y_memdup(yid->rxqueue + begin,
2914                                        end - begin);
2915                                who[end - begin - 1] = 0;
2916                                YAHOO_CALLBACK(ext_yahoo_webcam_viewer) (yd->
2917                                        client_id, who + 2, 2);
2918                                FREE(who);
2919                        }
2920                }
2921
2922                if (yid->wcm->direction == YAHOO_WEBCAM_DOWNLOAD) {
2923                        /* timestamp/status field */
2924                        /* 0 = declined viewing permission */
2925                        /* 1 = accepted viewing permission */
2926                        if (yid->wcd->timestamp == 0) {
2927                                YAHOO_CALLBACK(ext_yahoo_webcam_closed) (yd->
2928                                        client_id, yid->wcm->user, 3);
2929                        }
2930                }
2931                break;
2932        case 0x01:              /* status packets?? */
2933                /* timestamp contains status info */
2934                /* 00 00 00 01 = we have data?? */
2935                break;
2936        case 0x02:              /* image data */
2937                YAHOO_CALLBACK(ext_yahoo_got_webcam_image) (yd->client_id,
2938                        yid->wcm->user, yid->rxqueue + begin,
2939                        yid->wcd->data_size, pos - begin, yid->wcd->timestamp);
2940                break;
2941        case 0x05:              /* response packets when uploading */
2942                if (!yid->wcd->data_size) {
2943                        YAHOO_CALLBACK(ext_yahoo_webcam_data_request) (yd->
2944                                client_id, yid->wcd->timestamp);
2945                }
2946                break;
2947        case 0x07:              /* connection is closing */
2948                switch (reason) {
2949                case 0x01:      /* user closed connection */
2950                        closed = 1;
2951                        break;
2952                case 0x0F:      /* user cancelled permission */
2953                        closed = 2;
2954                        break;
2955                }
2956                YAHOO_CALLBACK(ext_yahoo_webcam_closed) (yd->client_id,
2957                        yid->wcm->user, closed);
2958                break;
2959        case 0x0C:              /* user connected */
2960        case 0x0D:              /* user disconnected */
2961                if (yid->wcd->data_size) {
2962                        who = y_memdup(yid->rxqueue + begin, pos - begin + 1);
2963                        who[pos - begin] = 0;
2964                        if (yid->wcd->packet_type == 0x0C)
2965                                connect = 1;
2966                        else
2967                                connect = 0;
2968                        YAHOO_CALLBACK(ext_yahoo_webcam_viewer) (yd->client_id,
2969                                who, connect);
2970                        FREE(who);
2971                }
2972                break;
2973        case 0x13:              /* user data */
2974                /* i=user_ip (ip of the user we are viewing) */
2975                /* j=user_ext_ip (external ip of the user we */
2976                /*                are viewing) */
2977                break;
2978        case 0x17:              /* ?? */
2979                break;
2980        }
2981        yid->wcd->to_read -= pos - begin;
2982
2983        yid->rxlen -= pos;
2984        DEBUG_MSG(("rxlen == %d, rxqueue == %p", yid->rxlen, yid->rxqueue));
2985        if (yid->rxlen > 0) {
2986                unsigned char *tmp = y_memdup(yid->rxqueue + pos, yid->rxlen);
2987                FREE(yid->rxqueue);
2988                yid->rxqueue = tmp;
2989                DEBUG_MSG(("new rxlen == %d, rxqueue == %p", yid->rxlen,
2990                                yid->rxqueue));
2991        } else {
2992                DEBUG_MSG(("freed rxqueue == %p", yid->rxqueue));
2993                FREE(yid->rxqueue);
2994        }
2995
2996        /* If we read a complete packet return success */
2997        if (!yid->wcd->to_read)
2998                return 1;
2999
3000        return 0;
3001}
3002
3003int yahoo_write_ready(int id, void *fd, void *data)
3004{
3005        struct yahoo_input_data *yid = data;
3006        int len;
3007        struct data_queue *tx;
3008
3009        LOG(("write callback: id=%d fd=%p data=%p", id, fd, data));
3010        if (!yid || !yid->txqueues)
3011                return -2;
3012
3013        tx = yid->txqueues->data;
3014        LOG(("writing %d bytes", tx->len));
3015        len = yahoo_send_data(fd, tx->queue, MIN(1024, tx->len));
3016
3017        if (len == -1 && errno == EAGAIN)
3018                return 1;
3019
3020        if (len <= 0) {
3021                int e = errno;
3022                DEBUG_MSG(("len == %d (<= 0)", len));
3023                while (yid->txqueues) {
3024                        YList *l = yid->txqueues;
3025                        tx = l->data;
3026                        free(tx->queue);
3027                        free(tx);
3028                        yid->txqueues =
3029                                y_list_remove_link(yid->txqueues,
3030                                yid->txqueues);
3031                        y_list_free_1(l);
3032                }
3033                LOG(("yahoo_write_ready(%d, %p) len < 0", id, fd));
3034                YAHOO_CALLBACK(ext_yahoo_remove_handler) (id, yid->write_tag);
3035                yid->write_tag = 0;
3036                errno = e;
3037                return 0;
3038        }
3039
3040
3041        tx->len -= len;
3042        if (tx->len > 0) {
3043                unsigned char *tmp = y_memdup(tx->queue + len, tx->len);
3044                FREE(tx->queue);
3045                tx->queue = tmp;
3046        } else {
3047                YList *l = yid->txqueues;
3048                free(tx->queue);
3049                free(tx);
3050                yid->txqueues =
3051                        y_list_remove_link(yid->txqueues, yid->txqueues);
3052                y_list_free_1(l);
3053                /*
3054                   if(!yid->txqueues)
3055                   LOG(("yahoo_write_ready(%d, %d) !yxqueues", id, fd));
3056                 */
3057                if (!yid->txqueues) {
3058                        LOG(("yahoo_write_ready(%d, %p) !txqueues", id, fd));
3059                        YAHOO_CALLBACK(ext_yahoo_remove_handler) (id,
3060                                yid->write_tag);
3061                        yid->write_tag = 0;
3062                }
3063        }
3064
3065        return 1;
3066}
3067
3068static void yahoo_process_pager_connection(struct yahoo_input_data *yid,
3069        int over)
3070{
3071        struct yahoo_packet *pkt;
3072        struct yahoo_data *yd = yid->yd;
3073        int id = yd->client_id;
3074
3075        if (over)
3076                return;
3077
3078        while (find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER)
3079                && (pkt = yahoo_getdata(yid)) != NULL) {
3080
3081                yahoo_packet_process(yid, pkt);
3082
3083                yahoo_packet_free(pkt);
3084        }
3085}
3086
3087static void yahoo_process_chatcat_connection(struct yahoo_input_data *yid,
3088        int over)
3089{
3090        if (over)
3091                return;
3092
3093        if (strstr((char *)yid->rxqueue + (yid->rxlen - 20), "</content>")) {
3094                YAHOO_CALLBACK(ext_yahoo_chat_cat_xml) (yid->yd->client_id,
3095                        (char *)yid->rxqueue);
3096        }
3097}
3098
3099static void yahoo_process_yab_connection(struct yahoo_input_data *yid, int over)
3100{
3101        struct yahoo_data *yd = yid->yd;
3102        struct yab *yab;
3103        YList *buds;
3104        int changed = 0;
3105        int id = yd->client_id;
3106        int yab_used = 0;
3107
3108        LOG(("Got data for YAB"));
3109
3110        if (over)
3111                return;
3112
3113        while (find_input_by_id_and_type(id, YAHOO_CONNECTION_YAB)
3114                && (yab = yahoo_getyab(yid)) != NULL) {
3115                if (!yab->id)
3116                        continue;
3117
3118                changed = 1;
3119                yab_used = 0;
3120                for (buds = yd->buddies; buds; buds = buds->next) {
3121                        struct yahoo_buddy *bud = buds->data;
3122                        if (!strcmp(bud->id, yab->id)) {
3123                                yab_used = 1;
3124                                bud->yab_entry = yab;
3125                                if (yab->nname) {
3126                                        bud->real_name = strdup(yab->nname);
3127                                } else if (yab->fname && yab->lname) {
3128                                        bud->real_name = y_new0(char,
3129                                                strlen(yab->fname) +
3130                                                strlen(yab->lname) + 2);
3131                                        sprintf(bud->real_name, "%s %s",
3132                                                yab->fname, yab->lname);
3133                                } else if (yab->fname) {
3134                                        bud->real_name = strdup(yab->fname);
3135                                }
3136                                break;  /* for */
3137                        }
3138                }
3139
3140                if (!yab_used) {
3141                        FREE(yab->fname);
3142                        FREE(yab->lname);
3143                        FREE(yab->nname);
3144                        FREE(yab->id);
3145                        FREE(yab->email);
3146                        FREE(yab->hphone);
3147                        FREE(yab->wphone);
3148                        FREE(yab->mphone);
3149                        FREE(yab);
3150                }
3151
3152        }
3153
3154        if (changed)
3155                YAHOO_CALLBACK(ext_yahoo_got_buddies) (yd->client_id,
3156                        yd->buddies);
3157}
3158
3159static void yahoo_process_search_connection(struct yahoo_input_data *yid,
3160        int over)
3161{
3162        struct yahoo_found_contact *yct = NULL;
3163        char *p = (char *)yid->rxqueue, *np, *cp;
3164        int k, n;
3165        int start = 0, found = 0, total = 0;
3166        YList *contacts = NULL;
3167        struct yahoo_input_data *pyid =
3168                find_input_by_id_and_type(yid->yd->client_id,
3169                YAHOO_CONNECTION_PAGER);
3170
3171        if (!over || !pyid)
3172                return;
3173
3174        if (p && (p = strstr(p, "\r\n\r\n"))) {
3175                p += 4;
3176
3177                for (k = 0; (p = strchr(p, 4)) && (k < 4); k++) {
3178                        p++;
3179                        n = atoi(p);
3180                        switch (k) {
3181                        case 0:
3182                                found = pyid->ys->lsearch_nfound = n;
3183                                break;
3184                        case 2:
3185                                start = pyid->ys->lsearch_nstart = n;
3186                                break;
3187                        case 3:
3188                                total = pyid->ys->lsearch_ntotal = n;
3189                                break;
3190                        }
3191                }
3192
3193                if (p)
3194                        p++;
3195
3196                k = 0;
3197                while (p && *p) {
3198                        cp = p;
3199                        np = strchr(p, 4);
3200
3201                        if (!np)
3202                                break;
3203                        *np = 0;
3204                        p = np + 1;
3205
3206                        switch (k++) {
3207                        case 1:
3208                                if (strlen(cp) > 2
3209                                        && y_list_length(contacts) < total) {
3210                                        yct = y_new0(struct yahoo_found_contact,
3211                                                1);
3212                                        contacts = y_list_append(contacts, yct);
3213                                        yct->id = cp + 2;
3214                                } else {
3215                                        *p = 0;
3216                                }
3217                                break;
3218                        case 2:
3219                                yct->online = !strcmp(cp, "2") ? 1 : 0;
3220                                break;
3221                        case 3:
3222                                yct->gender = cp;
3223                                break;
3224                        case 4:
3225                                yct->age = atoi(cp);
3226                                break;
3227                        case 5:
3228                                /* not worth the context switch for strcmp */
3229                                if (cp[0] != '\005' || cp[1] != '\000')
3230                                        yct->location = cp;
3231                                k = 0;
3232                                break;
3233                        }
3234                }
3235        }
3236
3237        YAHOO_CALLBACK(ext_yahoo_got_search_result) (yid->yd->client_id, found,
3238                start, total, contacts);
3239
3240        while (contacts) {
3241                YList *node = contacts;
3242                contacts = y_list_remove_link(contacts, node);
3243                free(node->data);
3244                y_list_free_1(node);
3245        }
3246}
3247
3248static void _yahoo_webcam_connected(void *fd, int error, void *d)
3249{
3250        struct yahoo_input_data *yid = d;
3251        struct yahoo_webcam *wcm = yid->wcm;
3252        struct yahoo_data *yd = yid->yd;
3253        char conn_type[100];
3254        char *data = NULL;
3255        char *packet = NULL;
3256        unsigned char magic_nr[] = { 1, 0, 0, 0, 1 };
3257        unsigned header_len = 0;
3258        unsigned int len = 0;
3259        unsigned int pos = 0;
3260
3261        if (error || !fd) {
3262                FREE(yid);
3263                return;
3264        }
3265
3266        yid->fd = fd;
3267        inputs = y_list_prepend(inputs, yid);
3268
3269        LOG(("Connected"));
3270        /* send initial packet */
3271        switch (wcm->direction) {
3272        case YAHOO_WEBCAM_DOWNLOAD:
3273                data = strdup("<REQIMG>");
3274                break;
3275        case YAHOO_WEBCAM_UPLOAD:
3276                data = strdup("<SNDIMG>");
3277                break;
3278        default:
3279                return;
3280        }
3281        yahoo_add_to_send_queue(yid, data, strlen(data));
3282        FREE(data);
3283
3284        /* send data */
3285        switch (wcm->direction) {
3286        case YAHOO_WEBCAM_DOWNLOAD:
3287                header_len = 8;
3288                data = strdup("a=2\r\nc=us\r\ne=21\r\nu=");
3289                data = y_string_append(data, yd->user);
3290                data = y_string_append(data, "\r\nt=");
3291                data = y_string_append(data, wcm->key);
3292                data = y_string_append(data, "\r\ni=");
3293                data = y_string_append(data, wcm->my_ip);
3294                data = y_string_append(data, "\r\ng=");
3295                data = y_string_append(data, wcm->user);
3296                data = y_string_append(data, "\r\no=w-2-5-1\r\np=");
3297                snprintf(conn_type, sizeof(conn_type), "%d", wcm->conn_type);
3298                data = y_string_append(data, conn_type);
3299                data = y_string_append(data, "\r\n");
3300                break;
3301        case YAHOO_WEBCAM_UPLOAD:
3302                header_len = 13;
3303                data = strdup("a=2\r\nc=us\r\nu=");
3304                data = y_string_append(data, yd->user);
3305                data = y_string_append(data, "\r\nt=");
3306                data = y_string_append(data, wcm->key);
3307                data = y_string_append(data, "\r\ni=");
3308                data = y_string_append(data, wcm->my_ip);
3309                data = y_string_append(data, "\r\no=w-2-5-1\r\np=");
3310                snprintf(conn_type, sizeof(conn_type), "%d", wcm->conn_type);
3311                data = y_string_append(data, conn_type);
3312                data = y_string_append(data, "\r\nb=");
3313                data = y_string_append(data, wcm->description);
3314                data = y_string_append(data, "\r\n");
3315                break;
3316        }
3317
3318        len = strlen(data);
3319        packet = y_new0(char, header_len + len);
3320        packet[pos++] = header_len;
3321        packet[pos++] = 0;
3322        switch (wcm->direction) {
3323        case YAHOO_WEBCAM_DOWNLOAD:
3324                packet[pos++] = 1;
3325                packet[pos++] = 0;
3326                break;
3327        case YAHOO_WEBCAM_UPLOAD:
3328                packet[pos++] = 5;
3329                packet[pos++] = 0;
3330                break;
3331        }
3332
3333        pos += yahoo_put32(packet + pos, len);
3334        if (wcm->direction == YAHOO_WEBCAM_UPLOAD) {
3335                memcpy(packet + pos, magic_nr, sizeof(magic_nr));
3336                pos += sizeof(magic_nr);
3337        }
3338        memcpy(packet + pos, data, len);
3339        yahoo_add_to_send_queue(yid, packet, header_len + len);
3340        FREE(packet);
3341        FREE(data);
3342
3343        yid->read_tag =
3344                YAHOO_CALLBACK(ext_yahoo_add_handler) (yid->yd->client_id,
3345                yid->fd, YAHOO_INPUT_READ, yid);
3346}
3347
3348static void yahoo_webcam_connect(struct yahoo_input_data *y)
3349{
3350        struct yahoo_webcam *wcm = y->wcm;
3351        struct yahoo_input_data *yid;
3352
3353        if (!wcm || !wcm->server || !wcm->key)
3354                return;
3355
3356        yid = y_new0(struct yahoo_input_data, 1);
3357        yid->type = YAHOO_CONNECTION_WEBCAM;
3358        yid->yd = y->yd;
3359
3360        /* copy webcam data to new connection */
3361        yid->wcm = y->wcm;
3362        y->wcm = NULL;
3363
3364        yid->wcd = y_new0(struct yahoo_webcam_data, 1);
3365
3366        LOG(("Connecting to: %s:%d", wcm->server, wcm->port));
3367        YAHOO_CALLBACK(ext_yahoo_connect_async) (y->yd->client_id, wcm->server,
3368                wcm->port, _yahoo_webcam_connected, yid, 0);
3369
3370}
3371
3372static void yahoo_process_webcam_master_connection(struct yahoo_input_data *yid,
3373        int over)
3374{
3375        char *server;
3376        struct yahoo_server_settings *yss;
3377
3378        if (over)
3379                return;
3380
3381        server = yahoo_getwebcam_master(yid);
3382
3383        if (server) {
3384                yss = yid->yd->server_settings;
3385                yid->wcm->server = strdup(server);
3386                yid->wcm->port = yss->webcam_port;
3387                yid->wcm->conn_type = yss->conn_type;
3388                yid->wcm->my_ip = strdup(yss->local_host);
3389                if (yid->wcm->direction == YAHOO_WEBCAM_UPLOAD)
3390                        yid->wcm->description = strdup(yss->webcam_description);
3391                yahoo_webcam_connect(yid);
3392                FREE(server);
3393        }
3394}
3395
3396static void yahoo_process_webcam_connection(struct yahoo_input_data *yid,
3397        int over)
3398{
3399        int id = yid->yd->client_id;
3400        void *fd = yid->fd;
3401
3402        if (over)
3403                return;
3404
3405        /* as long as we still have packets available keep processing them */
3406        while (find_input_by_id_and_fd(id, fd)
3407                && yahoo_get_webcam_data(yid) == 1) ;
3408}
3409
3410static void (*yahoo_process_connection[]) (struct yahoo_input_data *,
3411        int over) = {
3412yahoo_process_pager_connection, yahoo_process_ft_connection,
3413                yahoo_process_yab_connection,
3414                yahoo_process_webcam_master_connection,
3415                yahoo_process_webcam_connection,
3416                yahoo_process_chatcat_connection,
3417                yahoo_process_search_connection};
3418
3419int yahoo_read_ready(int id, void *fd, void *data)
3420{
3421        struct yahoo_input_data *yid = data;
3422        char buf[1024];
3423        int len;
3424
3425        LOG(("read callback: id=%d fd=%p data=%p", id, fd, data));
3426        if (!yid)
3427                return -2;
3428
3429        do {
3430                len = YAHOO_CALLBACK(ext_yahoo_read) (fd, buf, sizeof(buf));
3431        } while (len == -1 && errno == EINTR);
3432
3433        if (len == -1 && (errno == EAGAIN || errno == EINTR))   /* we'll try again later */
3434                return 1;
3435
3436        if (len <= 0) {
3437                int e = errno;
3438                DEBUG_MSG(("len == %d (<= 0)", len));
3439
3440                if (yid->type == YAHOO_CONNECTION_PAGER) {
3441                        YAHOO_CALLBACK(ext_yahoo_login_response) (yid->yd->
3442                                client_id, YAHOO_LOGIN_SOCK, NULL);
3443                }
3444
3445                yahoo_process_connection[yid->type] (yid, 1);
3446                yahoo_input_close(yid);
3447
3448                /* no need to return an error, because we've already fixed it */
3449                if (len == 0)
3450                        return 1;
3451
3452                errno = e;
3453                LOG(("read error: %s", strerror(errno)));
3454                return -1;
3455        }
3456
3457        yid->rxqueue =
3458                y_renew(unsigned char, yid->rxqueue, len + yid->rxlen + 1);
3459        memcpy(yid->rxqueue + yid->rxlen, buf, len);
3460        yid->rxlen += len;
3461        yid->rxqueue[yid->rxlen] = 0;
3462
3463        yahoo_process_connection[yid->type] (yid, 0);
3464
3465        return len;
3466}
3467
3468int yahoo_init_with_attributes(const char *username, const char *password, ...)
3469{
3470        va_list ap;
3471        struct yahoo_data *yd;
3472
3473        yd = y_new0(struct yahoo_data, 1);
3474
3475        if (!yd)
3476                return 0;
3477
3478        yd->user = strdup(username);
3479        yd->password = strdup(password);
3480
3481        yd->initial_status = -1;
3482        yd->current_status = -1;
3483
3484        yd->client_id = ++last_id;
3485
3486        add_to_list(yd);
3487
3488        va_start(ap, password);
3489        yd->server_settings = _yahoo_assign_server_settings(ap);
3490        va_end(ap);
3491
3492        return yd->client_id;
3493}
3494
3495int yahoo_init(const char *username, const char *password)
3496{
3497        return yahoo_init_with_attributes(username, password, NULL);
3498}
3499
3500static void yahoo_connected(void *fd, int error, void *data)
3501{
3502        struct connect_callback_data *ccd = data;
3503        struct yahoo_data *yd = ccd->yd;
3504        struct yahoo_packet *pkt;
3505        struct yahoo_input_data *yid;
3506        struct yahoo_server_settings *yss = yd->server_settings;
3507
3508        if (error) {
3509                int tag;
3510                if (fallback_ports[ccd->i]) {
3511                        char *host = yss->pager_host;
3512
3513                        if (!host)
3514                                host = yss->pager_host_list[ccd->server_i];
3515
3516                        yss->pager_port = fallback_ports[ccd->i++];
3517                        tag = YAHOO_CALLBACK(ext_yahoo_connect_async) (yd->
3518                                client_id, host, yss->pager_port,
3519                                yahoo_connected, ccd, 0);
3520
3521                        if (tag > 0)
3522                                ccd->tag = tag;
3523                } else if (yss->pager_host_list
3524                                && yss->pager_host_list[ccd->server_i]) {
3525
3526                        /* Get back to the default port */
3527                        yss->pager_port = pager_port;
3528                        ccd->server_i++;
3529                        LOG(("Fallback: Connecting to %s:%d", yss->pager_host_list[ccd->server_i], yss->pager_port));
3530
3531                        ccd->i = 0;
3532                        tag = YAHOO_CALLBACK(ext_yahoo_connect_async) (yd->client_id,
3533                                yss->pager_host_list[ccd->server_i], yss->pager_port,
3534                                yahoo_connected, ccd, 0);
3535                } else {
3536                        FREE(ccd);
3537                        YAHOO_CALLBACK(ext_yahoo_login_response) (yd->client_id,
3538                                YAHOO_LOGIN_SOCK, NULL);
3539                }
3540                return;
3541        }
3542
3543        FREE(ccd);
3544
3545        /* fd == NULL && error == 0 means connect was cancelled */
3546        if (!fd)
3547                return;
3548
3549        pkt = yahoo_packet_new(YAHOO_SERVICE_AUTH, YPACKET_STATUS_DEFAULT,
3550                yd->session_id);
3551        NOTICE(("Sending initial packet"));
3552
3553        yahoo_packet_hash(pkt, 1, yd->user);
3554
3555        yid = find_input_by_id_and_type(yd->client_id, YAHOO_CONNECTION_PAGER);
3556        yid->fd = fd;
3557
3558        yahoo_send_packet(yid, pkt, 0);
3559
3560        yahoo_packet_free(pkt);
3561
3562        yid->read_tag =
3563                YAHOO_CALLBACK(ext_yahoo_add_handler) (yid->yd->client_id,
3564                yid->fd, YAHOO_INPUT_READ, yid);
3565}
3566
3567void *yahoo_get_fd(int id)
3568{
3569        struct yahoo_input_data *yid =
3570                find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
3571        if (!yid)
3572                return 0;
3573        else
3574                return yid->fd;
3575}
3576
3577void yahoo_send_buzz(int id, const char *from, const char *who)
3578{
3579        yahoo_send_im(id, from, who, "<ding>", 1, 0);
3580}
3581
3582void yahoo_send_im(int id, const char *from, const char *who, const char *what,
3583        int utf8, int picture)
3584{
3585        struct yahoo_input_data *yid =
3586                find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
3587        struct yahoo_packet *pkt = NULL;
3588        struct yahoo_data *yd;
3589        char pic_str[10];
3590
3591        if (!yid)
3592                return;
3593
3594        yd = yid->yd;
3595
3596        pkt = yahoo_packet_new(YAHOO_SERVICE_MESSAGE, YAHOO_STATUS_OFFLINE,
3597                yd->session_id);
3598
3599        snprintf(pic_str, sizeof(pic_str), "%d", picture);
3600
3601        if (from && strcmp(from, yd->user))
3602                yahoo_packet_hash(pkt, 0, yd->user);
3603        yahoo_packet_hash(pkt, 1, from ? from : yd->user);
3604        yahoo_packet_hash(pkt, 5, who);
3605        yahoo_packet_hash(pkt, 14, what);
3606
3607        if (utf8)
3608                yahoo_packet_hash(pkt, 97, "1");
3609
3610        yahoo_packet_hash(pkt, 63, ";0");       /* imvironment name; or ;0 */
3611        yahoo_packet_hash(pkt, 64, "0");
3612        yahoo_packet_hash(pkt, 206, pic_str);
3613
3614        yahoo_send_packet(yid, pkt, 0);
3615
3616        yahoo_packet_free(pkt);
3617}
3618
3619void yahoo_send_typing(int id, const char *from, const char *who, int typ)
3620{
3621        struct yahoo_input_data *yid =
3622                find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
3623        struct yahoo_data *yd;
3624        struct yahoo_packet *pkt = NULL;
3625        if (!yid)
3626                return;
3627
3628        yd = yid->yd;
3629        pkt = yahoo_packet_new(YAHOO_SERVICE_NOTIFY, YPACKET_STATUS_NOTIFY,
3630                yd->session_id);
3631
3632        yahoo_packet_hash(pkt, 5, who);
3633        yahoo_packet_hash(pkt, 1, from ? from : yd->user);
3634        yahoo_packet_hash(pkt, 14, " ");
3635        yahoo_packet_hash(pkt, 13, typ ? "1" : "0");
3636        yahoo_packet_hash(pkt, 49, "TYPING");
3637
3638        yahoo_send_packet(yid, pkt, 0);
3639
3640        yahoo_packet_free(pkt);
3641}
3642
3643void yahoo_set_away(int id, enum yahoo_status state, const char *msg, int away)
3644{
3645        struct yahoo_input_data *yid =
3646                find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
3647        struct yahoo_data *yd;
3648        struct yahoo_packet *pkt = NULL;
3649        int old_status;
3650        char s[4];
3651
3652        if (!yid)
3653                return;
3654
3655        yd = yid->yd;
3656
3657        old_status = yd->current_status;
3658        yd->current_status = state;
3659
3660        /* Thank you libpurple :) */
3661        if (yd->current_status == YAHOO_STATUS_INVISIBLE) {
3662                pkt = yahoo_packet_new(YAHOO_SERVICE_Y6_VISIBLE_TOGGLE,
3663                        YAHOO_STATUS_AVAILABLE, 0);
3664                yahoo_packet_hash(pkt, 13, "2");
3665                yahoo_send_packet(yid, pkt, 0);
3666                yahoo_packet_free(pkt);
3667
3668                return;
3669        }
3670
3671        pkt = yahoo_packet_new(YAHOO_SERVICE_Y6_STATUS_UPDATE,
3672                yd->current_status, yd->session_id);
3673        snprintf(s, sizeof(s), "%d", yd->current_status);
3674        yahoo_packet_hash(pkt, 10, s);
3675        yahoo_packet_hash(pkt, 19, msg && state == YAHOO_STATUS_CUSTOM ? msg : "");
3676        yahoo_packet_hash(pkt, 47, (away == 2)? "2": (away) ?"1":"0");
3677        yahoo_send_packet(yid, pkt, 0);
3678        yahoo_packet_free(pkt);
3679
3680        if (old_status == YAHOO_STATUS_INVISIBLE) {
3681                pkt = yahoo_packet_new(YAHOO_SERVICE_Y6_VISIBLE_TOGGLE,
3682                        YAHOO_STATUS_AVAILABLE, 0);
3683                yahoo_packet_hash(pkt, 13, "1");
3684                yahoo_send_packet(yid, pkt, 0);
3685                yahoo_packet_free(pkt);
3686        }
3687}
3688
3689void yahoo_logoff(int id)
3690{
3691        struct yahoo_input_data *yid =
3692                find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
3693        struct yahoo_data *yd;
3694        struct yahoo_packet *pkt = NULL;
3695
3696        if (!yid)
3697                return;
3698        yd = yid->yd;
3699
3700        LOG(("yahoo_logoff: current status: %d", yd->current_status));
3701
3702        if (yd->current_status != -1 && 0) {
3703                /* Meh. Don't send this. The event handlers are not going to
3704                   get to do this so it'll just leak memory. And the TCP
3705                   connection reset will hopefully be clear enough. */
3706                pkt = yahoo_packet_new(YAHOO_SERVICE_LOGOFF,
3707                        YPACKET_STATUS_DEFAULT, yd->session_id);
3708                yd->current_status = -1;
3709
3710                if (pkt) {
3711                        yahoo_send_packet(yid, pkt, 0);
3712                        yahoo_packet_free(pkt);
3713                }
3714        }
3715
3716/*      do {
3717                yahoo_input_close(yid);
3718        } while((yid = find_input_by_id(id)));*/
3719
3720}
3721
3722void yahoo_get_list(int id)
3723{
3724        struct yahoo_input_data *yid =
3725                find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
3726        struct yahoo_data *yd;
3727        struct yahoo_packet *pkt = NULL;
3728
3729        if (!yid)
3730                return;
3731        yd = yid->yd;
3732
3733        pkt = yahoo_packet_new(YAHOO_SERVICE_LIST, YPACKET_STATUS_DEFAULT,
3734                yd->session_id);
3735        yahoo_packet_hash(pkt, 1, yd->user);
3736        if (pkt) {
3737                yahoo_send_packet(yid, pkt, 0);
3738                yahoo_packet_free(pkt);
3739        }
3740}
3741
3742static void _yahoo_http_connected(int id, void *fd, int error, void *data)
3743{
3744        struct yahoo_input_data *yid = data;
3745        if (fd == NULL || error) {
3746                inputs = y_list_remove(inputs, yid);
3747                FREE(yid);
3748                return;
3749        }
3750
3751        yid->fd = fd;
3752        yid->read_tag =
3753                YAHOO_CALLBACK(ext_yahoo_add_handler) (yid->yd->client_id, fd,
3754                YAHOO_INPUT_READ, yid);
3755}
3756
3757/* FIXME Get address book from address.yahoo.com instead */
3758void yahoo_get_yab(int id)
3759{
3760        struct yahoo_data *yd = find_conn_by_id(id);
3761        struct yahoo_input_data *yid;
3762        char url[1024];
3763        char buff[2048];
3764
3765        if (!yd)
3766                return;
3767
3768        yid = y_new0(struct yahoo_input_data, 1);
3769        yid->yd = yd;
3770        yid->type = YAHOO_CONNECTION_YAB;
3771
3772        LOG(("Sending request for Address Book"));
3773
3774        snprintf(url, 1024,
3775                "http://address.yahoo.com/yab/us?v=XM&prog=ymsgr&.intl=us"
3776                "&diffs=1&t=0&tags=short&rt=0&prog-ver=8.1.0.249&useutf8=1&legenc=codepage-1252");
3777
3778        snprintf(buff, sizeof(buff), "Y=%s; T=%s", yd->cookie_y, yd->cookie_t);
3779
3780        inputs = y_list_prepend(inputs, yid);
3781
3782        yahoo_http_get(yid->yd->client_id, url, buff, 0, 0,
3783                _yahoo_http_connected, yid);
3784}
3785
3786struct yahoo_post_data {
3787        struct yahoo_input_data *yid;
3788        char *data;
3789};
3790
3791static void _yahoo_http_post_connected(int id, void *fd, int error, void *data)
3792{
3793        struct yahoo_post_data *yad = data;
3794        struct yahoo_input_data *yid = yad->yid;
3795        char *buff = yad->data;
3796
3797        if (!fd) {
3798                inputs = y_list_remove(inputs, yid);
3799                FREE(yid);
3800                return;
3801        }
3802
3803        YAHOO_CALLBACK(ext_yahoo_write) (fd, buff, strlen(buff));
3804
3805        yid->fd = fd;
3806        yid->read_tag =
3807                YAHOO_CALLBACK(ext_yahoo_add_handler) (yid->yd->client_id, fd,
3808                YAHOO_INPUT_READ, yid);
3809
3810        FREE(buff);
3811        FREE(yad);
3812}
3813
3814/* FIXME This is also likely affected */
3815void yahoo_set_yab(int id, struct yab *yab)
3816{
3817        struct yahoo_post_data *yad = y_new0(struct yahoo_post_data, 1);
3818        struct yahoo_data *yd = find_conn_by_id(id);
3819        struct yahoo_input_data *yid;
3820        char url[1024];
3821        char buff[1024];
3822        char post[1024];
3823        int size = 0;
3824
3825        if (!yd)
3826                return;
3827
3828        yid = y_new0(struct yahoo_input_data, 1);
3829        yid->type = YAHOO_CONNECTION_YAB;
3830        yid->yd = yd;
3831
3832        if(yab->yid)
3833                size = snprintf(post, sizeof(post), "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
3834                        "<ab k=\"%s\" cc=\"%d\">"
3835                        "<ct id=\"%d\" e=\"1\" yi=\"%s\" nn=\"%s\" />"
3836                        "</ab>", yd->user, 9, yab->yid, /* Don't know why */
3837                        yab->id, yab->nname?yab->nname:"");
3838        else
3839                size = snprintf(post, sizeof(post), "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
3840                        "<ab k=\"%s\" cc=\"%d\">"
3841                        "<ct a=\"1\" yi=\"%s\" nn=\"%s\" />"
3842                        "</ab>", yd->user, 1,   /* Don't know why */
3843                        yab->id, yab->nname?yab->nname:"");
3844
3845        yad->yid = yid;
3846        yad->data = strdup(post);
3847
3848        strcpy(url, "http://address.yahoo.com/yab/us?v=XM&prog=ymsgr&.intl=us"
3849                "&sync=1&tags=short&noclear=1&useutf8=1&legenc=codepage-1252");
3850
3851        snprintf(buff, sizeof(buff), "Y=%s; T=%s", yd->cookie_y, yd->cookie_t);
3852
3853        inputs = y_list_prepend(inputs, yid);
3854
3855        yahoo_http_post(yid->yd->client_id, url, buff, size,
3856                _yahoo_http_post_connected, yad);
3857}
3858
3859void yahoo_set_identity_status(int id, const char *identity, int active)
3860{
3861        struct yahoo_input_data *yid =
3862                find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
3863        struct yahoo_data *yd;
3864        struct yahoo_packet *pkt = NULL;
3865
3866        if (!yid)
3867                return;
3868        yd = yid->yd;
3869
3870        pkt = yahoo_packet_new(active ? YAHOO_SERVICE_IDACT :
3871                YAHOO_SERVICE_IDDEACT, YPACKET_STATUS_DEFAULT, yd->session_id);
3872        yahoo_packet_hash(pkt, 3, identity);
3873        if (pkt) {
3874                yahoo_send_packet(yid, pkt, 0);
3875                yahoo_packet_free(pkt);
3876        }
3877}
3878
3879void yahoo_refresh(int id)
3880{
3881        struct yahoo_input_data *yid =
3882                find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
3883        struct yahoo_data *yd;
3884        struct yahoo_packet *pkt = NULL;
3885
3886        if (!yid)
3887                return;
3888        yd = yid->yd;
3889
3890        pkt = yahoo_packet_new(YAHOO_SERVICE_USERSTAT, YPACKET_STATUS_DEFAULT,
3891                yd->session_id);
3892        if (pkt) {
3893                yahoo_send_packet(yid, pkt, 0);
3894                yahoo_packet_free(pkt);
3895        }
3896}
3897
3898void yahoo_keepalive(int id)
3899{
3900        struct yahoo_input_data *yid =
3901                find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
3902        struct yahoo_data *yd;
3903        struct yahoo_packet *pkt = NULL;
3904        if (!yid)
3905                return;
3906        yd = yid->yd;
3907
3908        pkt = yahoo_packet_new(YAHOO_SERVICE_PING, YPACKET_STATUS_DEFAULT,
3909                yd->session_id);
3910        yahoo_send_packet(yid, pkt, 0);
3911        yahoo_packet_free(pkt);
3912}
3913
3914void yahoo_chat_keepalive(int id)
3915{
3916        struct yahoo_input_data *yid =
3917                find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
3918        struct yahoo_data *yd;
3919        struct yahoo_packet *pkt = NULL;
3920
3921        if (!yid)
3922                return;
3923
3924        yd = yid->yd;
3925
3926        pkt = yahoo_packet_new(YAHOO_SERVICE_CHATPING, YPACKET_STATUS_DEFAULT,
3927                yd->session_id);
3928        yahoo_send_packet(yid, pkt, 0);
3929        yahoo_packet_free(pkt);
3930}
3931
3932void yahoo_add_buddy(int id, const char *who, const char *group,
3933        const char *msg)
3934{
3935        struct yahoo_input_data *yid =
3936                find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
3937        struct yahoo_data *yd;
3938        struct yahoo_packet *pkt;
3939
3940        if (!yid)
3941                return;
3942        yd = yid->yd;
3943
3944        if (!yd->logged_in)
3945                return;
3946
3947        pkt = yahoo_packet_new(YAHOO_SERVICE_ADDBUDDY, YPACKET_STATUS_DEFAULT,
3948                yd->session_id);
3949        if (msg != NULL)        /* add message/request "it's me add me" */
3950                yahoo_packet_hash(pkt, 14, msg);
3951        else
3952                yahoo_packet_hash(pkt, 14, "");
3953        yahoo_packet_hash(pkt, 65, group);
3954        yahoo_packet_hash(pkt, 97, "1");
3955        yahoo_packet_hash(pkt, 1, yd->user);
3956        yahoo_packet_hash(pkt, 302, "319");
3957        yahoo_packet_hash(pkt, 300, "319");
3958        yahoo_packet_hash(pkt, 7, who);
3959        yahoo_packet_hash(pkt, 334, "0");
3960        yahoo_packet_hash(pkt, 301, "319");
3961        yahoo_packet_hash(pkt, 303, "319");
3962        yahoo_send_packet(yid, pkt, 0);
3963        yahoo_packet_free(pkt);
3964}
3965
3966void yahoo_remove_buddy(int id, const char *who, const char *group)
3967{
3968        struct yahoo_input_data *yid =
3969                find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
3970        struct yahoo_data *yd;
3971        struct yahoo_packet *pkt = NULL;
3972
3973        if (!yid)
3974                return;
3975        yd = yid->yd;
3976
3977        pkt = yahoo_packet_new(YAHOO_SERVICE_REMBUDDY, YPACKET_STATUS_DEFAULT,
3978                yd->session_id);
3979
3980        yahoo_packet_hash(pkt, 1, yd->user);
3981        yahoo_packet_hash(pkt, 7, who);
3982        yahoo_packet_hash(pkt, 65, group);
3983        yahoo_send_packet(yid, pkt, 0);
3984        yahoo_packet_free(pkt);
3985}
3986
3987void yahoo_confirm_buddy(int id, const char *who, int reject, const char *msg)
3988{
3989        struct yahoo_input_data *yid =
3990                find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
3991        struct yahoo_data *yd;
3992        struct yahoo_packet *pkt;
3993
3994        if (!yid)
3995                return;
3996        yd = yid->yd;
3997
3998        if (!yd->logged_in)
3999                return;
4000
4001        pkt = yahoo_packet_new(YAHOO_SERVICE_Y7_AUTHORIZATION,
4002                YPACKET_STATUS_DEFAULT, yd->session_id);
4003        yahoo_packet_hash(pkt, 1, yd->user);
4004        yahoo_packet_hash(pkt, 5, who);
4005        if (reject)
4006                yahoo_packet_hash(pkt, 13, "2");
4007        else {
4008                yahoo_packet_hash(pkt, 241, "0");
4009                yahoo_packet_hash(pkt, 13, "1");
4010        }
4011
4012        yahoo_packet_hash(pkt, 334, "0");
4013
4014        if (reject) {
4015                yahoo_packet_hash(pkt, 14, msg ? msg : "");
4016                yahoo_packet_hash(pkt, 97, "1");
4017        }
4018
4019        yahoo_send_packet(yid, pkt, 0);
4020        yahoo_packet_free(pkt);
4021}
4022
4023void yahoo_ignore_buddy(int id, const char *who, int unignore)
4024{
4025        struct yahoo_input_data *yid =
4026                find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
4027        struct yahoo_data *yd;
4028        struct yahoo_packet *pkt;
4029
4030        if (!yid)
4031                return;
4032        yd = yid->yd;
4033
4034        if (!yd->logged_in)
4035                return;
4036
4037        pkt = yahoo_packet_new(YAHOO_SERVICE_IGNORECONTACT,
4038                YPACKET_STATUS_DEFAULT, yd->session_id);
4039        yahoo_packet_hash(pkt, 1, yd->user);
4040        yahoo_packet_hash(pkt, 7, who);
4041        yahoo_packet_hash(pkt, 13, unignore ? "2" : "1");
4042        yahoo_send_packet(yid, pkt, 0);
4043        yahoo_packet_free(pkt);
4044}
4045
4046void yahoo_stealth_buddy(int id, const char *who, int unstealth)
4047{
4048        struct yahoo_input_data *yid =
4049                find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
4050        struct yahoo_data *yd;
4051        struct yahoo_packet *pkt;
4052
4053        if (!yid)
4054                return;
4055        yd = yid->yd;
4056
4057        if (!yd->logged_in)
4058                return;
4059
4060        pkt = yahoo_packet_new(YAHOO_SERVICE_STEALTH_PERM,
4061                YPACKET_STATUS_DEFAULT, yd->session_id);
4062        yahoo_packet_hash(pkt, 1, yd->user);
4063        yahoo_packet_hash(pkt, 7, who);
4064        yahoo_packet_hash(pkt, 31, unstealth ? "2" : "1");
4065        yahoo_packet_hash(pkt, 13, "2");
4066        yahoo_send_packet(yid, pkt, 0);
4067        yahoo_packet_free(pkt);
4068}
4069
4070void yahoo_change_buddy_group(int id, const char *who, const char *old_group,
4071        const char *new_group)
4072{
4073        struct yahoo_input_data *yid =
4074                find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
4075        struct yahoo_data *yd;
4076        struct yahoo_packet *pkt = NULL;
4077
4078        if (!yid)
4079                return;
4080        yd = yid->yd;
4081
4082        pkt = yahoo_packet_new(YAHOO_SERVICE_Y7_CHANGE_GROUP,
4083                YPACKET_STATUS_DEFAULT, yd->session_id);
4084        yahoo_packet_hash(pkt, 1, yd->user);
4085        yahoo_packet_hash(pkt, 302, "240");
4086        yahoo_packet_hash(pkt, 300, "240");
4087        yahoo_packet_hash(pkt, 7, who);
4088        yahoo_packet_hash(pkt, 224, old_group);
4089        yahoo_packet_hash(pkt, 264, new_group);
4090        yahoo_packet_hash(pkt, 301, "240");
4091        yahoo_packet_hash(pkt, 303, "240");
4092
4093        yahoo_send_packet(yid, pkt, 0);
4094        yahoo_packet_free(pkt);
4095}
4096
4097void yahoo_group_rename(int id, const char *old_group, const char *new_group)
4098{
4099        struct yahoo_input_data *yid =
4100                find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
4101        struct yahoo_data *yd;
4102        struct yahoo_packet *pkt = NULL;
4103
4104        if (!yid)
4105                return;
4106        yd = yid->yd;
4107
4108        pkt = yahoo_packet_new(YAHOO_SERVICE_GROUPRENAME,
4109                YPACKET_STATUS_DEFAULT, yd->session_id);
4110        yahoo_packet_hash(pkt, 1, yd->user);
4111        yahoo_packet_hash(pkt, 65, old_group);
4112        yahoo_packet_hash(pkt, 67, new_group);
4113
4114        yahoo_send_packet(yid, pkt, 0);
4115        yahoo_packet_free(pkt);
4116}
4117
4118void yahoo_conference_addinvite(int id, const char *from, const char *who,
4119        const char *room, const YList *members, const char *msg)
4120{
4121        struct yahoo_input_data *yid =
4122                find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
4123        struct yahoo_data *yd;
4124        struct yahoo_packet *pkt;
4125
4126        if (!yid)
4127                return;
4128        yd = yid->yd;
4129
4130        pkt = yahoo_packet_new(YAHOO_SERVICE_CONFADDINVITE,
4131                YPACKET_STATUS_DEFAULT, yd->session_id);
4132
4133        yahoo_packet_hash(pkt, 1, (from ? from : yd->user));
4134        yahoo_packet_hash(pkt, 51, who);
4135        yahoo_packet_hash(pkt, 57, room);
4136        yahoo_packet_hash(pkt, 58, msg);
4137        yahoo_packet_hash(pkt, 13, "0");
4138        for (; members; members = members->next) {
4139                yahoo_packet_hash(pkt, 52, (char *)members->data);
4140                yahoo_packet_hash(pkt, 53, (char *)members->data);
4141        }
4142        /* 52, 53 -> other members? */
4143
4144        yahoo_send_packet(yid, pkt, 0);
4145
4146        yahoo_packet_free(pkt);
4147}
4148
4149void yahoo_conference_invite(int id, const char *from, YList *who,
4150        const char *room, const char *msg)
4151{
4152        struct yahoo_input_data *yid =
4153                find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
4154        struct yahoo_data *yd;
4155        struct yahoo_packet *pkt;
4156
4157        if (!yid)
4158                return;
4159        yd = yid->yd;
4160
4161        pkt = yahoo_packet_new(YAHOO_SERVICE_CONFINVITE, YPACKET_STATUS_DEFAULT,
4162                yd->session_id);
4163
4164        yahoo_packet_hash(pkt, 1, (from ? from : yd->user));
4165        yahoo_packet_hash(pkt, 50, yd->user);
4166        for (; who; who = who->next) {
4167                yahoo_packet_hash(pkt, 52, (char *)who->data);
4168        }
4169        yahoo_packet_hash(pkt, 57, room);
4170        yahoo_packet_hash(pkt, 58, msg);
4171        yahoo_packet_hash(pkt, 13, "0");
4172
4173        yahoo_send_packet(yid, pkt, 0);
4174
4175        yahoo_packet_free(pkt);
4176}
4177
4178void yahoo_conference_logon(int id, const char *from, YList *who,
4179        const char *room)
4180{
4181        struct yahoo_input_data *yid =
4182                find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
4183        struct yahoo_data *yd;
4184        struct yahoo_packet *pkt;
4185
4186        if (!yid)
4187                return;
4188        yd = yid->yd;
4189
4190        pkt = yahoo_packet_new(YAHOO_SERVICE_CONFLOGON, YPACKET_STATUS_DEFAULT,
4191                yd->session_id);
4192
4193        yahoo_packet_hash(pkt, 1, (from ? from : yd->user));
4194        yahoo_packet_hash(pkt, 3, (from ? from : yd->user));
4195        yahoo_packet_hash(pkt, 57, room);
4196        for (; who; who = who->next)
4197                yahoo_packet_hash(pkt, 3, (char *)who->data);
4198
4199        yahoo_send_packet(yid, pkt, 0);
4200
4201        yahoo_packet_free(pkt);
4202}
4203
4204void yahoo_conference_decline(int id, const char *from, YList *who,
4205        const char *room, const char *msg)
4206{
4207        struct yahoo_input_data *yid =
4208                find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
4209        struct yahoo_data *yd;
4210        struct yahoo_packet *pkt;
4211
4212        if (!yid)
4213                return;
4214        yd = yid->yd;
4215
4216        pkt = yahoo_packet_new(YAHOO_SERVICE_CONFDECLINE,
4217                YPACKET_STATUS_DEFAULT, yd->session_id);
4218
4219        yahoo_packet_hash(pkt, 1, (from ? from : yd->user));
4220        yahoo_packet_hash(pkt, 3, (from ? from : yd->user));
4221        for (; who; who = who->next)
4222                yahoo_packet_hash(pkt, 3, (char *)who->data);
4223        yahoo_packet_hash(pkt, 57, room);
4224        yahoo_packet_hash(pkt, 14, msg);
4225
4226        yahoo_send_packet(yid, pkt, 0);
4227
4228        yahoo_packet_free(pkt);
4229}
4230
4231void yahoo_conference_logoff(int id, const char *from, YList *who,
4232        const char *room)
4233{
4234        struct yahoo_input_data *yid =
4235                find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
4236        struct yahoo_data *yd;
4237        struct yahoo_packet *pkt;
4238
4239        if (!yid)
4240                return;
4241        yd = yid->yd;
4242
4243        pkt = yahoo_packet_new(YAHOO_SERVICE_CONFLOGOFF, YPACKET_STATUS_DEFAULT,
4244                yd->session_id);
4245
4246        yahoo_packet_hash(pkt, 1, (from ? from : yd->user));
4247        yahoo_packet_hash(pkt, 3, (from ? from : yd->user));
4248        for (; who; who = who->next)
4249                yahoo_packet_hash(pkt, 3, (char *)who->data);
4250
4251        yahoo_packet_hash(pkt, 57, room);
4252
4253        yahoo_send_packet(yid, pkt, 0);
4254
4255        yahoo_packet_free(pkt);
4256}
4257
4258void yahoo_conference_message(int id, const char *from, YList *who,
4259        const char *room, const char *msg, int utf8)
4260{
4261        struct yahoo_input_data *yid =
4262                find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
4263        struct yahoo_data *yd;
4264        struct yahoo_packet *pkt;
4265
4266        if (!yid)
4267                return;
4268        yd = yid->yd;
4269
4270        pkt = yahoo_packet_new(YAHOO_SERVICE_CONFMSG, YPACKET_STATUS_DEFAULT,
4271                yd->session_id);
4272
4273        yahoo_packet_hash(pkt, 1, (from ? from : yd->user));
4274        yahoo_packet_hash(pkt, 53, (from ? from : yd->user));
4275        for (; who; who = who->next)
4276                yahoo_packet_hash(pkt, 53, (char *)who->data);
4277
4278        yahoo_packet_hash(pkt, 57, room);
4279        yahoo_packet_hash(pkt, 14, msg);
4280
4281        if (utf8)
4282                yahoo_packet_hash(pkt, 97, "1");
4283
4284        yahoo_send_packet(yid, pkt, 0);
4285
4286        yahoo_packet_free(pkt);
4287}
4288
4289void yahoo_get_chatrooms(int id, int chatroomid)
4290{
4291        struct yahoo_data *yd = find_conn_by_id(id);
4292        struct yahoo_input_data *yid;
4293        char url[1024];
4294        char buff[1024];
4295
4296        if (!yd)
4297                return;
4298
4299        yid = y_new0(struct yahoo_input_data, 1);
4300        yid->yd = yd;
4301        yid->type = YAHOO_CONNECTION_CHATCAT;
4302
4303        if (chatroomid == 0) {
4304                snprintf(url, 1024,
4305                        "http://insider.msg.yahoo.com/ycontent/?chatcat=0");
4306        } else {
4307                snprintf(url, 1024,
4308                        "http://insider.msg.yahoo.com/ycontent/?chatroom_%d=0",
4309                        chatroomid);
4310        }
4311
4312        snprintf(buff, sizeof(buff), "Y=%s; T=%s", yd->cookie_y, yd->cookie_t);
4313
4314        inputs = y_list_prepend(inputs, yid);
4315
4316        yahoo_http_get(yid->yd->client_id, url, buff, 0, 0,
4317                _yahoo_http_connected, yid);
4318}
4319
4320void yahoo_chat_logon(int id, const char *from, const char *room,
4321        const char *roomid)
4322{
4323        struct yahoo_input_data *yid =
4324                find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
4325        struct yahoo_data *yd;
4326        struct yahoo_packet *pkt;
4327
4328        if (!yid)
4329                return;
4330
4331        yd = yid->yd;
4332
4333        pkt = yahoo_packet_new(YAHOO_SERVICE_CHATONLINE, YPACKET_STATUS_DEFAULT,
4334                yd->session_id);
4335
4336        yahoo_packet_hash(pkt, 1, (from ? from : yd->user));
4337        yahoo_packet_hash(pkt, 109, yd->user);
4338        yahoo_packet_hash(pkt, 6, "abcde");
4339
4340        yahoo_send_packet(yid, pkt, 0);
4341
4342        yahoo_packet_free(pkt);
4343
4344        pkt = yahoo_packet_new(YAHOO_SERVICE_CHATJOIN, YPACKET_STATUS_DEFAULT,
4345                yd->session_id);
4346
4347        yahoo_packet_hash(pkt, 1, (from ? from : yd->user));
4348        yahoo_packet_hash(pkt, 104, room);
4349        yahoo_packet_hash(pkt, 129, roomid);
4350        yahoo_packet_hash(pkt, 62, "2");        /* ??? */
4351
4352        yahoo_send_packet(yid, pkt, 0);
4353
4354        yahoo_packet_free(pkt);
4355}
4356
4357void yahoo_chat_message(int id, const char *from, const char *room,
4358        const char *msg, const int msgtype, const int utf8)
4359{
4360        struct yahoo_input_data *yid =
4361                find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
4362        struct yahoo_data *yd;
4363        struct yahoo_packet *pkt;
4364        char buf[2];
4365
4366        if (!yid)
4367                return;
4368
4369        yd = yid->yd;
4370
4371        pkt = yahoo_packet_new(YAHOO_SERVICE_COMMENT, YPACKET_STATUS_DEFAULT,
4372                yd->session_id);
4373
4374        yahoo_packet_hash(pkt, 1, (from ? from : yd->user));
4375        yahoo_packet_hash(pkt, 104, room);
4376        yahoo_packet_hash(pkt, 117, msg);
4377
4378        snprintf(buf, sizeof(buf), "%d", msgtype);
4379        yahoo_packet_hash(pkt, 124, buf);
4380
4381        if (utf8)
4382                yahoo_packet_hash(pkt, 97, "1");
4383
4384        yahoo_send_packet(yid, pkt, 0);
4385
4386        yahoo_packet_free(pkt);
4387}
4388
4389void yahoo_chat_logoff(int id, const char *from)
4390{
4391        struct yahoo_input_data *yid =
4392                find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
4393        struct yahoo_data *yd;
4394        struct yahoo_packet *pkt;
4395
4396        if (!yid)
4397                return;
4398
4399        yd = yid->yd;
4400
4401        pkt = yahoo_packet_new(YAHOO_SERVICE_CHATLOGOUT, YPACKET_STATUS_DEFAULT,
4402                yd->session_id);
4403
4404        yahoo_packet_hash(pkt, 1, (from ? from : yd->user));
4405
4406        yahoo_send_packet(yid, pkt, 0);
4407
4408        yahoo_packet_free(pkt);
4409}
4410
4411void yahoo_buddyicon_request(int id, const char *who)
4412{
4413        struct yahoo_input_data *yid =
4414                find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
4415        struct yahoo_data *yd;
4416        struct yahoo_packet *pkt;
4417
4418        if (!yid)
4419                return;
4420
4421        yd = yid->yd;
4422
4423        pkt = yahoo_packet_new(YAHOO_SERVICE_PICTURE, YPACKET_STATUS_DEFAULT,
4424                0);
4425        yahoo_packet_hash(pkt, 4, yd->user);
4426        yahoo_packet_hash(pkt, 5, who);
4427        yahoo_packet_hash(pkt, 13, "1");
4428        yahoo_send_packet(yid, pkt, 0);
4429
4430        yahoo_packet_free(pkt);
4431}
4432
4433void yahoo_send_picture_info(int id, const char *who, const char *url,
4434        int checksum)
4435{
4436        struct yahoo_input_data *yid =
4437                find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
4438        struct yahoo_data *yd;
4439        struct yahoo_packet *pkt;
4440        char checksum_str[10];
4441
4442        if (!yid)
4443                return;
4444
4445        yd = yid->yd;
4446
4447        snprintf(checksum_str, sizeof(checksum_str), "%d", checksum);
4448
4449        pkt = yahoo_packet_new(YAHOO_SERVICE_PICTURE, YPACKET_STATUS_DEFAULT,
4450                0);
4451        yahoo_packet_hash(pkt, 1, yd->user);
4452        yahoo_packet_hash(pkt, 4, yd->user);
4453        yahoo_packet_hash(pkt, 5, who);
4454        yahoo_packet_hash(pkt, 13, "2");
4455        yahoo_packet_hash(pkt, 20, url);
4456        yahoo_packet_hash(pkt, 192, checksum_str);
4457        yahoo_send_packet(yid, pkt, 0);
4458
4459        yahoo_packet_free(pkt);
4460}
4461
4462void yahoo_send_picture_update(int id, const char *who, int type)
4463{
4464        struct yahoo_input_data *yid =
4465                find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
4466        struct yahoo_data *yd;
4467        struct yahoo_packet *pkt;
4468        char type_str[10];
4469
4470        if (!yid)
4471                return;
4472
4473        yd = yid->yd;
4474
4475        snprintf(type_str, sizeof(type_str), "%d", type);
4476
4477        pkt = yahoo_packet_new(YAHOO_SERVICE_PICTURE_UPDATE,
4478                YPACKET_STATUS_DEFAULT, 0);
4479        yahoo_packet_hash(pkt, 1, yd->user);
4480        yahoo_packet_hash(pkt, 5, who);
4481        yahoo_packet_hash(pkt, 206, type_str);
4482        yahoo_send_packet(yid, pkt, 0);
4483
4484        yahoo_packet_free(pkt);
4485}
4486
4487void yahoo_send_picture_checksum(int id, const char *who, int checksum)
4488{
4489        struct yahoo_input_data *yid =
4490                find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
4491        struct yahoo_data *yd;
4492        struct yahoo_packet *pkt;
4493        char checksum_str[10];
4494
4495        if (!yid)
4496                return;
4497
4498        yd = yid->yd;
4499
4500        snprintf(checksum_str, sizeof(checksum_str), "%d", checksum);
4501
4502        pkt = yahoo_packet_new(YAHOO_SERVICE_PICTURE_CHECKSUM,
4503                YPACKET_STATUS_DEFAULT, 0);
4504        yahoo_packet_hash(pkt, 1, yd->user);
4505        if (who != 0)
4506                yahoo_packet_hash(pkt, 5, who);
4507        yahoo_packet_hash(pkt, 192, checksum_str);
4508        yahoo_packet_hash(pkt, 212, "1");
4509        yahoo_send_packet(yid, pkt, 0);
4510
4511        yahoo_packet_free(pkt);
4512}
4513
4514void yahoo_webcam_close_feed(int id, const char *who)
4515{
4516        struct yahoo_input_data *yid =
4517                find_input_by_id_and_webcam_user(id, who);
4518
4519        if (yid)
4520                yahoo_input_close(yid);
4521}
4522
4523void yahoo_webcam_get_feed(int id, const char *who)
4524{
4525        struct yahoo_input_data *yid =
4526                find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
4527        struct yahoo_data *yd;
4528        struct yahoo_packet *pkt;
4529
4530        if (!yid)
4531                return;
4532
4533        /*
4534         * add the user to the queue.  this is a dirty hack, since
4535         * the yahoo server doesn't tell us who's key it's returning,
4536         * we have to just hope that it sends back keys in the same
4537         * order that we request them.
4538         * The queue is popped in yahoo_process_webcam_key
4539         */
4540        webcam_queue = y_list_append(webcam_queue, who ? strdup(who) : NULL);
4541
4542        yd = yid->yd;
4543
4544        pkt = yahoo_packet_new(YAHOO_SERVICE_WEBCAM, YPACKET_STATUS_DEFAULT,
4545                yd->session_id);
4546
4547        yahoo_packet_hash(pkt, 1, yd->user);
4548        if (who != NULL)
4549                yahoo_packet_hash(pkt, 5, who);
4550        yahoo_send_packet(yid, pkt, 0);
4551
4552        yahoo_packet_free(pkt);
4553}
4554
4555void yahoo_webcam_send_image(int id, unsigned char *image, unsigned int length,
4556        unsigned int timestamp)
4557{
4558        struct yahoo_input_data *yid =
4559                find_input_by_id_and_type(id, YAHOO_CONNECTION_WEBCAM);
4560        unsigned char *packet;
4561        unsigned char header_len = 13;
4562        unsigned int pos = 0;
4563
4564        if (!yid)
4565                return;
4566
4567        packet = y_new0(unsigned char, header_len);
4568
4569        packet[pos++] = header_len;
4570        packet[pos++] = 0;
4571        packet[pos++] = 5;      /* version byte?? */
4572        packet[pos++] = 0;
4573        pos += yahoo_put32(packet + pos, length);
4574        packet[pos++] = 2;      /* packet type, image */
4575        pos += yahoo_put32(packet + pos, timestamp);
4576        yahoo_add_to_send_queue(yid, packet, header_len);
4577        FREE(packet);
4578
4579        if (length)
4580                yahoo_add_to_send_queue(yid, image, length);
4581}
4582
4583void yahoo_webcam_accept_viewer(int id, const char *who, int accept)
4584{
4585        struct yahoo_input_data *yid =
4586                find_input_by_id_and_type(id, YAHOO_CONNECTION_WEBCAM);
4587        char *packet = NULL;
4588        char *data = NULL;
4589        unsigned char header_len = 13;
4590        unsigned int pos = 0;
4591        unsigned int len = 0;
4592
4593        if (!yid)
4594                return;
4595
4596        data = strdup("u=");
4597        data = y_string_append(data, (char *)who);
4598        data = y_string_append(data, "\r\n");
4599        len = strlen(data);
4600
4601        packet = y_new0(char, header_len + len);
4602        packet[pos++] = header_len;
4603        packet[pos++] = 0;
4604        packet[pos++] = 5;      /* version byte?? */
4605        packet[pos++] = 0;
4606        pos += yahoo_put32(packet + pos, len);
4607        packet[pos++] = 0;      /* packet type */
4608        pos += yahoo_put32(packet + pos, accept);
4609        memcpy(packet + pos, data, len);
4610        FREE(data);
4611        yahoo_add_to_send_queue(yid, packet, header_len + len);
4612        FREE(packet);
4613}
4614
4615void yahoo_webcam_invite(int id, const char *who)
4616{
4617        struct yahoo_input_data *yid =
4618                find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
4619        struct yahoo_packet *pkt;
4620
4621        if (!yid)
4622                return;
4623
4624        pkt = yahoo_packet_new(YAHOO_SERVICE_NOTIFY, YPACKET_STATUS_NOTIFY,
4625                yid->yd->session_id);
4626
4627        yahoo_packet_hash(pkt, 49, "WEBCAMINVITE");
4628        yahoo_packet_hash(pkt, 14, " ");
4629        yahoo_packet_hash(pkt, 13, "0");
4630        yahoo_packet_hash(pkt, 1, yid->yd->user);
4631        yahoo_packet_hash(pkt, 5, who);
4632        yahoo_send_packet(yid, pkt, 0);
4633
4634        yahoo_packet_free(pkt);
4635}
4636
4637static void yahoo_search_internal(int id, int t, const char *text, int g,
4638        int ar, int photo, int yahoo_only, int startpos, int total)
4639{
4640        struct yahoo_data *yd = find_conn_by_id(id);
4641        struct yahoo_input_data *yid;
4642        char url[1024];
4643        char buff[1024];
4644        char *ctext, *p;
4645
4646        if (!yd)
4647                return;
4648
4649        yid = y_new0(struct yahoo_input_data, 1);
4650        yid->yd = yd;
4651        yid->type = YAHOO_CONNECTION_SEARCH;
4652
4653        /*
4654           age range
4655           .ar=1 - 13-18, 2 - 18-25, 3 - 25-35, 4 - 35-50, 5 - 50-70, 6 - 70+
4656         */
4657
4658        snprintf(buff, sizeof(buff), "&.sq=%%20&.tt=%d&.ss=%d", total,
4659                startpos);
4660
4661        ctext = strdup(text);
4662        while ((p = strchr(ctext, ' ')))
4663                *p = '+';
4664
4665        snprintf(url, 1024,
4666                "http://members.yahoo.com/interests?.oc=m&.kw=%s&.sb=%d&.g=%d&.ar=0%s%s%s",
4667                ctext, t, g, photo ? "&.p=y" : "", yahoo_only ? "&.pg=y" : "",
4668                startpos ? buff : "");
4669
4670        FREE(ctext);
4671
4672        snprintf(buff, sizeof(buff), "Y=%s; T=%s", yd->cookie_y, yd->cookie_t);
4673
4674        inputs = y_list_prepend(inputs, yid);
4675        yahoo_http_get(yid->yd->client_id, url, buff, 0, 0,
4676                _yahoo_http_connected, yid);
4677}
4678
4679void yahoo_search(int id, enum yahoo_search_type t, const char *text,
4680        enum yahoo_search_gender g, enum yahoo_search_agerange ar, int photo,
4681        int yahoo_only)
4682{
4683        struct yahoo_input_data *yid =
4684                find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
4685        struct yahoo_search_state *yss;
4686
4687        if (!yid)
4688                return;
4689
4690        if (!yid->ys)
4691                yid->ys = y_new0(struct yahoo_search_state, 1);
4692
4693        yss = yid->ys;
4694
4695        FREE(yss->lsearch_text);
4696        yss->lsearch_type = t;
4697        yss->lsearch_text = strdup(text);
4698        yss->lsearch_gender = g;
4699        yss->lsearch_agerange = ar;
4700        yss->lsearch_photo = photo;
4701        yss->lsearch_yahoo_only = yahoo_only;
4702
4703        yahoo_search_internal(id, t, text, g, ar, photo, yahoo_only, 0, 0);
4704}
4705
4706void yahoo_search_again(int id, int start)
4707{
4708        struct yahoo_input_data *yid =
4709                find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
4710        struct yahoo_search_state *yss;
4711
4712        if (!yid || !yid->ys)
4713                return;
4714
4715        yss = yid->ys;
4716
4717        if (start == -1)
4718                start = yss->lsearch_nstart + yss->lsearch_nfound;
4719
4720        yahoo_search_internal(id, yss->lsearch_type, yss->lsearch_text,
4721                yss->lsearch_gender, yss->lsearch_agerange,
4722                yss->lsearch_photo, yss->lsearch_yahoo_only,
4723                start, yss->lsearch_ntotal);
4724}
4725
4726void yahoo_send_picture(int id, const char *name, unsigned long size,
4727        yahoo_get_fd_callback callback, void *data)
4728{
4729        /* Not Implemented */
4730}
4731
4732/* File Transfer */
4733static YList *active_file_transfers = NULL;
4734
4735enum {
4736        FT_STATE_HEAD = 1,
4737        FT_STATE_RECV,
4738        FT_STATE_RECV_START,
4739        FT_STATE_SEND
4740};
4741
4742struct send_file_data {
4743        int client_id;
4744        char *id;
4745        char *who;
4746        char *filename;
4747        char *ip_addr;
4748        char *token;
4749        int size;
4750
4751        struct yahoo_input_data *yid;
4752        int state;
4753
4754        yahoo_get_fd_callback callback;
4755        void *data;
4756};
4757
4758static char *yahoo_get_random(void)
4759{
4760        int i = 0;
4761        int r = 0;
4762        int c = 0;
4763        char out[25];
4764
4765        out[24] = '\0';
4766        out[23] = '$';
4767        out[22] = '$';
4768       
4769        for (i = 0; i < 22; i++) {
4770                if(r == 0)
4771                        r = rand();
4772
4773                c = r%61;
4774
4775                if(c<26)
4776                        out[i] = c + 'a';
4777                else if (c<52)
4778                        out[i] = c - 26 + 'A';
4779                else
4780                        out[i] = c - 52 + '0';
4781
4782                r /= 61;
4783        }
4784
4785        return strdup(out);
4786}
4787
4788static int _are_same_id(const void *sfd1, const void *id)
4789{
4790        return strcmp(((struct send_file_data *)sfd1)->id, (char *)id);
4791}
4792
4793static int _are_same_yid(const void *sfd1, const void *yid)
4794{
4795        if(((struct send_file_data *)sfd1)->yid == yid)
4796                return 0;
4797        else
4798                return 1;
4799}
4800
4801static struct send_file_data *yahoo_get_active_transfer(char *id)
4802{
4803        YList *l = y_list_find_custom(active_file_transfers, id,
4804                _are_same_id);
4805
4806        if(l)
4807                return (struct send_file_data *)l->data;
4808       
4809        return NULL;
4810}
4811
4812static struct send_file_data *yahoo_get_active_transfer_with_yid(void *yid)
4813{
4814        YList *l = y_list_find_custom(active_file_transfers, yid,
4815                _are_same_yid);
4816
4817        if(l)
4818                return (struct send_file_data *)l->data;
4819       
4820        return NULL;
4821}
4822
4823static void yahoo_add_active_transfer(struct send_file_data *sfd)
4824{
4825        active_file_transfers = y_list_prepend(active_file_transfers, sfd);
4826}
4827
4828static void yahoo_remove_active_transfer(struct send_file_data *sfd)
4829{
4830        if (sfd == NULL)
4831                return;
4832       
4833        active_file_transfers = y_list_remove(active_file_transfers, sfd);
4834        free(sfd->id);
4835        free(sfd->who);
4836        free(sfd->filename);
4837        free(sfd->ip_addr);
4838        FREE(sfd);
4839}
4840
4841static void _yahoo_ft_upload_connected(int id, void *fd, int error, void *data)
4842{
4843        struct send_file_data *sfd = data;
4844        struct yahoo_input_data *yid = sfd->yid;
4845
4846        if (!fd) {
4847                inputs = y_list_remove(inputs, yid);
4848                FREE(yid);
4849                return;
4850        }
4851
4852        sfd->callback(id, fd, error, sfd->data);
4853
4854        yid->fd = fd;
4855        yid->read_tag =
4856                YAHOO_CALLBACK(ext_yahoo_add_handler) (yid->yd->client_id, fd,
4857                YAHOO_INPUT_READ, yid);
4858}
4859
4860static void yahoo_file_transfer_upload(struct yahoo_data *yd,
4861        struct send_file_data *sfd)
4862{
4863        char url[256];
4864        char buff[4096];
4865        char *sender_enc = NULL, *recv_enc = NULL, *token_enc = NULL;
4866
4867        struct yahoo_input_data *yid = y_new0(struct yahoo_input_data, 1);
4868
4869        yid->yd = yd;
4870        yid->type = YAHOO_CONNECTION_FT;
4871
4872        inputs = y_list_prepend(inputs, yid);
4873        sfd->yid = yid;
4874        sfd->state = FT_STATE_SEND;
4875
4876        token_enc = yahoo_urlencode(sfd->token);
4877        sender_enc = yahoo_urlencode(yd->user);
4878        recv_enc = yahoo_urlencode(sfd->who);
4879
4880        snprintf(url, sizeof(url), 
4881                "http://%s/relay?token=%s&sender=%s&recver=%s", sfd->ip_addr,
4882                token_enc, sender_enc, recv_enc);
4883
4884        snprintf(buff, sizeof(buff), "T=%s; Y=%s", yd->cookie_t, yd->cookie_y);
4885
4886        yahoo_http_post(yd->client_id, url, buff, sfd->size,
4887                _yahoo_ft_upload_connected, sfd);
4888
4889        FREE(token_enc);
4890        FREE(sender_enc);
4891        FREE(recv_enc);
4892}
4893
4894static void yahoo_init_ft_recv(struct yahoo_data *yd,
4895        struct send_file_data *sfd)
4896{
4897        char url[256];
4898        char buff[1024];
4899        char *sender_enc = NULL, *recv_enc = NULL, *token_enc = NULL;
4900
4901        struct yahoo_input_data *yid = y_new0(struct yahoo_input_data, 1);
4902
4903        yid->yd = yd;
4904        yid->type = YAHOO_CONNECTION_FT;
4905
4906        inputs = y_list_prepend(inputs, yid);
4907        sfd->yid = yid;
4908        sfd->state = FT_STATE_HEAD;
4909
4910        token_enc = yahoo_urlencode(sfd->token);
4911        sender_enc = yahoo_urlencode(sfd->who);
4912        recv_enc = yahoo_urlencode(yd->user);
4913
4914        snprintf(url, sizeof(url), 
4915                "http://%s/relay?token=%s&sender=%s&recver=%s", sfd->ip_addr,
4916                token_enc, sender_enc, recv_enc);
4917
4918        snprintf(buff, sizeof(buff), "Y=%s; T=%s", yd->cookie_y, yd->cookie_t);
4919
4920        yahoo_http_head(yid->yd->client_id, url, buff, 0, NULL,
4921                _yahoo_http_connected, yid);
4922
4923        FREE(token_enc);
4924        FREE(sender_enc);
4925        FREE(recv_enc);
4926}
4927
4928static void yahoo_file_transfer_accept(struct yahoo_input_data *yid,
4929        struct send_file_data *sfd)
4930{
4931        struct yahoo_packet *pkt;
4932
4933        pkt = yahoo_packet_new(YAHOO_SERVICE_Y7_FILETRANSFERACCEPT,
4934                YPACKET_STATUS_DEFAULT, yid->yd->session_id);
4935
4936        yahoo_packet_hash(pkt, 1, yid->yd->user);
4937        yahoo_packet_hash(pkt, 5, sfd->who);
4938        yahoo_packet_hash(pkt, 265, sfd->id);
4939        yahoo_packet_hash(pkt, 27, sfd->filename);
4940        yahoo_packet_hash(pkt, 249, "3");
4941        yahoo_packet_hash(pkt, 251, sfd->token);
4942
4943        yahoo_send_packet(yid, pkt, 0);
4944
4945        yahoo_packet_free(pkt);
4946
4947        yahoo_init_ft_recv(yid->yd, sfd);
4948}
4949
4950static void yahoo_process_filetransferaccept(struct yahoo_input_data *yid,
4951        struct yahoo_packet *pkt)
4952{
4953        YList *l;
4954        struct send_file_data *sfd;
4955        char *id = NULL;
4956        char *token = NULL;
4957
4958        for (l = pkt->hash; l; l = l->next) {
4959                struct yahoo_pair *pair = l->data;
4960                switch (pair->key) {
4961                case 4:
4962                        /* who */
4963                        break;
4964                case 5:
4965                        /* Me... don't care */
4966                        break;
4967                case 249:
4968                        break;
4969                case 265:
4970                        id = pair->value;
4971                        break;
4972                case 251:
4973                        token = pair->value;
4974                        break;
4975                case 27:
4976                        /* filename */
4977                        break;
4978                }
4979        }
4980
4981        sfd = yahoo_get_active_transfer(id);
4982
4983        if (sfd) {
4984                sfd->token = strdup(token);
4985
4986                yahoo_file_transfer_upload(yid->yd, sfd);
4987        }
4988        else {
4989                YAHOO_CALLBACK(ext_yahoo_file_transfer_done)
4990                        (yid->yd->client_id, YAHOO_FILE_TRANSFER_UNKNOWN,
4991                        sfd ? sfd->data : NULL);
4992
4993                yahoo_remove_active_transfer(sfd);
4994        }
4995}
4996
4997static void yahoo_process_filetransferinfo(struct yahoo_input_data *yid,
4998        struct yahoo_packet *pkt)
4999{
5000        YList *l;
5001        char *id = NULL;
5002        char *token = NULL;
5003        char *ip_addr = NULL;
5004
5005        struct send_file_data *sfd;
5006
5007        for (l = pkt->hash; l; l = l->next) {
5008                struct yahoo_pair *pair = l->data;
5009                switch (pair->key) {
5010                case 1:
5011                case 4:
5012                        /* who */
5013                        break;
5014                case 5:
5015                        /* Me... don't care */
5016                        break;
5017                case 249:
5018                        break;
5019                case 265:
5020                        id = pair->value;
5021                        break;
5022                case 250:
5023                        ip_addr = pair->value;
5024                        break;
5025                case 251:
5026                        token = pair->value;
5027                        break;
5028                case 27:
5029                        /* filename */
5030                        break;
5031                }
5032        }
5033
5034        sfd = yahoo_get_active_transfer(id);
5035
5036        if (sfd) {
5037                sfd->token = strdup(token);
5038                sfd->ip_addr = strdup(ip_addr);
5039
5040                yahoo_file_transfer_accept(yid, sfd);
5041        }
5042        else {
5043                YAHOO_CALLBACK(ext_yahoo_file_transfer_done)
5044                        (yid->yd->client_id, YAHOO_FILE_TRANSFER_UNKNOWN,
5045                        sfd ? sfd->data : NULL);
5046
5047                yahoo_remove_active_transfer(sfd);
5048        }
5049}
5050
5051static void yahoo_send_filetransferinfo(struct yahoo_data *yd,
5052        struct send_file_data *sfd)
5053{
5054        struct yahoo_input_data *yid;
5055        struct yahoo_packet *pkt;
5056
5057        yid = find_input_by_id_and_type(yd->client_id, YAHOO_CONNECTION_PAGER);
5058        sfd->ip_addr = YAHOO_CALLBACK(ext_yahoo_get_ip_addr)("relay.yahoo.com");
5059
5060        if (!sfd->ip_addr) {
5061                YAHOO_CALLBACK(ext_yahoo_file_transfer_done)
5062                        (yd->client_id, YAHOO_FILE_TRANSFER_RELAY, sfd->data);
5063
5064                yahoo_remove_active_transfer(sfd);
5065
5066                return;
5067        }
5068
5069        pkt = yahoo_packet_new(YAHOO_SERVICE_Y7_FILETRANSFERINFO,
5070                YPACKET_STATUS_DEFAULT, yd->session_id);
5071
5072        yahoo_packet_hash(pkt, 1, yd->user);
5073        yahoo_packet_hash(pkt, 5, sfd->who);
5074        yahoo_packet_hash(pkt, 265, sfd->id);
5075        yahoo_packet_hash(pkt, 27, sfd->filename);
5076        yahoo_packet_hash(pkt, 249, "3");
5077        yahoo_packet_hash(pkt, 250, sfd->ip_addr);
5078
5079        yahoo_send_packet(yid, pkt, 0);
5080
5081        yahoo_packet_free(pkt);
5082}
5083
5084static void yahoo_process_filetransfer(struct yahoo_input_data *yid,
5085        struct yahoo_packet *pkt)
5086{
5087        YList *l;
5088        char *who = NULL;
5089        char *filename = NULL;
5090        char *msg = NULL;
5091        char *id = NULL;
5092        int action = 0;
5093        int size = 0;
5094        struct yahoo_data *yd = yid->yd;
5095
5096        struct send_file_data *sfd;
5097
5098        for (l = pkt->hash; l; l = l->next) {
5099                struct yahoo_pair *pair = l->data;
5100                switch (pair->key) {
5101                case 4:
5102                        who = pair->value;
5103                        break;
5104                case 5:
5105                        /* Me... don't care */
5106                        break;
5107                case 222:
5108                        action = atoi(pair->value);
5109                        break;
5110                case 265:
5111                        id = pair->value;
5112                        break;
5113                case 266: /* Don't know */
5114                        break;
5115                case 302: /* Start Data? */
5116                        break;
5117                case 300:
5118                        break;
5119                case 27:
5120                        filename = pair->value;
5121                        break;
5122                case 28:
5123                        size = atoi(pair->value);
5124                        break;
5125                case 14:
5126                        msg = pair->value;
5127                case 301: /* End Data? */
5128                        break;
5129                case 303:
5130                        break;
5131
5132                }
5133        }
5134
5135        if (action == YAHOO_FILE_TRANSFER_INIT) {
5136                /* Received a FT request from buddy */
5137                sfd = y_new0(struct send_file_data, 1);
5138       
5139                sfd->client_id = yd->client_id;
5140                sfd->id = strdup(id);
5141                sfd->who = strdup(who);
5142                sfd->filename = strdup(filename);
5143                sfd->size = size;
5144       
5145                yahoo_add_active_transfer(sfd);
5146
5147                YAHOO_CALLBACK(ext_yahoo_got_file) (yd->client_id, yd->user,
5148                        who, msg, filename, size, sfd->id);
5149        }
5150        else {
5151                /* Response to our request */
5152                sfd = yahoo_get_active_transfer(id);
5153
5154                if (sfd && action == YAHOO_FILE_TRANSFER_ACCEPT) {
5155                        yahoo_send_filetransferinfo(yd, sfd);
5156                }
5157                else if (!sfd || action == YAHOO_FILE_TRANSFER_REJECT) {
5158                        YAHOO_CALLBACK(ext_yahoo_file_transfer_done)
5159                                (yd->client_id, YAHOO_FILE_TRANSFER_REJECT,
5160                                sfd ? sfd->data : NULL);
5161
5162                        yahoo_remove_active_transfer(sfd);
5163                }
5164        }
5165}
5166
5167void yahoo_send_file(int id, const char *who, const char *msg,
5168        const char *name, unsigned long size,
5169        yahoo_get_fd_callback callback, void *data)
5170{
5171        struct yahoo_packet *pkt = NULL;
5172        char size_str[10];
5173        struct yahoo_input_data *yid;
5174        struct yahoo_data *yd;
5175        struct send_file_data *sfd;
5176       
5177        yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
5178        yd = find_conn_by_id(id);
5179        sfd = y_new0(struct send_file_data, 1);
5180
5181        sfd->client_id = id;
5182        sfd->id = yahoo_get_random();
5183        sfd->who = strdup(who);
5184        sfd->filename = strdup(name);
5185        sfd->size = size;
5186        sfd->callback = callback;
5187        sfd->data = data;
5188
5189        yahoo_add_active_transfer(sfd);
5190
5191        if (!yd)
5192                return;
5193
5194        pkt = yahoo_packet_new(YAHOO_SERVICE_Y7_FILETRANSFER,
5195                YPACKET_STATUS_DEFAULT, yd->session_id);
5196
5197        snprintf(size_str, sizeof(size_str), "%ld", size);
5198
5199        yahoo_packet_hash(pkt, 1, yd->user);
5200        yahoo_packet_hash(pkt, 5, who);
5201        yahoo_packet_hash(pkt, 265, sfd->id);
5202        yahoo_packet_hash(pkt, 222, "1");
5203        yahoo_packet_hash(pkt, 266, "1");
5204        yahoo_packet_hash(pkt, 302, "268");
5205        yahoo_packet_hash(pkt, 300, "268");
5206        yahoo_packet_hash(pkt, 27, name);
5207        yahoo_packet_hash(pkt, 28, size_str);
5208        yahoo_packet_hash(pkt, 301, "268");
5209        yahoo_packet_hash(pkt, 303, "268");
5210
5211        yahoo_send_packet(yid, pkt, 0);
5212
5213        yahoo_packet_free(pkt);
5214}
5215
5216void yahoo_send_file_transfer_response(int client_id, int response, char *id, void *data)
5217{
5218        struct yahoo_packet *pkt = NULL;
5219        char resp[2];
5220        struct yahoo_input_data *yid;
5221       
5222        struct send_file_data *sfd = yahoo_get_active_transfer(id);
5223
5224        sfd->data = data;
5225
5226        yid = find_input_by_id_and_type(client_id, YAHOO_CONNECTION_PAGER);
5227
5228        pkt = yahoo_packet_new(YAHOO_SERVICE_Y7_FILETRANSFER,
5229                YPACKET_STATUS_DEFAULT, yid->yd->session_id);
5230
5231        snprintf(resp, sizeof(resp), "%d", response);
5232
5233        yahoo_packet_hash(pkt, 1, yid->yd->user);
5234        yahoo_packet_hash(pkt, 5, sfd->who);
5235        yahoo_packet_hash(pkt, 265, sfd->id);
5236        yahoo_packet_hash(pkt, 222, resp);
5237
5238        yahoo_send_packet(yid, pkt, 0);
5239
5240        yahoo_packet_free(pkt);
5241
5242        if(response == YAHOO_FILE_TRANSFER_REJECT)
5243                yahoo_remove_active_transfer(sfd);
5244}
5245
5246static void yahoo_process_ft_connection(struct yahoo_input_data *yid, int over)
5247{
5248        struct send_file_data *sfd;
5249        struct yahoo_data *yd = yid->yd;
5250       
5251        sfd = yahoo_get_active_transfer_with_yid(yid);
5252
5253        if (!sfd) {
5254                LOG(("Something funny happened. yid %p has no sfd.\n", yid));
5255                return;
5256        }
5257
5258        /*
5259         * We want to handle only the complete data with HEAD since we don't
5260         * want a situation where both the GET and HEAD are active.
5261         * With SEND, we really can't do much with partial response
5262         */
5263        if ((sfd->state == FT_STATE_HEAD || sfd->state == FT_STATE_SEND) 
5264                        && !over)
5265                return;
5266
5267        if (sfd->state == FT_STATE_HEAD) {
5268                /* Do a GET */
5269                char url[256];
5270                char buff[1024];
5271                char *sender_enc = NULL, *recv_enc = NULL, *token_enc = NULL;
5272
5273                struct yahoo_input_data *yid_ft = 
5274                        y_new0(struct yahoo_input_data, 1);
5275       
5276                yid_ft->yd = yid->yd;
5277                yid_ft->type = YAHOO_CONNECTION_FT;
5278       
5279                inputs = y_list_prepend(inputs, yid_ft);
5280                sfd->yid = yid_ft;
5281                sfd->state = FT_STATE_RECV;
5282
5283                token_enc = yahoo_urlencode(sfd->token);
5284                sender_enc = yahoo_urlencode(sfd->who);
5285                recv_enc = yahoo_urlencode(yd->user);
5286       
5287                snprintf(url, sizeof(url), 
5288                        "http://%s/relay?token=%s&sender=%s&recver=%s", sfd->ip_addr,
5289                        token_enc, sender_enc, recv_enc);
5290
5291                snprintf(buff, sizeof(buff), "Y=%s; T=%s", yd->cookie_y,
5292                        yd->cookie_t);
5293
5294
5295                yahoo_http_get(yd->client_id, url, buff, 1, 1,
5296                        _yahoo_http_connected, yid_ft);
5297
5298                FREE(token_enc);
5299                FREE(sender_enc);
5300                FREE(recv_enc);
5301        }
5302        else if (sfd->state == FT_STATE_RECV || 
5303                sfd->state == FT_STATE_RECV_START) {
5304
5305                unsigned char *data_begin = NULL;
5306
5307                if (yid->rxlen == 0)
5308                        yahoo_remove_active_transfer(sfd);
5309
5310                if (sfd->state != FT_STATE_RECV_START &&
5311                        (data_begin = 
5312                                (unsigned char *)strstr((char *)yid->rxqueue,
5313                                "\r\n\r\n"))) {
5314
5315                        sfd->state = FT_STATE_RECV_START;
5316
5317                        yid->rxlen -= 4+(data_begin-yid->rxqueue)/sizeof(char);
5318                        data_begin += 4;
5319
5320                        if (yid->rxlen > 0)
5321                                YAHOO_CALLBACK(ext_yahoo_got_ft_data) 
5322                                        (yd->client_id, data_begin,
5323                                        yid->rxlen, sfd->data);
5324                }
5325                else if (sfd->state == FT_STATE_RECV_START)
5326                        YAHOO_CALLBACK(ext_yahoo_got_ft_data) (yd->client_id,
5327                                yid->rxqueue, yid->rxlen, sfd->data);
5328
5329                FREE(yid->rxqueue);
5330                yid->rxqueue = NULL;
5331                yid->rxlen = 0;
5332        }
5333        else if (sfd->state == FT_STATE_SEND) {
5334                /* Sent file completed */
5335                int len = 0;
5336                char *off = strstr((char *)yid->rxqueue, "Content-Length: ");
5337
5338                if (off) {
5339                        off += 16;
5340                        len = atoi(off);
5341                }
5342
5343                if (len < sfd->size)
5344                        YAHOO_CALLBACK(ext_yahoo_file_transfer_done)
5345                                (yd->client_id,
5346                                YAHOO_FILE_TRANSFER_FAILED, sfd->data);
5347                else
5348                        YAHOO_CALLBACK(ext_yahoo_file_transfer_done)
5349                                (yd->client_id,
5350                                YAHOO_FILE_TRANSFER_DONE, sfd->data);
5351
5352                yahoo_remove_active_transfer(sfd);
5353        }
5354}
5355
5356/* End File Transfer */
5357
5358enum yahoo_status yahoo_current_status(int id)
5359{
5360        struct yahoo_data *yd = find_conn_by_id(id);
5361        if (!yd)
5362                return YAHOO_STATUS_OFFLINE;
5363        return yd->current_status;
5364}
5365
5366const YList *yahoo_get_buddylist(int id)
5367{
5368        struct yahoo_data *yd = find_conn_by_id(id);
5369        if (!yd)
5370                return NULL;
5371        return yd->buddies;
5372}
5373
5374const YList *yahoo_get_ignorelist(int id)
5375{
5376        struct yahoo_data *yd = find_conn_by_id(id);
5377        if (!yd)
5378                return NULL;
5379        return yd->ignore;
5380}
5381
5382const YList *yahoo_get_identities(int id)
5383{
5384        struct yahoo_data *yd = find_conn_by_id(id);
5385        if (!yd)
5386                return NULL;
5387        return yd->identities;
5388}
5389
5390const char *yahoo_get_cookie(int id, const char *which)
5391{
5392        struct yahoo_data *yd = find_conn_by_id(id);
5393        if (!yd)
5394                return NULL;
5395        if (!strncasecmp(which, "y", 1))
5396                return yd->cookie_y;
5397        if (!strncasecmp(which, "b", 1))
5398                return yd->cookie_b;
5399        if (!strncasecmp(which, "t", 1))
5400                return yd->cookie_t;
5401        if (!strncasecmp(which, "c", 1))
5402                return yd->cookie_c;
5403        if (!strncasecmp(which, "login", 5))
5404                return yd->login_cookie;
5405        return NULL;
5406}
5407
5408const char *yahoo_get_profile_url(void)
5409{
5410        return profile_url;
5411}
Note: See TracBrowser for help on using the repository browser.