source: protocols/yahoo/libyahoo2.c @ a6cd799

Last change on this file since a6cd799 was 25c4c78, checked in by dequis <dx@…>, at 2015-01-16T19:50:24Z

Fix compiler warnings on Cygwin and Mac OS X.

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