source: protocols/yahoo/libyahoo2.c @ 3fa5a8c

Last change on this file since 3fa5a8c was c6bf805, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-07-25T13:59:12Z

Some NULL pointer checks for libyahoo2 - this code's currently crashing
because BitlBee ignores file transfers.

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