source: protocols/yahoo/libyahoo2.c @ e252d8c

Last change on this file since e252d8c was e252d8c, checked in by dequis <dx@…>, at 2014-09-27T14:54:35Z

RIP native win32 support (use cygwin instead)

It has been broken for a very long time and nobody cared about it.

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