source: protocols/yahoo/libyahoo2.c @ b38d399

Last change on this file since b38d399 was b38d399, checked in by dequis <dx@…>, at 2014-11-24T05:16:09Z

Use glib functions for base64 decoding/encoding

This fixes several coverity warnings about 'tainted data index sink' and
a fixme about thread safety in the old base64_decode implementation.

Had to adapt the code that used base64_encode_real:

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