source: protocols/yahoo/libyahoo2.c @ bc676ac

Last change on this file since bc676ac was d7edadf, checked in by Miklos Vajna <vmiklos@…>, at 2011-12-06T00:53:34Z

yahoo: unused-but-set-variables

  • Property mode set to 100644
File size: 125.2 KB
Line 
1/*
2 * libyahoo2: libyahoo2.c
3 *
4 * Some code copyright (C) 2002-2004, Philip S Tellis <philip.tellis AT gmx.net>
5 * YMSG16 code copyright (C) 2009,
6 *              Siddhesh Poyarekar <siddhesh dot poyarekar at gmail dot com>
7 *
8 * Yahoo Search copyright (C) 2003, Konstantin Klyagin <konst AT konst.org.ua>
9 *
10 * Much of this code was taken and adapted from the yahoo module for
11 * gaim released under the GNU GPL.  This code is also released under the
12 * GNU GPL.
13 *
14 * This code is derivitive of Gaim <http://gaim.sourceforge.net>
15 * copyright (C) 1998-1999, Mark Spencer <markster@marko.net>
16 *             1998-1999, Adam Fritzler <afritz@marko.net>
17 *             1998-2002, Rob Flynn <rob@marko.net>
18 *             2000-2002, Eric Warmenhoven <eric@warmenhoven.org>
19 *             2001-2002, Brian Macke <macke@strangelove.net>
20 *                  2001, Anand Biligiri S <abiligiri@users.sf.net>
21 *                  2001, Valdis Kletnieks
22 *                  2002, Sean Egan <bj91704@binghamton.edu>
23 *                  2002, Toby Gray <toby.gray@ntlworld.com>
24 *
25 * This library also uses code from other libraries, namely:
26 *     Portions from libfaim copyright 1998, 1999 Adam Fritzler
27 *     <afritz@auk.cx>
28 *     Portions of Sylpheed copyright 2000-2002 Hiroyuki Yamamoto
29 *     <hiro-y@kcn.ne.jp>
30 *
31 * YMSG16 authentication code based mostly on write-up at:
32 *      http://www.carbonize.co.uk/ymsg16.html
33 *
34 * This program is free software; you can redistribute it and/or modify
35 * it under the terms of the GNU General Public License as published by
36 * the Free Software Foundation; either version 2 of the License, or
37 * (at your option) any later version.
38 *
39 * This program is distributed in the hope that it will be useful,
40 * but WITHOUT ANY WARRANTY; without even the implied warranty of
41 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
42 * GNU General Public License for more details.
43 *
44 * You should have received a copy of the GNU General Public License
45 * along with this program; if not, write to the Free Software
46 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
47 *
48 */
49
50#ifndef _WIN32
51#include <unistd.h>
52#endif
53#include <errno.h>
54#include <stdio.h>
55#include <stdarg.h>
56
57#if STDC_HEADERS
58# include <string.h>
59#else
60# if !HAVE_STRCHR
61#  define strchr index
62#  define strrchr rindex
63# endif
64char *strchr (), *strrchr ();
65# if !HAVE_MEMCPY
66#  define memcpy(d, s, n) bcopy ((s), (d), (n))
67#  define memmove(d, s, n) bcopy ((s), (d), (n))
68# endif
69#endif
70
71#include <sys/types.h>
72
73#ifdef __MINGW32__
74# include <winsock2.h>
75#endif
76
77#include <stdlib.h>
78#include <ctype.h>
79
80#include "sha1.h"
81#include "md5.h"
82#include "yahoo2.h"
83#include "yahoo_httplib.h"
84#include "yahoo_util.h"
85#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        char *login, *passwd, *chal;
1812        char *url;
1813       
1814        login = g_strndup(yd->user, 3 * strlen(yd->user));
1815        http_encode(login);
1816        passwd = g_strndup(yd->password, 3 * strlen(yd->password));
1817        http_encode(passwd);
1818        chal = g_strndup(had->chal, 3 * strlen(had->chal));
1819        http_encode(chal);
1820       
1821        url = g_strdup_printf("https://login.yahoo.com/config/pwtoken_get?src=ymsgr&ts=%d&login=%s&passwd=%s&chal=%s",
1822                               (int) time(NULL), login, passwd, chal);
1823       
1824        http_dorequest_url(url, yahoo_https_auth_token_finish, had);
1825       
1826        g_free(url);
1827        g_free(chal);
1828        g_free(passwd);
1829        g_free(login);
1830}
1831
1832static void yahoo_https_auth_token_finish(struct http_request *req)
1833{
1834        struct yahoo_https_auth_data *had = req->data;
1835        struct yahoo_input_data *yid;
1836        struct yahoo_data *yd;
1837        int st;
1838       
1839        if (y_list_find(inputs, had->yid) == NULL)
1840                return;
1841       
1842        yid = had->yid;
1843        yd = yid->yd;
1844       
1845        if (req->status_code != 200) {
1846                YAHOO_CALLBACK(ext_yahoo_login_response) (yd->client_id, 2000 + req->status_code, NULL);
1847                goto fail;
1848        }
1849       
1850        if (sscanf(req->reply_body, "%d", &st) != 1 || st != 0) {
1851                YAHOO_CALLBACK(ext_yahoo_login_response) (yd->client_id, yahoo_https_status_parse(st), NULL);
1852                goto fail;
1853        }
1854       
1855        if ((had->token = yahoo_ha_find_key(req->reply_body, "ymsgr")) == NULL) {
1856                YAHOO_CALLBACK(ext_yahoo_login_response) (yd->client_id, 3001, NULL);
1857                goto fail;
1858        }
1859       
1860        yahoo_https_auth_init(had);
1861        return;
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        char *url;
1872       
1873        url = g_strdup_printf("https://login.yahoo.com/config/pwtoken_login?src=ymsgr&ts=%d&token=%s",
1874                              (int) time(NULL), had->token);
1875       
1876        http_dorequest_url(url, yahoo_https_auth_finish, had);
1877       
1878        g_free(url);
1879}
1880
1881static void yahoo_https_auth_finish(struct http_request *req)
1882{
1883        struct yahoo_https_auth_data *had = req->data;
1884        struct yahoo_input_data *yid;
1885        struct yahoo_data *yd;
1886        struct yahoo_packet *pack;
1887        char *crumb = NULL;
1888        int st;
1889       
1890        if (y_list_find(inputs, had->yid) == NULL)
1891                return;
1892       
1893        yid = had->yid;
1894        yd = yid->yd;
1895       
1896        md5_byte_t result[16];
1897        md5_state_t ctx;
1898       
1899        unsigned char yhash[32];
1900
1901        if (req->status_code != 200) {
1902                YAHOO_CALLBACK(ext_yahoo_login_response) (yd->client_id, 2000 + req->status_code, NULL);
1903                goto fail;
1904        }
1905       
1906        if (sscanf(req->reply_body, "%d", &st) != 1 || st != 0) {
1907                YAHOO_CALLBACK(ext_yahoo_login_response) (yd->client_id, yahoo_https_status_parse(st), NULL);
1908                goto fail;
1909        }
1910       
1911        if ((yd->cookie_y = yahoo_ha_find_key(req->reply_body, "Y")) == NULL ||
1912            (yd->cookie_t = yahoo_ha_find_key(req->reply_body, "T")) == NULL ||
1913            (crumb = yahoo_ha_find_key(req->reply_body, "crumb")) == NULL) {
1914                YAHOO_CALLBACK(ext_yahoo_login_response) (yd->client_id, 3002, NULL);
1915                goto fail;
1916        }
1917       
1918        md5_init(&ctx); 
1919        md5_append(&ctx, (unsigned char*) crumb, 11);
1920        md5_append(&ctx, (unsigned char*) had->chal, strlen(had->chal));
1921        md5_finish(&ctx, result);
1922        to_y64(yhash, result, 16);
1923
1924        pack = yahoo_packet_new(YAHOO_SERVICE_AUTHRESP, yd->initial_status, yd->session_id);
1925        yahoo_packet_hash(pack, 1, yd->user);
1926        yahoo_packet_hash(pack, 0, yd->user);
1927        yahoo_packet_hash(pack, 277, yd->cookie_y);
1928        yahoo_packet_hash(pack, 278, yd->cookie_t);
1929        yahoo_packet_hash(pack, 307, (char*) yhash);
1930        yahoo_packet_hash(pack, 244, "524223");
1931        yahoo_packet_hash(pack, 2, yd->user);
1932        yahoo_packet_hash(pack, 2, "1");
1933        yahoo_packet_hash(pack, 98, "us");
1934        yahoo_packet_hash(pack, 135, "7.5.0.647");
1935       
1936        yahoo_send_packet(yid, pack, 0);
1937               
1938        yahoo_packet_free(pack);
1939       
1940fail:
1941        g_free(crumb);
1942        g_free(had->token);
1943        g_free(had->chal);
1944        g_free(had);
1945}
1946
1947static void yahoo_process_auth(struct yahoo_input_data *yid,
1948        struct yahoo_packet *pkt)
1949{
1950        char *seed = NULL;
1951        char *sn = NULL;
1952        YList *l = pkt->hash;
1953        int m = 0;
1954        struct yahoo_data *yd = yid->yd;
1955
1956        while (l) {
1957                struct yahoo_pair *pair = l->data;
1958
1959                switch (pair->key) {
1960                case 94:
1961                        seed = pair->value;
1962                        break;
1963                case 1:
1964                        sn = pair->value;
1965                        break;
1966                case 13:
1967                        m = atoi(pair->value);
1968                        break;
1969                }
1970                l = l->next;
1971        }
1972
1973        if (!seed)
1974                return;
1975
1976        if (m==2)
1977                yahoo_https_auth(yid, seed, sn);
1978        else {
1979                /* call error */
1980                WARNING(("unknown auth type %d", m));
1981                YAHOO_CALLBACK(ext_yahoo_login_response) (yd->client_id,
1982                        YAHOO_LOGIN_UNKNOWN, NULL);
1983        }
1984}
1985
1986static void yahoo_process_auth_resp(struct yahoo_input_data *yid,
1987        struct yahoo_packet *pkt)
1988{
1989        struct yahoo_data *yd = yid->yd;
1990        char *url = NULL;
1991        int login_status = -1;
1992
1993        YList *l;
1994
1995        for (l = pkt->hash; l; l = l->next) {
1996                struct yahoo_pair *pair = l->data;
1997                if (pair->key == 0)
1998                        ; /* login_id */
1999                else if (pair->key == 1)
2000                        ; /* handle */
2001                else if (pair->key == 20)
2002                        url = pair->value;
2003                else if (pair->key == 66)
2004                        login_status = atoi(pair->value);
2005        }
2006
2007        if (pkt->status == YPACKET_STATUS_DISCONNECTED) {
2008                YAHOO_CALLBACK(ext_yahoo_login_response) (yd->client_id,
2009                        login_status, url);
2010                /*      yahoo_logoff(yd->client_id); */
2011        }
2012}
2013
2014static void yahoo_process_mail(struct yahoo_input_data *yid,
2015        struct yahoo_packet *pkt)
2016{
2017        struct yahoo_data *yd = yid->yd;
2018        char *who = NULL;
2019        char *email = NULL;
2020        char *subj = NULL;
2021        int count = 0;
2022        YList *l;
2023
2024        for (l = pkt->hash; l; l = l->next) {
2025                struct yahoo_pair *pair = l->data;
2026                if (pair->key == 9)
2027                        count = strtol(pair->value, NULL, 10);
2028                else if (pair->key == 43)
2029                        who = pair->value;
2030                else if (pair->key == 42)
2031                        email = pair->value;
2032                else if (pair->key == 18)
2033                        subj = pair->value;
2034                else
2035                        LOG(("key: %d => value: %s", pair->key, pair->value));
2036        }
2037
2038        if (who && email && subj) {
2039                char from[1024];
2040                snprintf(from, sizeof(from), "%s (%s)", who, email);
2041                YAHOO_CALLBACK(ext_yahoo_mail_notify) (yd->client_id, from,
2042                        subj, count);
2043        } else if (count > 0)
2044                YAHOO_CALLBACK(ext_yahoo_mail_notify) (yd->client_id, NULL,
2045                        NULL, count);
2046}
2047
2048static void yahoo_process_new_contact(struct yahoo_input_data *yid,
2049        struct yahoo_packet *pkt)
2050{
2051        struct yahoo_data *yd = yid->yd;
2052        char *me = NULL;
2053        char *who = NULL;
2054        char *msg = NULL;
2055        int online = -1;
2056
2057        YList *l;
2058
2059        for (l = pkt->hash; l; l = l->next) {
2060                struct yahoo_pair *pair = l->data;
2061                if (pair->key == 4)
2062                        who = pair->value;
2063                else if (pair->key == 5)
2064                        me = pair->value;
2065                else if (pair->key == 14)
2066                        msg = pair->value;
2067                else if (pair->key == 13)
2068                        online = strtol(pair->value, NULL, 10);
2069        }
2070
2071        if (who && online < 0)
2072                YAHOO_CALLBACK(ext_yahoo_contact_added) (yd->client_id, me, who,
2073                        msg);
2074        else if (online == 2)
2075                YAHOO_CALLBACK(ext_yahoo_rejected) (yd->client_id, who, msg);
2076}
2077
2078/* UNUSED? */
2079static void yahoo_process_contact(struct yahoo_input_data *yid,
2080        struct yahoo_packet *pkt)
2081{
2082        struct yahoo_data *yd = yid->yd;
2083        char *id = NULL;
2084        char *who = NULL;
2085        char *msg = NULL;
2086        char *name = NULL;
2087        int state = YAHOO_STATUS_AVAILABLE;
2088        int away = 0;
2089        int idle = 0;
2090        int mobile = 0;
2091
2092        YList *l;
2093
2094        for (l = pkt->hash; l; l = l->next) {
2095                struct yahoo_pair *pair = l->data;
2096                if (pair->key == 1)
2097                        id = pair->value;
2098                else if (pair->key == 3)
2099                        who = pair->value;
2100                else if (pair->key == 14)
2101                        msg = pair->value;
2102                else if (pair->key == 7)
2103                        name = pair->value;
2104                else if (pair->key == 10)
2105                        state = strtol(pair->value, NULL, 10);
2106                else if (pair->key == 15)
2107                        ; /* tm */
2108                else if (pair->key == 13)
2109                        ; /* online */
2110                else if (pair->key == 47)
2111                        away = strtol(pair->value, NULL, 10);
2112                else if (pair->key == 137)
2113                        idle = strtol(pair->value, NULL, 10);
2114                else if (pair->key == 60)
2115                        mobile = strtol(pair->value, NULL, 10);
2116
2117        }
2118
2119        if (id)
2120                YAHOO_CALLBACK(ext_yahoo_contact_added) (yd->client_id, id, who,
2121                        msg);
2122        else if (name)
2123                YAHOO_CALLBACK(ext_yahoo_status_changed) (yd->client_id, name,
2124                        state, msg, away, idle, mobile);
2125        else if (pkt->status == 0x07)
2126                YAHOO_CALLBACK(ext_yahoo_rejected) (yd->client_id, who, msg);
2127}
2128
2129static void yahoo_process_buddyadd(struct yahoo_input_data *yid,
2130        struct yahoo_packet *pkt)
2131{
2132        struct yahoo_data *yd = yid->yd;
2133        char *who = NULL;
2134        char *where = NULL;
2135        int status = 0;
2136
2137        struct yahoo_buddy *bud = NULL;
2138
2139        YList *l;
2140        for (l = pkt->hash; l; l = l->next) {
2141                struct yahoo_pair *pair = l->data;
2142                if (pair->key == 1)
2143                        ; /* Me... don't care */
2144                if (pair->key == 7)
2145                        who = pair->value;
2146                if (pair->key == 65)
2147                        where = pair->value;
2148                if (pair->key == 66)
2149                        status = strtol(pair->value, NULL, 10);
2150        }
2151
2152        if (!who)
2153                return;
2154        if (!where)
2155                where = "Unknown";
2156
2157        bud = y_new0(struct yahoo_buddy, 1);
2158        bud->id = strdup(who);
2159        bud->group = strdup(where);
2160        bud->real_name = NULL;
2161
2162        yd->buddies = y_list_append(yd->buddies, bud);
2163
2164#if 0
2165        /* BitlBee: This seems to be wrong in my experience. I think:
2166           status = 0: Success
2167           status = 2: Already on list
2168           status = 3: Doesn't exist
2169           status = 42: Invalid handle (possibly banned/reserved, I get it for
2170                        handles like joe or jjjjjj)
2171           Haven't seen others yet. But whenever the add is successful, there
2172           will be a separate "went online" packet when the auth. request is
2173           accepted. Couldn't find any test account that doesn't require auth.
2174           unfortunately (if there is even such a thing?) */
2175           
2176        /* A non-zero status (i've seen 2) seems to mean the buddy is already
2177         * added and is online */
2178        if (status) {
2179                LOG(("Setting online see packet for info"));
2180                yahoo_dump_unhandled(pkt);
2181                YAHOO_CALLBACK(ext_yahoo_status_changed) (yd->client_id, who,
2182                        YAHOO_STATUS_AVAILABLE, NULL, 0, 0, 0);
2183        }
2184#endif
2185        /* BitlBee: Need ACK of added buddy, if it was successful. */
2186        if (status == 0) {
2187                YList *tmp = y_list_append(NULL, bud);
2188                YAHOO_CALLBACK(ext_yahoo_got_buddies) (yd->client_id, tmp);
2189                y_list_free(tmp);
2190        }
2191}
2192
2193static void yahoo_process_buddydel(struct yahoo_input_data *yid,
2194        struct yahoo_packet *pkt)
2195{
2196        struct yahoo_data *yd = yid->yd;
2197        char *who = NULL;
2198        char *where = NULL;
2199        struct yahoo_buddy *bud;
2200
2201        YList *buddy;
2202
2203        YList *l;
2204        for (l = pkt->hash; l; l = l->next) {
2205                struct yahoo_pair *pair = l->data;
2206                if (pair->key == 1)
2207                        ; /* Me... don't care */
2208                else if (pair->key == 7)
2209                        who = pair->value;
2210                else if (pair->key == 65)
2211                        where = pair->value;
2212                else if (pair->key == 66)
2213                        ; /* unk_66 */
2214                else
2215                        DEBUG_MSG(("unknown key: %d = %s", pair->key,
2216                                        pair->value));
2217        }
2218
2219        if (!who || !where)
2220                return;
2221
2222        bud = y_new0(struct yahoo_buddy, 1);
2223        bud->id = strdup(who);
2224        bud->group = strdup(where);
2225
2226        buddy = y_list_find_custom(yd->buddies, bud, is_same_bud);
2227
2228        FREE(bud->id);
2229        FREE(bud->group);
2230        FREE(bud);
2231
2232        if (buddy) {
2233                bud = buddy->data;
2234                yd->buddies = y_list_remove_link(yd->buddies, buddy);
2235                y_list_free_1(buddy);
2236
2237                FREE(bud->id);
2238                FREE(bud->group);
2239                FREE(bud->real_name);
2240                FREE(bud);
2241
2242                bud = NULL;
2243        }
2244}
2245
2246static void yahoo_process_ignore(struct yahoo_input_data *yid,
2247        struct yahoo_packet *pkt)
2248{
2249        YList *l;
2250        for (l = pkt->hash; l; l = l->next) {
2251                struct yahoo_pair *pair = l->data;
2252                if (pair->key == 0)
2253                        ; /* who */
2254                if (pair->key == 1)
2255                        ; /* Me... don't care */
2256                if (pair->key == 13)    /* 1 == ignore, 2 == unignore */
2257                        ;
2258                if (pair->key == 66)
2259                        ; /* status */
2260        }
2261
2262        /*
2263         * status
2264         *      0  - ok
2265         *      2  - already in ignore list, could not add
2266         *      3  - not in ignore list, could not delete
2267         *      12 - is a buddy, could not add
2268         */
2269
2270/*      if(status)
2271                YAHOO_CALLBACK(ext_yahoo_error)(yd->client_id, who, 0, status);
2272*/
2273}
2274
2275static void yahoo_process_voicechat(struct yahoo_input_data *yid,
2276        struct yahoo_packet *pkt)
2277{
2278        char *who = NULL;
2279        char *me = NULL;
2280        char *room = NULL;
2281
2282        YList *l;
2283        for (l = pkt->hash; l; l = l->next) {
2284                struct yahoo_pair *pair = l->data;
2285                if (pair->key == 4)
2286                        who = pair->value;
2287                if (pair->key == 5)
2288                        me = pair->value;
2289                if (pair->key == 13)
2290                        ; /* voice room */
2291                if (pair->key == 57)
2292                        room = pair->value;
2293        }
2294
2295        NOTICE(("got voice chat invite from %s in %s to identity %s", who, room,
2296                        me));
2297        /*
2298         * send: s:0 1:me 5:who 57:room 13:1
2299         * ????  s:4 5:who 10:99 19:-1615114531
2300         * gotr: s:4 5:who 10:99 19:-1615114615
2301         * ????  s:1 5:me 4:who 57:room 13:3room
2302         * got:  s:1 5:me 4:who 57:room 13:1room
2303         * rej:  s:0 1:me 5:who 57:room 13:3
2304         * rejr: s:4 5:who 10:99 19:-1617114599
2305         */
2306}
2307
2308static void yahoo_process_ping(struct yahoo_input_data *yid,
2309        struct yahoo_packet *pkt)
2310{
2311        char *errormsg = NULL;
2312
2313        YList *l;
2314        for (l = pkt->hash; l; l = l->next) {
2315                struct yahoo_pair *pair = l->data;
2316                if (pair->key == 16)
2317                        errormsg = pair->value;
2318        }
2319
2320        NOTICE(("got ping packet"));
2321        YAHOO_CALLBACK(ext_yahoo_got_ping) (yid->yd->client_id, errormsg);
2322}
2323
2324static void yahoo_process_buddy_change_group(struct yahoo_input_data *yid,
2325        struct yahoo_packet *pkt)
2326{
2327        YList *l;
2328        char *me = NULL;
2329        char *who = NULL;
2330        char *old_group = NULL;
2331        char *new_group = NULL;
2332
2333        for (l = pkt->hash; l; l = l->next) {
2334                struct yahoo_pair *pair = l->data;
2335                if (pair->key == 1)
2336                        me = pair->value;
2337                if (pair->key == 7)
2338                        who = pair->value;
2339                if (pair->key == 224)
2340                        old_group = pair->value;
2341                if (pair->key == 264)
2342                        new_group = pair->value;
2343        }
2344
2345        YAHOO_CALLBACK(ext_yahoo_got_buddy_change_group) (yid->yd->client_id,
2346                me, who, old_group, new_group);
2347}
2348
2349static void _yahoo_webcam_get_server_connected(void *fd, int error, void *d)
2350{
2351        struct yahoo_input_data *yid = d;
2352        char *who = yid->wcm->user;
2353        char *data = NULL;
2354        char *packet = NULL;
2355        unsigned char magic_nr[] = { 0, 1, 0 };
2356        unsigned char header_len = 8;
2357        unsigned int len = 0;
2358        unsigned int pos = 0;
2359
2360        if (error || !fd) {
2361                FREE(who);
2362                FREE(yid);
2363                return;
2364        }
2365
2366        yid->fd = fd;
2367        inputs = y_list_prepend(inputs, yid);
2368
2369        /* send initial packet */
2370        if (who)
2371                data = strdup("<RVWCFG>");
2372        else
2373                data = strdup("<RUPCFG>");
2374        yahoo_add_to_send_queue(yid, data, strlen(data));
2375        FREE(data);
2376
2377        /* send data */
2378        if (who) {
2379                data = strdup("g=");
2380                data = y_string_append(data, who);
2381                data = y_string_append(data, "\r\n");
2382        } else {
2383                data = strdup("f=1\r\n");
2384        }
2385        len = strlen(data);
2386        packet = y_new0(char, header_len + len);
2387        packet[pos++] = header_len;
2388        memcpy(packet + pos, magic_nr, sizeof(magic_nr));
2389        pos += sizeof(magic_nr);
2390        pos += yahoo_put32(packet + pos, len);
2391        memcpy(packet + pos, data, len);
2392        pos += len;
2393        yahoo_add_to_send_queue(yid, packet, pos);
2394        FREE(packet);
2395        FREE(data);
2396
2397        yid->read_tag =
2398                YAHOO_CALLBACK(ext_yahoo_add_handler) (yid->yd->client_id, fd,
2399                YAHOO_INPUT_READ, yid);
2400}
2401
2402static void yahoo_webcam_get_server(struct yahoo_input_data *y, char *who,
2403        char *key)
2404{
2405        struct yahoo_input_data *yid = y_new0(struct yahoo_input_data, 1);
2406        struct yahoo_server_settings *yss = y->yd->server_settings;
2407
2408        yid->type = YAHOO_CONNECTION_WEBCAM_MASTER;
2409        yid->yd = y->yd;
2410        yid->wcm = y_new0(struct yahoo_webcam, 1);
2411        yid->wcm->user = who ? strdup(who) : NULL;
2412        yid->wcm->direction = who ? YAHOO_WEBCAM_DOWNLOAD : YAHOO_WEBCAM_UPLOAD;
2413        yid->wcm->key = strdup(key);
2414
2415        YAHOO_CALLBACK(ext_yahoo_connect_async) (yid->yd->client_id,
2416                yss->webcam_host, yss->webcam_port,
2417                _yahoo_webcam_get_server_connected, yid, 0);
2418
2419}
2420
2421static YList *webcam_queue = NULL;
2422static void yahoo_process_webcam_key(struct yahoo_input_data *yid,
2423        struct yahoo_packet *pkt)
2424{
2425        char *key = NULL;
2426        char *who = NULL;
2427
2428        YList *l;
2429        yahoo_dump_unhandled(pkt);
2430        for (l = pkt->hash; l; l = l->next) {
2431                struct yahoo_pair *pair = l->data;
2432                if (pair->key == 5)
2433                        ; /* me */
2434                if (pair->key == 61)
2435                        key = pair->value;
2436        }
2437
2438        l = webcam_queue;
2439        if (!l)
2440                return;
2441        who = l->data;
2442        webcam_queue = y_list_remove_link(webcam_queue, webcam_queue);
2443        y_list_free_1(l);
2444        yahoo_webcam_get_server(yid, who, key);
2445        FREE(who);
2446}
2447
2448static void yahoo_packet_process(struct yahoo_input_data *yid,
2449        struct yahoo_packet *pkt)
2450{
2451        DEBUG_MSG(("yahoo_packet_process: 0x%02x", pkt->service));
2452        switch (pkt->service) {
2453        case YAHOO_SERVICE_USERSTAT:
2454        case YAHOO_SERVICE_LOGON:
2455        case YAHOO_SERVICE_LOGOFF:
2456        case YAHOO_SERVICE_ISAWAY:
2457        case YAHOO_SERVICE_ISBACK:
2458        case YAHOO_SERVICE_GAMELOGON:
2459        case YAHOO_SERVICE_GAMELOGOFF:
2460        case YAHOO_SERVICE_IDACT:
2461        case YAHOO_SERVICE_IDDEACT:
2462        case YAHOO_SERVICE_Y6_STATUS_UPDATE:
2463        case YAHOO_SERVICE_Y8_STATUS:
2464                yahoo_process_status(yid, pkt);
2465                break;
2466        case YAHOO_SERVICE_NOTIFY:
2467                yahoo_process_notify(yid, pkt);
2468                break;
2469        case YAHOO_SERVICE_MESSAGE:
2470        case YAHOO_SERVICE_GAMEMSG:
2471        case YAHOO_SERVICE_SYSMESSAGE:
2472                yahoo_process_message(yid, pkt);
2473                break;
2474        case YAHOO_SERVICE_NEWMAIL:
2475                yahoo_process_mail(yid, pkt);
2476                break;
2477        case YAHOO_SERVICE_Y7_AUTHORIZATION:
2478                yahoo_process_new_contact(yid, pkt);
2479                break;
2480        case YAHOO_SERVICE_NEWCONTACT:
2481                yahoo_process_contact(yid, pkt);
2482                break;
2483        case YAHOO_SERVICE_LIST:
2484                yahoo_process_list(yid, pkt);
2485                break;
2486        case YAHOO_SERVICE_VERIFY:
2487                yahoo_process_verify(yid, pkt);
2488                break;
2489        case YAHOO_SERVICE_AUTH:
2490                yahoo_process_auth(yid, pkt);
2491                break;
2492        case YAHOO_SERVICE_AUTHRESP:
2493                yahoo_process_auth_resp(yid, pkt);
2494                break;
2495        case YAHOO_SERVICE_CONFINVITE:
2496        case YAHOO_SERVICE_CONFADDINVITE:
2497        case YAHOO_SERVICE_CONFDECLINE:
2498        case YAHOO_SERVICE_CONFLOGON:
2499        case YAHOO_SERVICE_CONFLOGOFF:
2500        case YAHOO_SERVICE_CONFMSG:
2501                yahoo_process_conference(yid, pkt);
2502                break;
2503        case YAHOO_SERVICE_CHATONLINE:
2504        case YAHOO_SERVICE_CHATGOTO:
2505        case YAHOO_SERVICE_CHATJOIN:
2506        case YAHOO_SERVICE_CHATLEAVE:
2507        case YAHOO_SERVICE_CHATEXIT:
2508        case YAHOO_SERVICE_CHATLOGOUT:
2509        case YAHOO_SERVICE_CHATPING:
2510        case YAHOO_SERVICE_COMMENT:
2511                yahoo_process_chat(yid, pkt);
2512                break;
2513        case YAHOO_SERVICE_P2PFILEXFER:
2514        case YAHOO_SERVICE_Y7_FILETRANSFER:
2515                yahoo_process_filetransfer(yid, pkt);
2516                break;
2517        case YAHOO_SERVICE_Y7_FILETRANSFERINFO:
2518                yahoo_process_filetransferinfo(yid, pkt);
2519                break;
2520        case YAHOO_SERVICE_Y7_FILETRANSFERACCEPT:
2521                yahoo_process_filetransferaccept(yid, pkt);
2522                break;
2523        case YAHOO_SERVICE_ADDBUDDY:
2524                yahoo_process_buddyadd(yid, pkt);
2525                break;
2526        case YAHOO_SERVICE_REMBUDDY:
2527                yahoo_process_buddydel(yid, pkt);
2528                break;
2529        case YAHOO_SERVICE_IGNORECONTACT:
2530                yahoo_process_ignore(yid, pkt);
2531                break;
2532        case YAHOO_SERVICE_VOICECHAT:
2533                yahoo_process_voicechat(yid, pkt);
2534                break;
2535        case YAHOO_SERVICE_WEBCAM:
2536                yahoo_process_webcam_key(yid, pkt);
2537                break;
2538        case YAHOO_SERVICE_PING:
2539                yahoo_process_ping(yid, pkt);
2540                break;
2541        case YAHOO_SERVICE_Y7_CHANGE_GROUP:
2542                yahoo_process_buddy_change_group(yid, pkt);
2543                break;
2544        case YAHOO_SERVICE_IDLE:
2545        case YAHOO_SERVICE_MAILSTAT:
2546        case YAHOO_SERVICE_CHATINVITE:
2547        case YAHOO_SERVICE_CALENDAR:
2548        case YAHOO_SERVICE_NEWPERSONALMAIL:
2549        case YAHOO_SERVICE_ADDIDENT:
2550        case YAHOO_SERVICE_ADDIGNORE:
2551        case YAHOO_SERVICE_GOTGROUPRENAME:
2552        case YAHOO_SERVICE_GROUPRENAME:
2553        case YAHOO_SERVICE_PASSTHROUGH2:
2554        case YAHOO_SERVICE_CHATLOGON:
2555        case YAHOO_SERVICE_CHATLOGOFF:
2556        case YAHOO_SERVICE_CHATMSG:
2557        case YAHOO_SERVICE_REJECTCONTACT:
2558        case YAHOO_SERVICE_PEERTOPEER:
2559                WARNING(("unhandled service 0x%02x", pkt->service));
2560                yahoo_dump_unhandled(pkt);
2561                break;
2562        case YAHOO_SERVICE_PICTURE:
2563                yahoo_process_picture(yid, pkt);
2564                break;
2565        case YAHOO_SERVICE_PICTURE_CHECKSUM:
2566                yahoo_process_picture_checksum(yid, pkt);
2567                break;
2568        case YAHOO_SERVICE_PICTURE_UPLOAD:
2569                yahoo_process_picture_upload(yid, pkt);
2570                break;
2571        case YAHOO_SERVICE_Y8_LIST:     /* Buddy List */
2572                yahoo_process_buddy_list(yid, pkt);
2573                break;
2574        default:
2575                WARNING(("unknown service 0x%02x", pkt->service));
2576                yahoo_dump_unhandled(pkt);
2577                break;
2578        }
2579}
2580
2581static struct yahoo_packet *yahoo_getdata(struct yahoo_input_data *yid)
2582{
2583        struct yahoo_packet *pkt;
2584        struct yahoo_data *yd = yid->yd;
2585        int pos = 0;
2586        int pktlen;
2587
2588        if (!yd)
2589                return NULL;
2590
2591        DEBUG_MSG(("rxlen is %d", yid->rxlen));
2592        if (yid->rxlen < YAHOO_PACKET_HDRLEN) {
2593                DEBUG_MSG(("len < YAHOO_PACKET_HDRLEN"));
2594                return NULL;
2595        }
2596
2597        pos += 4;               /* YMSG */
2598        pos += 2;
2599        pos += 2;
2600
2601        pktlen = yahoo_get16(yid->rxqueue + pos);
2602        pos += 2;
2603        DEBUG_MSG(("%d bytes to read, rxlen is %d", pktlen, yid->rxlen));
2604
2605        if (yid->rxlen < (YAHOO_PACKET_HDRLEN + pktlen)) {
2606                DEBUG_MSG(("len < YAHOO_PACKET_HDRLEN + pktlen"));
2607                return NULL;
2608        }
2609
2610        LOG(("reading packet"));
2611        yahoo_packet_dump(yid->rxqueue, YAHOO_PACKET_HDRLEN + pktlen);
2612
2613        pkt = yahoo_packet_new(0, 0, 0);
2614
2615        pkt->service = yahoo_get16(yid->rxqueue + pos);
2616        pos += 2;
2617        pkt->status = yahoo_get32(yid->rxqueue + pos);
2618        pos += 4;
2619        DEBUG_MSG(("Yahoo Service: 0x%02x Status: %d", pkt->service,
2620                        pkt->status));
2621        pkt->id = yahoo_get32(yid->rxqueue + pos);
2622        pos += 4;
2623
2624        yd->session_id = pkt->id;
2625
2626        yahoo_packet_read(pkt, yid->rxqueue + pos, pktlen);
2627
2628        yid->rxlen -= YAHOO_PACKET_HDRLEN + pktlen;
2629        DEBUG_MSG(("rxlen == %d, rxqueue == %p", yid->rxlen, yid->rxqueue));
2630        if (yid->rxlen > 0) {
2631                unsigned char *tmp = y_memdup(yid->rxqueue + YAHOO_PACKET_HDRLEN
2632                        + pktlen, yid->rxlen);
2633                FREE(yid->rxqueue);
2634                yid->rxqueue = tmp;
2635                DEBUG_MSG(("new rxlen == %d, rxqueue == %p", yid->rxlen,
2636                                yid->rxqueue));
2637        } else {
2638                DEBUG_MSG(("freed rxqueue == %p", yid->rxqueue));
2639                FREE(yid->rxqueue);
2640        }
2641
2642        return pkt;
2643}
2644
2645static struct yab *yahoo_yab_read(unsigned char *d, int len)
2646{
2647        char *st, *en;
2648        char *data = (char *)d;
2649        struct yab *yab = NULL;
2650
2651        data[len] = '\0';
2652
2653        DEBUG_MSG(("Got yab: %s", data));
2654        st = en = strstr(data, "e0=\"");
2655        if (st) {
2656                yab = y_new0(struct yab, 1);
2657
2658                st += strlen("e0=\"");
2659                en = strchr(st, '"');
2660                *en++ = '\0';
2661                yab->email = yahoo_xmldecode(st);
2662        }
2663
2664        if (!en)
2665                return NULL;
2666
2667        st = strstr(en, "id=\"");
2668        if (st) {
2669                st += strlen("id=\"");
2670                en = strchr(st, '"');
2671                *en++ = '\0';
2672                yab->yid = atoi(yahoo_xmldecode(st));
2673        }
2674
2675        st = strstr(en, "fn=\"");
2676        if (st) {
2677                st += strlen("fn=\"");
2678                en = strchr(st, '"');
2679                *en++ = '\0';
2680                yab->fname = yahoo_xmldecode(st);
2681        }
2682
2683        st = strstr(en, "ln=\"");
2684        if (st) {
2685                st += strlen("ln=\"");
2686                en = strchr(st, '"');
2687                *en++ = '\0';
2688                yab->lname = yahoo_xmldecode(st);
2689        }
2690
2691        st = strstr(en, "nn=\"");
2692        if (st) {
2693                st += strlen("nn=\"");
2694                en = strchr(st, '"');
2695                *en++ = '\0';
2696                yab->nname = yahoo_xmldecode(st);
2697        }
2698
2699        st = strstr(en, "yi=\"");
2700        if (st) {
2701                st += strlen("yi=\"");
2702                en = strchr(st, '"');
2703                *en++ = '\0';
2704                yab->id = yahoo_xmldecode(st);
2705        }
2706
2707        st = strstr(en, "hphone=\"");
2708        if (st) {
2709                st += strlen("hphone=\"");
2710                en = strchr(st, '"');
2711                *en++ = '\0';
2712                yab->hphone = yahoo_xmldecode(st);
2713        }
2714
2715        st = strstr(en, "wphone=\"");
2716        if (st) {
2717                st += strlen("wphone=\"");
2718                en = strchr(st, '"');
2719                *en++ = '\0';
2720                yab->wphone = yahoo_xmldecode(st);
2721        }
2722
2723        st = strstr(en, "mphone=\"");
2724        if (st) {
2725                st += strlen("mphone=\"");
2726                en = strchr(st, '"');
2727                *en++ = '\0';
2728                yab->mphone = yahoo_xmldecode(st);
2729        }
2730
2731        st = strstr(en, "dbid=\"");
2732        if (st) {
2733                st += strlen("dbid=\"");
2734                en = strchr(st, '"');
2735                *en++ = '\0';
2736                yab->dbid = atoi(st);
2737        }
2738
2739        return yab;
2740}
2741
2742static struct yab *yahoo_getyab(struct yahoo_input_data *yid)
2743{
2744        struct yab *yab = NULL;
2745        int pos = 0, end = 0;
2746        struct yahoo_data *yd = yid->yd;
2747
2748        if (!yd)
2749                return NULL;
2750
2751        do {
2752                DEBUG_MSG(("rxlen is %d", yid->rxlen));
2753
2754                if (yid->rxlen <= strlen("<ct"))
2755                        return NULL;
2756
2757                /* start with <ct */
2758                while (pos < yid->rxlen - strlen("<ct") + 1
2759                        && memcmp(yid->rxqueue + pos, "<ct", strlen("<ct")))
2760                        pos++;
2761
2762                if (pos >= yid->rxlen - 1)
2763                        return NULL;
2764
2765                end = pos + 2;
2766                /* end with > */
2767                while (end < yid->rxlen - strlen(">")
2768                        && memcmp(yid->rxqueue + end, ">", strlen(">")))
2769                        end++;
2770
2771                if (end >= yid->rxlen - 1)
2772                        return NULL;
2773
2774                yab = yahoo_yab_read(yid->rxqueue + pos, end + 2 - pos);
2775
2776                yid->rxlen -= end + 1;
2777                DEBUG_MSG(("rxlen == %d, rxqueue == %p", yid->rxlen,
2778                                yid->rxqueue));
2779                if (yid->rxlen > 0) {
2780                        unsigned char *tmp =
2781                                y_memdup(yid->rxqueue + end + 1, yid->rxlen);
2782                        FREE(yid->rxqueue);
2783                        yid->rxqueue = tmp;
2784                        DEBUG_MSG(("new rxlen == %d, rxqueue == %p", yid->rxlen,
2785                                        yid->rxqueue));
2786                } else {
2787                        DEBUG_MSG(("freed rxqueue == %p", yid->rxqueue));
2788                        FREE(yid->rxqueue);
2789                }
2790
2791        } while (!yab && end < yid->rxlen - 1);
2792
2793        return yab;
2794}
2795
2796static char *yahoo_getwebcam_master(struct yahoo_input_data *yid)
2797{
2798        unsigned int pos = 0;
2799        unsigned int len = 0;
2800        unsigned int status = 0;
2801        char *server = NULL;
2802        struct yahoo_data *yd = yid->yd;
2803
2804        if (!yid || !yd)
2805                return NULL;
2806
2807        DEBUG_MSG(("rxlen is %d", yid->rxlen));
2808
2809        len = yid->rxqueue[pos++];
2810        if (yid->rxlen < len)
2811                return NULL;
2812
2813        /* extract status (0 = ok, 6 = webcam not online) */
2814        status = yid->rxqueue[pos++];
2815
2816        if (status == 0) {
2817                pos += 2;       /* skip next 2 bytes */
2818                server = y_memdup(yid->rxqueue + pos, 16);
2819                pos += 16;
2820        } else if (status == 6) {
2821                YAHOO_CALLBACK(ext_yahoo_webcam_closed)
2822                        (yd->client_id, yid->wcm->user, 4);
2823        }
2824
2825        /* skip rest of the data */
2826
2827        yid->rxlen -= len;
2828        DEBUG_MSG(("rxlen == %d, rxqueue == %p", yid->rxlen, yid->rxqueue));
2829        if (yid->rxlen > 0) {
2830                unsigned char *tmp = y_memdup(yid->rxqueue + pos, yid->rxlen);
2831                FREE(yid->rxqueue);
2832                yid->rxqueue = tmp;
2833                DEBUG_MSG(("new rxlen == %d, rxqueue == %p", yid->rxlen,
2834                                yid->rxqueue));
2835        } else {
2836                DEBUG_MSG(("freed rxqueue == %p", yid->rxqueue));
2837                FREE(yid->rxqueue);
2838        }
2839
2840        return server;
2841}
2842
2843static int yahoo_get_webcam_data(struct yahoo_input_data *yid)
2844{
2845        unsigned char reason = 0;
2846        unsigned int pos = 0;
2847        unsigned int begin = 0;
2848        unsigned int end = 0;
2849        unsigned int closed = 0;
2850        unsigned char header_len = 0;
2851        char *who;
2852        int connect = 0;
2853        struct yahoo_data *yd = yid->yd;
2854
2855        if (!yd)
2856                return -1;
2857
2858        if (!yid->wcm || !yid->wcd || !yid->rxlen)
2859                return -1;
2860
2861        DEBUG_MSG(("rxlen is %d", yid->rxlen));
2862
2863        /* if we are not reading part of image then read header */
2864        if (!yid->wcd->to_read) {
2865                header_len = yid->rxqueue[pos++];
2866                yid->wcd->packet_type = 0;
2867
2868                if (yid->rxlen < header_len)
2869                        return 0;
2870
2871                if (header_len >= 8) {
2872                        reason = yid->rxqueue[pos++];
2873                        /* next 2 bytes should always be 05 00 */
2874                        pos += 2;
2875                        yid->wcd->data_size = yahoo_get32(yid->rxqueue + pos);
2876                        pos += 4;
2877                        yid->wcd->to_read = yid->wcd->data_size;
2878                }
2879                if (header_len >= 13) {
2880                        yid->wcd->packet_type = yid->rxqueue[pos++];
2881                        yid->wcd->timestamp = yahoo_get32(yid->rxqueue + pos);
2882                        pos += 4;
2883                }
2884
2885                /* skip rest of header */
2886                pos = header_len;
2887        }
2888
2889        begin = pos;
2890        pos += yid->wcd->to_read;
2891        if (pos > yid->rxlen)
2892                pos = yid->rxlen;
2893
2894        /* if it is not an image then make sure we have the whole packet */
2895        if (yid->wcd->packet_type != 0x02) {
2896                if ((pos - begin) != yid->wcd->data_size) {
2897                        yid->wcd->to_read = 0;
2898                        return 0;
2899                } else {
2900                        yahoo_packet_dump(yid->rxqueue + begin, pos - begin);
2901                }
2902        }
2903
2904        DEBUG_MSG(("packet type %.2X, data length %d", yid->wcd->packet_type,
2905                        yid->wcd->data_size));
2906
2907        /* find out what kind of packet we got */
2908        switch (yid->wcd->packet_type) {
2909        case 0x00:
2910                /* user requests to view webcam (uploading) */
2911                if (yid->wcd->data_size &&
2912                        yid->wcm->direction == YAHOO_WEBCAM_UPLOAD) {
2913                        end = begin;
2914                        while (end <= yid->rxlen && yid->rxqueue[end++] != 13) ;
2915                        if (end > begin) {
2916                                who = y_memdup(yid->rxqueue + begin,
2917                                        end - begin);
2918                                who[end - begin - 1] = 0;
2919                                YAHOO_CALLBACK(ext_yahoo_webcam_viewer) (yd->
2920                                        client_id, who + 2, 2);
2921                                FREE(who);
2922                        }
2923                }
2924
2925                if (yid->wcm->direction == YAHOO_WEBCAM_DOWNLOAD) {
2926                        /* timestamp/status field */
2927                        /* 0 = declined viewing permission */
2928                        /* 1 = accepted viewing permission */
2929                        if (yid->wcd->timestamp == 0) {
2930                                YAHOO_CALLBACK(ext_yahoo_webcam_closed) (yd->
2931                                        client_id, yid->wcm->user, 3);
2932                        }
2933                }
2934                break;
2935        case 0x01:              /* status packets?? */
2936                /* timestamp contains status info */
2937                /* 00 00 00 01 = we have data?? */
2938                break;
2939        case 0x02:              /* image data */
2940                YAHOO_CALLBACK(ext_yahoo_got_webcam_image) (yd->client_id,
2941                        yid->wcm->user, yid->rxqueue + begin,
2942                        yid->wcd->data_size, pos - begin, yid->wcd->timestamp);
2943                break;
2944        case 0x05:              /* response packets when uploading */
2945                if (!yid->wcd->data_size) {
2946                        YAHOO_CALLBACK(ext_yahoo_webcam_data_request) (yd->
2947                                client_id, yid->wcd->timestamp);
2948                }
2949                break;
2950        case 0x07:              /* connection is closing */
2951                switch (reason) {
2952                case 0x01:      /* user closed connection */
2953                        closed = 1;
2954                        break;
2955                case 0x0F:      /* user cancelled permission */
2956                        closed = 2;
2957                        break;
2958                }
2959                YAHOO_CALLBACK(ext_yahoo_webcam_closed) (yd->client_id,
2960                        yid->wcm->user, closed);
2961                break;
2962        case 0x0C:              /* user connected */
2963        case 0x0D:              /* user disconnected */
2964                if (yid->wcd->data_size) {
2965                        who = y_memdup(yid->rxqueue + begin, pos - begin + 1);
2966                        who[pos - begin] = 0;
2967                        if (yid->wcd->packet_type == 0x0C)
2968                                connect = 1;
2969                        else
2970                                connect = 0;
2971                        YAHOO_CALLBACK(ext_yahoo_webcam_viewer) (yd->client_id,
2972                                who, connect);
2973                        FREE(who);
2974                }
2975                break;
2976        case 0x13:              /* user data */
2977                /* i=user_ip (ip of the user we are viewing) */
2978                /* j=user_ext_ip (external ip of the user we */
2979                /*                are viewing) */
2980                break;
2981        case 0x17:              /* ?? */
2982                break;
2983        }
2984        yid->wcd->to_read -= pos - begin;
2985
2986        yid->rxlen -= pos;
2987        DEBUG_MSG(("rxlen == %d, rxqueue == %p", yid->rxlen, yid->rxqueue));
2988        if (yid->rxlen > 0) {
2989                unsigned char *tmp = y_memdup(yid->rxqueue + pos, yid->rxlen);
2990                FREE(yid->rxqueue);
2991                yid->rxqueue = tmp;
2992                DEBUG_MSG(("new rxlen == %d, rxqueue == %p", yid->rxlen,
2993                                yid->rxqueue));
2994        } else {
2995                DEBUG_MSG(("freed rxqueue == %p", yid->rxqueue));
2996                FREE(yid->rxqueue);
2997        }
2998
2999        /* If we read a complete packet return success */
3000        if (!yid->wcd->to_read)
3001                return 1;
3002
3003        return 0;
3004}
3005
3006int yahoo_write_ready(int id, void *fd, void *data)
3007{
3008        struct yahoo_input_data *yid = data;
3009        int len;
3010        struct data_queue *tx;
3011
3012        LOG(("write callback: id=%d fd=%p data=%p", id, fd, data));
3013        if (!yid || !yid->txqueues)
3014                return -2;
3015
3016        tx = yid->txqueues->data;
3017        LOG(("writing %d bytes", tx->len));
3018        len = yahoo_send_data(fd, tx->queue, MIN(1024, tx->len));
3019
3020        if (len == -1 && errno == EAGAIN)
3021                return 1;
3022
3023        if (len <= 0) {
3024                int e = errno;
3025                DEBUG_MSG(("len == %d (<= 0)", len));
3026                while (yid->txqueues) {
3027                        YList *l = yid->txqueues;
3028                        tx = l->data;
3029                        free(tx->queue);
3030                        free(tx);
3031                        yid->txqueues =
3032                                y_list_remove_link(yid->txqueues,
3033                                yid->txqueues);
3034                        y_list_free_1(l);
3035                }
3036                LOG(("yahoo_write_ready(%d, %p) len < 0", id, fd));
3037                YAHOO_CALLBACK(ext_yahoo_remove_handler) (id, yid->write_tag);
3038                yid->write_tag = 0;
3039                errno = e;
3040                return 0;
3041        }
3042
3043
3044        tx->len -= len;
3045        if (tx->len > 0) {
3046                unsigned char *tmp = y_memdup(tx->queue + len, tx->len);
3047                FREE(tx->queue);
3048                tx->queue = tmp;
3049        } else {
3050                YList *l = yid->txqueues;
3051                free(tx->queue);
3052                free(tx);
3053                yid->txqueues =
3054                        y_list_remove_link(yid->txqueues, yid->txqueues);
3055                y_list_free_1(l);
3056                /*
3057                   if(!yid->txqueues)
3058                   LOG(("yahoo_write_ready(%d, %d) !yxqueues", id, fd));
3059                 */
3060                if (!yid->txqueues) {
3061                        LOG(("yahoo_write_ready(%d, %p) !txqueues", id, fd));
3062                        YAHOO_CALLBACK(ext_yahoo_remove_handler) (id,
3063                                yid->write_tag);
3064                        yid->write_tag = 0;
3065                }
3066        }
3067
3068        return 1;
3069}
3070
3071static void yahoo_process_pager_connection(struct yahoo_input_data *yid,
3072        int over)
3073{
3074        struct yahoo_packet *pkt;
3075        struct yahoo_data *yd = yid->yd;
3076        int id = yd->client_id;
3077
3078        if (over)
3079                return;
3080
3081        while (find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER)
3082                && (pkt = yahoo_getdata(yid)) != NULL) {
3083
3084                yahoo_packet_process(yid, pkt);
3085
3086                yahoo_packet_free(pkt);
3087        }
3088}
3089
3090static void yahoo_process_chatcat_connection(struct yahoo_input_data *yid,
3091        int over)
3092{
3093        if (over)
3094                return;
3095
3096        if (strstr((char *)yid->rxqueue + (yid->rxlen - 20), "</content>")) {
3097                YAHOO_CALLBACK(ext_yahoo_chat_cat_xml) (yid->yd->client_id,
3098                        (char *)yid->rxqueue);
3099        }
3100}
3101
3102static void yahoo_process_yab_connection(struct yahoo_input_data *yid, int over)
3103{
3104        struct yahoo_data *yd = yid->yd;
3105        struct yab *yab;
3106        YList *buds;
3107        int changed = 0;
3108        int id = yd->client_id;
3109        int yab_used = 0;
3110
3111        LOG(("Got data for YAB"));
3112
3113        if (over)
3114                return;
3115
3116        while (find_input_by_id_and_type(id, YAHOO_CONNECTION_YAB)
3117                && (yab = yahoo_getyab(yid)) != NULL) {
3118                if (!yab->id)
3119                        continue;
3120
3121                changed = 1;
3122                yab_used = 0;
3123                for (buds = yd->buddies; buds; buds = buds->next) {
3124                        struct yahoo_buddy *bud = buds->data;
3125                        if (!strcmp(bud->id, yab->id)) {
3126                                yab_used = 1;
3127                                bud->yab_entry = yab;
3128                                if (yab->nname) {
3129                                        bud->real_name = strdup(yab->nname);
3130                                } else if (yab->fname && yab->lname) {
3131                                        bud->real_name = y_new0(char,
3132                                                strlen(yab->fname) +
3133                                                strlen(yab->lname) + 2);
3134                                        sprintf(bud->real_name, "%s %s",
3135                                                yab->fname, yab->lname);
3136                                } else if (yab->fname) {
3137                                        bud->real_name = strdup(yab->fname);
3138                                }
3139                                break;  /* for */
3140                        }
3141                }
3142
3143                if (!yab_used) {
3144                        FREE(yab->fname);
3145                        FREE(yab->lname);
3146                        FREE(yab->nname);
3147                        FREE(yab->id);
3148                        FREE(yab->email);
3149                        FREE(yab->hphone);
3150                        FREE(yab->wphone);
3151                        FREE(yab->mphone);
3152                        FREE(yab);
3153                }
3154
3155        }
3156
3157        if (changed)
3158                YAHOO_CALLBACK(ext_yahoo_got_buddies) (yd->client_id,
3159                        yd->buddies);
3160}
3161
3162static void yahoo_process_search_connection(struct yahoo_input_data *yid,
3163        int over)
3164{
3165        struct yahoo_found_contact *yct = NULL;
3166        char *p = (char *)yid->rxqueue, *np, *cp;
3167        int k, n;
3168        int start = 0, found = 0, total = 0;
3169        YList *contacts = NULL;
3170        struct yahoo_input_data *pyid =
3171                find_input_by_id_and_type(yid->yd->client_id,
3172                YAHOO_CONNECTION_PAGER);
3173
3174        if (!over || !pyid)
3175                return;
3176
3177        if (p && (p = strstr(p, "\r\n\r\n"))) {
3178                p += 4;
3179
3180                for (k = 0; (p = strchr(p, 4)) && (k < 4); k++) {
3181                        p++;
3182                        n = atoi(p);
3183                        switch (k) {
3184                        case 0:
3185                                found = pyid->ys->lsearch_nfound = n;
3186                                break;
3187                        case 2:
3188                                start = pyid->ys->lsearch_nstart = n;
3189                                break;
3190                        case 3:
3191                                total = pyid->ys->lsearch_ntotal = n;
3192                                break;
3193                        }
3194                }
3195
3196                if (p)
3197                        p++;
3198
3199                k = 0;
3200                while (p && *p) {
3201                        cp = p;
3202                        np = strchr(p, 4);
3203
3204                        if (!np)
3205                                break;
3206                        *np = 0;
3207                        p = np + 1;
3208
3209                        switch (k++) {
3210                        case 1:
3211                                if (strlen(cp) > 2
3212                                        && y_list_length(contacts) < total) {
3213                                        yct = y_new0(struct yahoo_found_contact,
3214                                                1);
3215                                        contacts = y_list_append(contacts, yct);
3216                                        yct->id = cp + 2;
3217                                } else {
3218                                        *p = 0;
3219                                }
3220                                break;
3221                        case 2:
3222                                yct->online = !strcmp(cp, "2") ? 1 : 0;
3223                                break;
3224                        case 3:
3225                                yct->gender = cp;
3226                                break;
3227                        case 4:
3228                                yct->age = atoi(cp);
3229                                break;
3230                        case 5:
3231                                /* not worth the context switch for strcmp */
3232                                if (cp[0] != '\005' || cp[1] != '\000')
3233                                        yct->location = cp;
3234                                k = 0;
3235                                break;
3236                        }
3237                }
3238        }
3239
3240        YAHOO_CALLBACK(ext_yahoo_got_search_result) (yid->yd->client_id, found,
3241                start, total, contacts);
3242
3243        while (contacts) {
3244                YList *node = contacts;
3245                contacts = y_list_remove_link(contacts, node);
3246                free(node->data);
3247                y_list_free_1(node);
3248        }
3249}
3250
3251static void _yahoo_webcam_connected(void *fd, int error, void *d)
3252{
3253        struct yahoo_input_data *yid = d;
3254        struct yahoo_webcam *wcm = yid->wcm;
3255        struct yahoo_data *yd = yid->yd;
3256        char conn_type[100];
3257        char *data = NULL;
3258        char *packet = NULL;
3259        unsigned char magic_nr[] = { 1, 0, 0, 0, 1 };
3260        unsigned header_len = 0;
3261        unsigned int len = 0;
3262        unsigned int pos = 0;
3263
3264        if (error || !fd) {
3265                FREE(yid);
3266                return;
3267        }
3268
3269        yid->fd = fd;
3270        inputs = y_list_prepend(inputs, yid);
3271
3272        LOG(("Connected"));
3273        /* send initial packet */
3274        switch (wcm->direction) {
3275        case YAHOO_WEBCAM_DOWNLOAD:
3276                data = strdup("<REQIMG>");
3277                break;
3278        case YAHOO_WEBCAM_UPLOAD:
3279                data = strdup("<SNDIMG>");
3280                break;
3281        default:
3282                return;
3283        }
3284        yahoo_add_to_send_queue(yid, data, strlen(data));
3285        FREE(data);
3286
3287        /* send data */
3288        switch (wcm->direction) {
3289        case YAHOO_WEBCAM_DOWNLOAD:
3290                header_len = 8;
3291                data = strdup("a=2\r\nc=us\r\ne=21\r\nu=");
3292                data = y_string_append(data, yd->user);
3293                data = y_string_append(data, "\r\nt=");
3294                data = y_string_append(data, wcm->key);
3295                data = y_string_append(data, "\r\ni=");
3296                data = y_string_append(data, wcm->my_ip);
3297                data = y_string_append(data, "\r\ng=");
3298                data = y_string_append(data, wcm->user);
3299                data = y_string_append(data, "\r\no=w-2-5-1\r\np=");
3300                snprintf(conn_type, sizeof(conn_type), "%d", wcm->conn_type);
3301                data = y_string_append(data, conn_type);
3302                data = y_string_append(data, "\r\n");
3303                break;
3304        case YAHOO_WEBCAM_UPLOAD:
3305                header_len = 13;
3306                data = strdup("a=2\r\nc=us\r\nu=");
3307                data = y_string_append(data, yd->user);
3308                data = y_string_append(data, "\r\nt=");
3309                data = y_string_append(data, wcm->key);
3310                data = y_string_append(data, "\r\ni=");
3311                data = y_string_append(data, wcm->my_ip);
3312                data = y_string_append(data, "\r\no=w-2-5-1\r\np=");
3313                snprintf(conn_type, sizeof(conn_type), "%d", wcm->conn_type);
3314                data = y_string_append(data, conn_type);
3315                data = y_string_append(data, "\r\nb=");
3316                data = y_string_append(data, wcm->description);
3317                data = y_string_append(data, "\r\n");
3318                break;
3319        }
3320
3321        len = strlen(data);
3322        packet = y_new0(char, header_len + len);
3323        packet[pos++] = header_len;
3324        packet[pos++] = 0;
3325        switch (wcm->direction) {
3326        case YAHOO_WEBCAM_DOWNLOAD:
3327                packet[pos++] = 1;
3328                packet[pos++] = 0;
3329                break;
3330        case YAHOO_WEBCAM_UPLOAD:
3331                packet[pos++] = 5;
3332                packet[pos++] = 0;
3333                break;
3334        }
3335
3336        pos += yahoo_put32(packet + pos, len);
3337        if (wcm->direction == YAHOO_WEBCAM_UPLOAD) {
3338                memcpy(packet + pos, magic_nr, sizeof(magic_nr));
3339                pos += sizeof(magic_nr);
3340        }
3341        memcpy(packet + pos, data, len);
3342        yahoo_add_to_send_queue(yid, packet, header_len + len);
3343        FREE(packet);
3344        FREE(data);
3345
3346        yid->read_tag =
3347                YAHOO_CALLBACK(ext_yahoo_add_handler) (yid->yd->client_id,
3348                yid->fd, YAHOO_INPUT_READ, yid);
3349}
3350
3351static void yahoo_webcam_connect(struct yahoo_input_data *y)
3352{
3353        struct yahoo_webcam *wcm = y->wcm;
3354        struct yahoo_input_data *yid;
3355
3356        if (!wcm || !wcm->server || !wcm->key)
3357                return;
3358
3359        yid = y_new0(struct yahoo_input_data, 1);
3360        yid->type = YAHOO_CONNECTION_WEBCAM;
3361        yid->yd = y->yd;
3362
3363        /* copy webcam data to new connection */
3364        yid->wcm = y->wcm;
3365        y->wcm = NULL;
3366
3367        yid->wcd = y_new0(struct yahoo_webcam_data, 1);
3368
3369        LOG(("Connecting to: %s:%d", wcm->server, wcm->port));
3370        YAHOO_CALLBACK(ext_yahoo_connect_async) (y->yd->client_id, wcm->server,
3371                wcm->port, _yahoo_webcam_connected, yid, 0);
3372
3373}
3374
3375static void yahoo_process_webcam_master_connection(struct yahoo_input_data *yid,
3376        int over)
3377{
3378        char *server;
3379        struct yahoo_server_settings *yss;
3380
3381        if (over)
3382                return;
3383
3384        server = yahoo_getwebcam_master(yid);
3385
3386        if (server) {
3387                yss = yid->yd->server_settings;
3388                yid->wcm->server = strdup(server);
3389                yid->wcm->port = yss->webcam_port;
3390                yid->wcm->conn_type = yss->conn_type;
3391                yid->wcm->my_ip = strdup(yss->local_host);
3392                if (yid->wcm->direction == YAHOO_WEBCAM_UPLOAD)
3393                        yid->wcm->description = strdup(yss->webcam_description);
3394                yahoo_webcam_connect(yid);
3395                FREE(server);
3396        }
3397}
3398
3399static void yahoo_process_webcam_connection(struct yahoo_input_data *yid,
3400        int over)
3401{
3402        int id = yid->yd->client_id;
3403        void *fd = yid->fd;
3404
3405        if (over)
3406                return;
3407
3408        /* as long as we still have packets available keep processing them */
3409        while (find_input_by_id_and_fd(id, fd)
3410                && yahoo_get_webcam_data(yid) == 1) ;
3411}
3412
3413static void (*yahoo_process_connection[]) (struct yahoo_input_data *,
3414        int over) = {
3415yahoo_process_pager_connection, yahoo_process_ft_connection,
3416                yahoo_process_yab_connection,
3417                yahoo_process_webcam_master_connection,
3418                yahoo_process_webcam_connection,
3419                yahoo_process_chatcat_connection,
3420                yahoo_process_search_connection};
3421
3422int yahoo_read_ready(int id, void *fd, void *data)
3423{
3424        struct yahoo_input_data *yid = data;
3425        char buf[1024];
3426        int len;
3427
3428        LOG(("read callback: id=%d fd=%p data=%p", id, fd, data));
3429        if (!yid)
3430                return -2;
3431
3432        do {
3433                len = YAHOO_CALLBACK(ext_yahoo_read) (fd, buf, sizeof(buf));
3434        } while (len == -1 && errno == EINTR);
3435
3436        if (len == -1 && (errno == EAGAIN || errno == EINTR))   /* we'll try again later */
3437                return 1;
3438
3439        if (len <= 0) {
3440                int e = errno;
3441                DEBUG_MSG(("len == %d (<= 0)", len));
3442
3443                if (yid->type == YAHOO_CONNECTION_PAGER) {
3444                        YAHOO_CALLBACK(ext_yahoo_login_response) (yid->yd->
3445                                client_id, YAHOO_LOGIN_SOCK, NULL);
3446                }
3447
3448                yahoo_process_connection[yid->type] (yid, 1);
3449                yahoo_input_close(yid);
3450
3451                /* no need to return an error, because we've already fixed it */
3452                if (len == 0)
3453                        return 1;
3454
3455                errno = e;
3456                LOG(("read error: %s", strerror(errno)));
3457                return -1;
3458        }
3459
3460        yid->rxqueue =
3461                y_renew(unsigned char, yid->rxqueue, len + yid->rxlen + 1);
3462        memcpy(yid->rxqueue + yid->rxlen, buf, len);
3463        yid->rxlen += len;
3464        yid->rxqueue[yid->rxlen] = 0;
3465
3466        yahoo_process_connection[yid->type] (yid, 0);
3467
3468        return len;
3469}
3470
3471int yahoo_init_with_attributes(const char *username, const char *password, ...)
3472{
3473        va_list ap;
3474        struct yahoo_data *yd;
3475
3476        yd = y_new0(struct yahoo_data, 1);
3477
3478        if (!yd)
3479                return 0;
3480
3481        yd->user = strdup(username);
3482        yd->password = strdup(password);
3483
3484        yd->initial_status = -1;
3485        yd->current_status = -1;
3486
3487        yd->client_id = ++last_id;
3488
3489        add_to_list(yd);
3490
3491        va_start(ap, password);
3492        yd->server_settings = _yahoo_assign_server_settings(ap);
3493        va_end(ap);
3494
3495        return yd->client_id;
3496}
3497
3498int yahoo_init(const char *username, const char *password)
3499{
3500        return yahoo_init_with_attributes(username, password, NULL);
3501}
3502
3503static void yahoo_connected(void *fd, int error, void *data)
3504{
3505        struct connect_callback_data *ccd = data;
3506        struct yahoo_data *yd = ccd->yd;
3507        struct yahoo_packet *pkt;
3508        struct yahoo_input_data *yid;
3509        struct yahoo_server_settings *yss = yd->server_settings;
3510
3511        if (error) {
3512                int tag;
3513                if (fallback_ports[ccd->i]) {
3514                        char *host = yss->pager_host;
3515
3516                        if (!host)
3517                                host = yss->pager_host_list[ccd->server_i];
3518
3519                        yss->pager_port = fallback_ports[ccd->i++];
3520                        tag = YAHOO_CALLBACK(ext_yahoo_connect_async) (yd->
3521                                client_id, host, yss->pager_port,
3522                                yahoo_connected, ccd, 0);
3523
3524                        if (tag > 0)
3525                                ccd->tag = tag;
3526                } else if (yss->pager_host_list
3527                                && yss->pager_host_list[ccd->server_i]) {
3528
3529                        /* Get back to the default port */
3530                        yss->pager_port = pager_port;
3531                        ccd->server_i++;
3532                        LOG(("Fallback: Connecting to %s:%d", yss->pager_host_list[ccd->server_i], yss->pager_port));
3533
3534                        ccd->i = 0;
3535                        tag = YAHOO_CALLBACK(ext_yahoo_connect_async) (yd->client_id,
3536                                yss->pager_host_list[ccd->server_i], yss->pager_port,
3537                                yahoo_connected, ccd, 0);
3538                } else {
3539                        FREE(ccd);
3540                        YAHOO_CALLBACK(ext_yahoo_login_response) (yd->client_id,
3541                                YAHOO_LOGIN_SOCK, NULL);
3542                }
3543                return;
3544        }
3545
3546        FREE(ccd);
3547
3548        /* fd == NULL && error == 0 means connect was cancelled */
3549        if (!fd)
3550                return;
3551
3552        pkt = yahoo_packet_new(YAHOO_SERVICE_AUTH, YPACKET_STATUS_DEFAULT,
3553                yd->session_id);
3554        NOTICE(("Sending initial packet"));
3555
3556        yahoo_packet_hash(pkt, 1, yd->user);
3557
3558        yid = find_input_by_id_and_type(yd->client_id, YAHOO_CONNECTION_PAGER);
3559        yid->fd = fd;
3560
3561        yahoo_send_packet(yid, pkt, 0);
3562
3563        yahoo_packet_free(pkt);
3564
3565        yid->read_tag =
3566                YAHOO_CALLBACK(ext_yahoo_add_handler) (yid->yd->client_id,
3567                yid->fd, YAHOO_INPUT_READ, yid);
3568}
3569
3570void *yahoo_get_fd(int id)
3571{
3572        struct yahoo_input_data *yid =
3573                find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
3574        if (!yid)
3575                return 0;
3576        else
3577                return yid->fd;
3578}
3579
3580void yahoo_send_buzz(int id, const char *from, const char *who)
3581{
3582        yahoo_send_im(id, from, who, "<ding>", 1, 0);
3583}
3584
3585void yahoo_send_im(int id, const char *from, const char *who, const char *what,
3586        int utf8, int picture)
3587{
3588        struct yahoo_input_data *yid =
3589                find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
3590        struct yahoo_packet *pkt = NULL;
3591        struct yahoo_data *yd;
3592        char pic_str[10];
3593
3594        if (!yid)
3595                return;
3596
3597        yd = yid->yd;
3598
3599        pkt = yahoo_packet_new(YAHOO_SERVICE_MESSAGE, YAHOO_STATUS_OFFLINE,
3600                yd->session_id);
3601
3602        snprintf(pic_str, sizeof(pic_str), "%d", picture);
3603
3604        if (from && strcmp(from, yd->user))
3605                yahoo_packet_hash(pkt, 0, yd->user);
3606        yahoo_packet_hash(pkt, 1, from ? from : yd->user);
3607        yahoo_packet_hash(pkt, 5, who);
3608        yahoo_packet_hash(pkt, 14, what);
3609
3610        if (utf8)
3611                yahoo_packet_hash(pkt, 97, "1");
3612
3613        yahoo_packet_hash(pkt, 63, ";0");       /* imvironment name; or ;0 */
3614        yahoo_packet_hash(pkt, 64, "0");
3615        yahoo_packet_hash(pkt, 206, pic_str);
3616
3617        yahoo_send_packet(yid, pkt, 0);
3618
3619        yahoo_packet_free(pkt);
3620}
3621
3622void yahoo_send_typing(int id, const char *from, const char *who, int typ)
3623{
3624        struct yahoo_input_data *yid =
3625                find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
3626        struct yahoo_data *yd;
3627        struct yahoo_packet *pkt = NULL;
3628        if (!yid)
3629                return;
3630
3631        yd = yid->yd;
3632        pkt = yahoo_packet_new(YAHOO_SERVICE_NOTIFY, YPACKET_STATUS_NOTIFY,
3633                yd->session_id);
3634
3635        yahoo_packet_hash(pkt, 5, who);
3636        yahoo_packet_hash(pkt, 1, from ? from : yd->user);
3637        yahoo_packet_hash(pkt, 14, " ");
3638        yahoo_packet_hash(pkt, 13, typ ? "1" : "0");
3639        yahoo_packet_hash(pkt, 49, "TYPING");
3640
3641        yahoo_send_packet(yid, pkt, 0);
3642
3643        yahoo_packet_free(pkt);
3644}
3645
3646void yahoo_set_away(int id, enum yahoo_status state, const char *msg, int away)
3647{
3648        struct yahoo_input_data *yid =
3649                find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
3650        struct yahoo_data *yd;
3651        struct yahoo_packet *pkt = NULL;
3652        int old_status;
3653        char s[4];
3654
3655        if (!yid)
3656                return;
3657
3658        yd = yid->yd;
3659
3660        old_status = yd->current_status;
3661        yd->current_status = state;
3662
3663        /* Thank you libpurple :) */
3664        if (yd->current_status == YAHOO_STATUS_INVISIBLE) {
3665                pkt = yahoo_packet_new(YAHOO_SERVICE_Y6_VISIBLE_TOGGLE,
3666                        YAHOO_STATUS_AVAILABLE, 0);
3667                yahoo_packet_hash(pkt, 13, "2");
3668                yahoo_send_packet(yid, pkt, 0);
3669                yahoo_packet_free(pkt);
3670
3671                return;
3672        }
3673
3674        pkt = yahoo_packet_new(YAHOO_SERVICE_Y6_STATUS_UPDATE,
3675                yd->current_status, yd->session_id);
3676        snprintf(s, sizeof(s), "%d", yd->current_status);
3677        yahoo_packet_hash(pkt, 10, s);
3678        yahoo_packet_hash(pkt, 19, msg && state == YAHOO_STATUS_CUSTOM ? msg : "");
3679        yahoo_packet_hash(pkt, 47, (away == 2)? "2": (away) ?"1":"0");
3680        yahoo_send_packet(yid, pkt, 0);
3681        yahoo_packet_free(pkt);
3682
3683        if (old_status == YAHOO_STATUS_INVISIBLE) {
3684                pkt = yahoo_packet_new(YAHOO_SERVICE_Y6_VISIBLE_TOGGLE,
3685                        YAHOO_STATUS_AVAILABLE, 0);
3686                yahoo_packet_hash(pkt, 13, "1");
3687                yahoo_send_packet(yid, pkt, 0);
3688                yahoo_packet_free(pkt);
3689        }
3690}
3691
3692void yahoo_logoff(int id)
3693{
3694        struct yahoo_input_data *yid =
3695                find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
3696        struct yahoo_data *yd;
3697        struct yahoo_packet *pkt = NULL;
3698
3699        if (!yid)
3700                return;
3701        yd = yid->yd;
3702
3703        LOG(("yahoo_logoff: current status: %d", yd->current_status));
3704
3705        if (yd->current_status != -1 && 0) {
3706                /* Meh. Don't send this. The event handlers are not going to
3707                   get to do this so it'll just leak memory. And the TCP
3708                   connection reset will hopefully be clear enough. */
3709                pkt = yahoo_packet_new(YAHOO_SERVICE_LOGOFF,
3710                        YPACKET_STATUS_DEFAULT, yd->session_id);
3711                yd->current_status = -1;
3712
3713                if (pkt) {
3714                        yahoo_send_packet(yid, pkt, 0);
3715                        yahoo_packet_free(pkt);
3716                }
3717        }
3718
3719/*      do {
3720                yahoo_input_close(yid);
3721        } while((yid = find_input_by_id(id)));*/
3722
3723}
3724
3725void yahoo_get_list(int id)
3726{
3727        struct yahoo_input_data *yid =
3728                find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
3729        struct yahoo_data *yd;
3730        struct yahoo_packet *pkt = NULL;
3731
3732        if (!yid)
3733                return;
3734        yd = yid->yd;
3735
3736        pkt = yahoo_packet_new(YAHOO_SERVICE_LIST, YPACKET_STATUS_DEFAULT,
3737                yd->session_id);
3738        yahoo_packet_hash(pkt, 1, yd->user);
3739        if (pkt) {
3740                yahoo_send_packet(yid, pkt, 0);
3741                yahoo_packet_free(pkt);
3742        }
3743}
3744
3745static void _yahoo_http_connected(int id, void *fd, int error, void *data)
3746{
3747        struct yahoo_input_data *yid = data;
3748        if (fd == NULL || error) {
3749                inputs = y_list_remove(inputs, yid);
3750                FREE(yid);
3751                return;
3752        }
3753
3754        yid->fd = fd;
3755        yid->read_tag =
3756                YAHOO_CALLBACK(ext_yahoo_add_handler) (yid->yd->client_id, fd,
3757                YAHOO_INPUT_READ, yid);
3758}
3759
3760/* FIXME Get address book from address.yahoo.com instead */
3761void yahoo_get_yab(int id)
3762{
3763        struct yahoo_data *yd = find_conn_by_id(id);
3764        struct yahoo_input_data *yid;
3765        char url[1024];
3766        char buff[2048];
3767
3768        if (!yd)
3769                return;
3770
3771        yid = y_new0(struct yahoo_input_data, 1);
3772        yid->yd = yd;
3773        yid->type = YAHOO_CONNECTION_YAB;
3774
3775        LOG(("Sending request for Address Book"));
3776
3777        snprintf(url, 1024,
3778                "http://address.yahoo.com/yab/us?v=XM&prog=ymsgr&.intl=us"
3779                "&diffs=1&t=0&tags=short&rt=0&prog-ver=8.1.0.249&useutf8=1&legenc=codepage-1252");
3780
3781        snprintf(buff, sizeof(buff), "Y=%s; T=%s", yd->cookie_y, yd->cookie_t);
3782
3783        inputs = y_list_prepend(inputs, yid);
3784
3785        yahoo_http_get(yid->yd->client_id, url, buff, 0, 0,
3786                _yahoo_http_connected, yid);
3787}
3788
3789struct yahoo_post_data {
3790        struct yahoo_input_data *yid;
3791        char *data;
3792};
3793
3794static void _yahoo_http_post_connected(int id, void *fd, int error, void *data)
3795{
3796        struct yahoo_post_data *yad = data;
3797        struct yahoo_input_data *yid = yad->yid;
3798        char *buff = yad->data;
3799
3800        if (!fd) {
3801                inputs = y_list_remove(inputs, yid);
3802                FREE(yid);
3803                return;
3804        }
3805
3806        YAHOO_CALLBACK(ext_yahoo_write) (fd, buff, strlen(buff));
3807
3808        yid->fd = fd;
3809        yid->read_tag =
3810                YAHOO_CALLBACK(ext_yahoo_add_handler) (yid->yd->client_id, fd,
3811                YAHOO_INPUT_READ, yid);
3812
3813        FREE(buff);
3814        FREE(yad);
3815}
3816
3817/* FIXME This is also likely affected */
3818void yahoo_set_yab(int id, struct yab *yab)
3819{
3820        struct yahoo_post_data *yad = y_new0(struct yahoo_post_data, 1);
3821        struct yahoo_data *yd = find_conn_by_id(id);
3822        struct yahoo_input_data *yid;
3823        char url[1024];
3824        char buff[1024];
3825        char post[1024];
3826        int size = 0;
3827
3828        if (!yd)
3829                return;
3830
3831        yid = y_new0(struct yahoo_input_data, 1);
3832        yid->type = YAHOO_CONNECTION_YAB;
3833        yid->yd = yd;
3834
3835        if(yab->yid)
3836                size = snprintf(post, sizeof(post), "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
3837                        "<ab k=\"%s\" cc=\"%d\">"
3838                        "<ct id=\"%d\" e=\"1\" yi=\"%s\" nn=\"%s\" />"
3839                        "</ab>", yd->user, 9, yab->yid, /* Don't know why */
3840                        yab->id, yab->nname?yab->nname:"");
3841        else
3842                size = snprintf(post, sizeof(post), "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
3843                        "<ab k=\"%s\" cc=\"%d\">"
3844                        "<ct a=\"1\" yi=\"%s\" nn=\"%s\" />"
3845                        "</ab>", yd->user, 1,   /* Don't know why */
3846                        yab->id, yab->nname?yab->nname:"");
3847
3848        yad->yid = yid;
3849        yad->data = strdup(post);
3850
3851        strcpy(url, "http://address.yahoo.com/yab/us?v=XM&prog=ymsgr&.intl=us"
3852                "&sync=1&tags=short&noclear=1&useutf8=1&legenc=codepage-1252");
3853
3854        snprintf(buff, sizeof(buff), "Y=%s; T=%s", yd->cookie_y, yd->cookie_t);
3855
3856        inputs = y_list_prepend(inputs, yid);
3857
3858        yahoo_http_post(yid->yd->client_id, url, buff, size,
3859                _yahoo_http_post_connected, yad);
3860}
3861
3862void yahoo_set_identity_status(int id, const char *identity, int active)
3863{
3864        struct yahoo_input_data *yid =
3865                find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
3866        struct yahoo_data *yd;
3867        struct yahoo_packet *pkt = NULL;
3868
3869        if (!yid)
3870                return;
3871        yd = yid->yd;
3872
3873        pkt = yahoo_packet_new(active ? YAHOO_SERVICE_IDACT :
3874                YAHOO_SERVICE_IDDEACT, YPACKET_STATUS_DEFAULT, yd->session_id);
3875        yahoo_packet_hash(pkt, 3, identity);
3876        if (pkt) {
3877                yahoo_send_packet(yid, pkt, 0);
3878                yahoo_packet_free(pkt);
3879        }
3880}
3881
3882void yahoo_refresh(int id)
3883{
3884        struct yahoo_input_data *yid =
3885                find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
3886        struct yahoo_data *yd;
3887        struct yahoo_packet *pkt = NULL;
3888
3889        if (!yid)
3890                return;
3891        yd = yid->yd;
3892
3893        pkt = yahoo_packet_new(YAHOO_SERVICE_USERSTAT, YPACKET_STATUS_DEFAULT,
3894                yd->session_id);
3895        if (pkt) {
3896                yahoo_send_packet(yid, pkt, 0);
3897                yahoo_packet_free(pkt);
3898        }
3899}
3900
3901void yahoo_keepalive(int id)
3902{
3903        struct yahoo_input_data *yid =
3904                find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
3905        struct yahoo_data *yd;
3906        struct yahoo_packet *pkt = NULL;
3907        if (!yid)
3908                return;
3909        yd = yid->yd;
3910
3911        pkt = yahoo_packet_new(YAHOO_SERVICE_PING, YPACKET_STATUS_DEFAULT,
3912                yd->session_id);
3913        yahoo_send_packet(yid, pkt, 0);
3914        yahoo_packet_free(pkt);
3915}
3916
3917void yahoo_chat_keepalive(int id)
3918{
3919        struct yahoo_input_data *yid =
3920                find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
3921        struct yahoo_data *yd;
3922        struct yahoo_packet *pkt = NULL;
3923
3924        if (!yid)
3925                return;
3926
3927        yd = yid->yd;
3928
3929        pkt = yahoo_packet_new(YAHOO_SERVICE_CHATPING, YPACKET_STATUS_DEFAULT,
3930                yd->session_id);
3931        yahoo_send_packet(yid, pkt, 0);
3932        yahoo_packet_free(pkt);
3933}
3934
3935void yahoo_add_buddy(int id, const char *who, const char *group,
3936        const char *msg)
3937{
3938        struct yahoo_input_data *yid =
3939                find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
3940        struct yahoo_data *yd;
3941        struct yahoo_packet *pkt;
3942
3943        if (!yid)
3944                return;
3945        yd = yid->yd;
3946
3947        if (!yd->logged_in)
3948                return;
3949
3950        pkt = yahoo_packet_new(YAHOO_SERVICE_ADDBUDDY, YPACKET_STATUS_DEFAULT,
3951                yd->session_id);
3952        if (msg != NULL)        /* add message/request "it's me add me" */
3953                yahoo_packet_hash(pkt, 14, msg);
3954        else
3955                yahoo_packet_hash(pkt, 14, "");
3956        yahoo_packet_hash(pkt, 65, group);
3957        yahoo_packet_hash(pkt, 97, "1");
3958        yahoo_packet_hash(pkt, 1, yd->user);
3959        yahoo_packet_hash(pkt, 302, "319");
3960        yahoo_packet_hash(pkt, 300, "319");
3961        yahoo_packet_hash(pkt, 7, who);
3962        yahoo_packet_hash(pkt, 334, "0");
3963        yahoo_packet_hash(pkt, 301, "319");
3964        yahoo_packet_hash(pkt, 303, "319");
3965        yahoo_send_packet(yid, pkt, 0);
3966        yahoo_packet_free(pkt);
3967}
3968
3969void yahoo_remove_buddy(int id, const char *who, const char *group)
3970{
3971        struct yahoo_input_data *yid =
3972                find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
3973        struct yahoo_data *yd;
3974        struct yahoo_packet *pkt = NULL;
3975
3976        if (!yid)
3977                return;
3978        yd = yid->yd;
3979
3980        pkt = yahoo_packet_new(YAHOO_SERVICE_REMBUDDY, YPACKET_STATUS_DEFAULT,
3981                yd->session_id);
3982
3983        yahoo_packet_hash(pkt, 1, yd->user);
3984        yahoo_packet_hash(pkt, 7, who);
3985        yahoo_packet_hash(pkt, 65, group);
3986        yahoo_send_packet(yid, pkt, 0);
3987        yahoo_packet_free(pkt);
3988}
3989
3990void yahoo_confirm_buddy(int id, const char *who, int reject, const char *msg)
3991{
3992        struct yahoo_input_data *yid =
3993                find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
3994        struct yahoo_data *yd;
3995        struct yahoo_packet *pkt;
3996
3997        if (!yid)
3998                return;
3999        yd = yid->yd;
4000
4001        if (!yd->logged_in)
4002                return;
4003
4004        pkt = yahoo_packet_new(YAHOO_SERVICE_Y7_AUTHORIZATION,
4005                YPACKET_STATUS_DEFAULT, yd->session_id);
4006        yahoo_packet_hash(pkt, 1, yd->user);
4007        yahoo_packet_hash(pkt, 5, who);
4008        if (reject)
4009                yahoo_packet_hash(pkt, 13, "2");
4010        else {
4011                yahoo_packet_hash(pkt, 241, "0");
4012                yahoo_packet_hash(pkt, 13, "1");
4013        }
4014
4015        yahoo_packet_hash(pkt, 334, "0");
4016
4017        if (reject) {
4018                yahoo_packet_hash(pkt, 14, msg ? msg : "");
4019                yahoo_packet_hash(pkt, 97, "1");
4020        }
4021
4022        yahoo_send_packet(yid, pkt, 0);
4023        yahoo_packet_free(pkt);
4024}
4025
4026void yahoo_ignore_buddy(int id, const char *who, int unignore)
4027{
4028        struct yahoo_input_data *yid =
4029                find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
4030        struct yahoo_data *yd;
4031        struct yahoo_packet *pkt;
4032
4033        if (!yid)
4034                return;
4035        yd = yid->yd;
4036
4037        if (!yd->logged_in)
4038                return;
4039
4040        pkt = yahoo_packet_new(YAHOO_SERVICE_IGNORECONTACT,
4041                YPACKET_STATUS_DEFAULT, yd->session_id);
4042        yahoo_packet_hash(pkt, 1, yd->user);
4043        yahoo_packet_hash(pkt, 7, who);
4044        yahoo_packet_hash(pkt, 13, unignore ? "2" : "1");
4045        yahoo_send_packet(yid, pkt, 0);
4046        yahoo_packet_free(pkt);
4047}
4048
4049void yahoo_stealth_buddy(int id, const char *who, int unstealth)
4050{
4051        struct yahoo_input_data *yid =
4052                find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
4053        struct yahoo_data *yd;
4054        struct yahoo_packet *pkt;
4055
4056        if (!yid)
4057                return;
4058        yd = yid->yd;
4059
4060        if (!yd->logged_in)
4061                return;
4062
4063        pkt = yahoo_packet_new(YAHOO_SERVICE_STEALTH_PERM,
4064                YPACKET_STATUS_DEFAULT, yd->session_id);
4065        yahoo_packet_hash(pkt, 1, yd->user);
4066        yahoo_packet_hash(pkt, 7, who);
4067        yahoo_packet_hash(pkt, 31, unstealth ? "2" : "1");
4068        yahoo_packet_hash(pkt, 13, "2");
4069        yahoo_send_packet(yid, pkt, 0);
4070        yahoo_packet_free(pkt);
4071}
4072
4073void yahoo_change_buddy_group(int id, const char *who, const char *old_group,
4074        const char *new_group)
4075{
4076        struct yahoo_input_data *yid =
4077                find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
4078        struct yahoo_data *yd;
4079        struct yahoo_packet *pkt = NULL;
4080
4081        if (!yid)
4082                return;
4083        yd = yid->yd;
4084
4085        pkt = yahoo_packet_new(YAHOO_SERVICE_Y7_CHANGE_GROUP,
4086                YPACKET_STATUS_DEFAULT, yd->session_id);
4087        yahoo_packet_hash(pkt, 1, yd->user);
4088        yahoo_packet_hash(pkt, 302, "240");
4089        yahoo_packet_hash(pkt, 300, "240");
4090        yahoo_packet_hash(pkt, 7, who);
4091        yahoo_packet_hash(pkt, 224, old_group);
4092        yahoo_packet_hash(pkt, 264, new_group);
4093        yahoo_packet_hash(pkt, 301, "240");
4094        yahoo_packet_hash(pkt, 303, "240");
4095
4096        yahoo_send_packet(yid, pkt, 0);
4097        yahoo_packet_free(pkt);
4098}
4099
4100void yahoo_group_rename(int id, const char *old_group, const char *new_group)
4101{
4102        struct yahoo_input_data *yid =
4103                find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
4104        struct yahoo_data *yd;
4105        struct yahoo_packet *pkt = NULL;
4106
4107        if (!yid)
4108                return;
4109        yd = yid->yd;
4110
4111        pkt = yahoo_packet_new(YAHOO_SERVICE_GROUPRENAME,
4112                YPACKET_STATUS_DEFAULT, yd->session_id);
4113        yahoo_packet_hash(pkt, 1, yd->user);
4114        yahoo_packet_hash(pkt, 65, old_group);
4115        yahoo_packet_hash(pkt, 67, new_group);
4116
4117        yahoo_send_packet(yid, pkt, 0);
4118        yahoo_packet_free(pkt);
4119}
4120
4121void yahoo_conference_addinvite(int id, const char *from, const char *who,
4122        const char *room, const YList *members, const char *msg)
4123{
4124        struct yahoo_input_data *yid =
4125                find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
4126        struct yahoo_data *yd;
4127        struct yahoo_packet *pkt;
4128
4129        if (!yid)
4130                return;
4131        yd = yid->yd;
4132
4133        pkt = yahoo_packet_new(YAHOO_SERVICE_CONFADDINVITE,
4134                YPACKET_STATUS_DEFAULT, yd->session_id);
4135
4136        yahoo_packet_hash(pkt, 1, (from ? from : yd->user));
4137        yahoo_packet_hash(pkt, 51, who);
4138        yahoo_packet_hash(pkt, 57, room);
4139        yahoo_packet_hash(pkt, 58, msg);
4140        yahoo_packet_hash(pkt, 13, "0");
4141        for (; members; members = members->next) {
4142                yahoo_packet_hash(pkt, 52, (char *)members->data);
4143                yahoo_packet_hash(pkt, 53, (char *)members->data);
4144        }
4145        /* 52, 53 -> other members? */
4146
4147        yahoo_send_packet(yid, pkt, 0);
4148
4149        yahoo_packet_free(pkt);
4150}
4151
4152void yahoo_conference_invite(int id, const char *from, YList *who,
4153        const char *room, const char *msg)
4154{
4155        struct yahoo_input_data *yid =
4156                find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
4157        struct yahoo_data *yd;
4158        struct yahoo_packet *pkt;
4159
4160        if (!yid)
4161                return;
4162        yd = yid->yd;
4163
4164        pkt = yahoo_packet_new(YAHOO_SERVICE_CONFINVITE, YPACKET_STATUS_DEFAULT,
4165                yd->session_id);
4166
4167        yahoo_packet_hash(pkt, 1, (from ? from : yd->user));
4168        yahoo_packet_hash(pkt, 50, yd->user);
4169        for (; who; who = who->next) {
4170                yahoo_packet_hash(pkt, 52, (char *)who->data);
4171        }
4172        yahoo_packet_hash(pkt, 57, room);
4173        yahoo_packet_hash(pkt, 58, msg);
4174        yahoo_packet_hash(pkt, 13, "0");
4175
4176        yahoo_send_packet(yid, pkt, 0);
4177
4178        yahoo_packet_free(pkt);
4179}
4180
4181void yahoo_conference_logon(int id, const char *from, YList *who,
4182        const char *room)
4183{
4184        struct yahoo_input_data *yid =
4185                find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
4186        struct yahoo_data *yd;
4187        struct yahoo_packet *pkt;
4188
4189        if (!yid)
4190                return;
4191        yd = yid->yd;
4192
4193        pkt = yahoo_packet_new(YAHOO_SERVICE_CONFLOGON, YPACKET_STATUS_DEFAULT,
4194                yd->session_id);
4195
4196        yahoo_packet_hash(pkt, 1, (from ? from : yd->user));
4197        yahoo_packet_hash(pkt, 3, (from ? from : yd->user));
4198        yahoo_packet_hash(pkt, 57, room);
4199        for (; who; who = who->next)
4200                yahoo_packet_hash(pkt, 3, (char *)who->data);
4201
4202        yahoo_send_packet(yid, pkt, 0);
4203
4204        yahoo_packet_free(pkt);
4205}
4206
4207void yahoo_conference_decline(int id, const char *from, YList *who,
4208        const char *room, const char *msg)
4209{
4210        struct yahoo_input_data *yid =
4211                find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
4212        struct yahoo_data *yd;
4213        struct yahoo_packet *pkt;
4214
4215        if (!yid)
4216                return;
4217        yd = yid->yd;
4218
4219        pkt = yahoo_packet_new(YAHOO_SERVICE_CONFDECLINE,
4220                YPACKET_STATUS_DEFAULT, yd->session_id);
4221
4222        yahoo_packet_hash(pkt, 1, (from ? from : yd->user));
4223        yahoo_packet_hash(pkt, 3, (from ? from : yd->user));
4224        for (; who; who = who->next)
4225                yahoo_packet_hash(pkt, 3, (char *)who->data);
4226        yahoo_packet_hash(pkt, 57, room);
4227        yahoo_packet_hash(pkt, 14, msg);
4228
4229        yahoo_send_packet(yid, pkt, 0);
4230
4231        yahoo_packet_free(pkt);
4232}
4233
4234void yahoo_conference_logoff(int id, const char *from, YList *who,
4235        const char *room)
4236{
4237        struct yahoo_input_data *yid =
4238                find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
4239        struct yahoo_data *yd;
4240        struct yahoo_packet *pkt;
4241
4242        if (!yid)
4243                return;
4244        yd = yid->yd;
4245
4246        pkt = yahoo_packet_new(YAHOO_SERVICE_CONFLOGOFF, YPACKET_STATUS_DEFAULT,
4247                yd->session_id);
4248
4249        yahoo_packet_hash(pkt, 1, (from ? from : yd->user));
4250        yahoo_packet_hash(pkt, 3, (from ? from : yd->user));
4251        for (; who; who = who->next)
4252                yahoo_packet_hash(pkt, 3, (char *)who->data);
4253
4254        yahoo_packet_hash(pkt, 57, room);
4255
4256        yahoo_send_packet(yid, pkt, 0);
4257
4258        yahoo_packet_free(pkt);
4259}
4260
4261void yahoo_conference_message(int id, const char *from, YList *who,
4262        const char *room, const char *msg, int utf8)
4263{
4264        struct yahoo_input_data *yid =
4265                find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
4266        struct yahoo_data *yd;
4267        struct yahoo_packet *pkt;
4268
4269        if (!yid)
4270                return;
4271        yd = yid->yd;
4272
4273        pkt = yahoo_packet_new(YAHOO_SERVICE_CONFMSG, YPACKET_STATUS_DEFAULT,
4274                yd->session_id);
4275
4276        yahoo_packet_hash(pkt, 1, (from ? from : yd->user));
4277        yahoo_packet_hash(pkt, 53, (from ? from : yd->user));
4278        for (; who; who = who->next)
4279                yahoo_packet_hash(pkt, 53, (char *)who->data);
4280
4281        yahoo_packet_hash(pkt, 57, room);
4282        yahoo_packet_hash(pkt, 14, msg);
4283
4284        if (utf8)
4285                yahoo_packet_hash(pkt, 97, "1");
4286
4287        yahoo_send_packet(yid, pkt, 0);
4288
4289        yahoo_packet_free(pkt);
4290}
4291
4292void yahoo_get_chatrooms(int id, int chatroomid)
4293{
4294        struct yahoo_data *yd = find_conn_by_id(id);
4295        struct yahoo_input_data *yid;
4296        char url[1024];
4297        char buff[1024];
4298
4299        if (!yd)
4300                return;
4301
4302        yid = y_new0(struct yahoo_input_data, 1);
4303        yid->yd = yd;
4304        yid->type = YAHOO_CONNECTION_CHATCAT;
4305
4306        if (chatroomid == 0) {
4307                snprintf(url, 1024,
4308                        "http://insider.msg.yahoo.com/ycontent/?chatcat=0");
4309        } else {
4310                snprintf(url, 1024,
4311                        "http://insider.msg.yahoo.com/ycontent/?chatroom_%d=0",
4312                        chatroomid);
4313        }
4314
4315        snprintf(buff, sizeof(buff), "Y=%s; T=%s", yd->cookie_y, yd->cookie_t);
4316
4317        inputs = y_list_prepend(inputs, yid);
4318
4319        yahoo_http_get(yid->yd->client_id, url, buff, 0, 0,
4320                _yahoo_http_connected, yid);
4321}
4322
4323void yahoo_chat_logon(int id, const char *from, const char *room,
4324        const char *roomid)
4325{
4326        struct yahoo_input_data *yid =
4327                find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
4328        struct yahoo_data *yd;
4329        struct yahoo_packet *pkt;
4330
4331        if (!yid)
4332                return;
4333
4334        yd = yid->yd;
4335
4336        pkt = yahoo_packet_new(YAHOO_SERVICE_CHATONLINE, YPACKET_STATUS_DEFAULT,
4337                yd->session_id);
4338
4339        yahoo_packet_hash(pkt, 1, (from ? from : yd->user));
4340        yahoo_packet_hash(pkt, 109, yd->user);
4341        yahoo_packet_hash(pkt, 6, "abcde");
4342
4343        yahoo_send_packet(yid, pkt, 0);
4344
4345        yahoo_packet_free(pkt);
4346
4347        pkt = yahoo_packet_new(YAHOO_SERVICE_CHATJOIN, YPACKET_STATUS_DEFAULT,
4348                yd->session_id);
4349
4350        yahoo_packet_hash(pkt, 1, (from ? from : yd->user));
4351        yahoo_packet_hash(pkt, 104, room);
4352        yahoo_packet_hash(pkt, 129, roomid);
4353        yahoo_packet_hash(pkt, 62, "2");        /* ??? */
4354
4355        yahoo_send_packet(yid, pkt, 0);
4356
4357        yahoo_packet_free(pkt);
4358}
4359
4360void yahoo_chat_message(int id, const char *from, const char *room,
4361        const char *msg, const int msgtype, const int utf8)
4362{
4363        struct yahoo_input_data *yid =
4364                find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
4365        struct yahoo_data *yd;
4366        struct yahoo_packet *pkt;
4367        char buf[2];
4368
4369        if (!yid)
4370                return;
4371
4372        yd = yid->yd;
4373
4374        pkt = yahoo_packet_new(YAHOO_SERVICE_COMMENT, YPACKET_STATUS_DEFAULT,
4375                yd->session_id);
4376
4377        yahoo_packet_hash(pkt, 1, (from ? from : yd->user));
4378        yahoo_packet_hash(pkt, 104, room);
4379        yahoo_packet_hash(pkt, 117, msg);
4380
4381        snprintf(buf, sizeof(buf), "%d", msgtype);
4382        yahoo_packet_hash(pkt, 124, buf);
4383
4384        if (utf8)
4385                yahoo_packet_hash(pkt, 97, "1");
4386
4387        yahoo_send_packet(yid, pkt, 0);
4388
4389        yahoo_packet_free(pkt);
4390}
4391
4392void yahoo_chat_logoff(int id, const char *from)
4393{
4394        struct yahoo_input_data *yid =
4395                find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
4396        struct yahoo_data *yd;
4397        struct yahoo_packet *pkt;
4398
4399        if (!yid)
4400                return;
4401
4402        yd = yid->yd;
4403
4404        pkt = yahoo_packet_new(YAHOO_SERVICE_CHATLOGOUT, YPACKET_STATUS_DEFAULT,
4405                yd->session_id);
4406
4407        yahoo_packet_hash(pkt, 1, (from ? from : yd->user));
4408
4409        yahoo_send_packet(yid, pkt, 0);
4410
4411        yahoo_packet_free(pkt);
4412}
4413
4414void yahoo_buddyicon_request(int id, const char *who)
4415{
4416        struct yahoo_input_data *yid =
4417                find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
4418        struct yahoo_data *yd;
4419        struct yahoo_packet *pkt;
4420
4421        if (!yid)
4422                return;
4423
4424        yd = yid->yd;
4425
4426        pkt = yahoo_packet_new(YAHOO_SERVICE_PICTURE, YPACKET_STATUS_DEFAULT,
4427                0);
4428        yahoo_packet_hash(pkt, 4, yd->user);
4429        yahoo_packet_hash(pkt, 5, who);
4430        yahoo_packet_hash(pkt, 13, "1");
4431        yahoo_send_packet(yid, pkt, 0);
4432
4433        yahoo_packet_free(pkt);
4434}
4435
4436void yahoo_send_picture_info(int id, const char *who, const char *url,
4437        int checksum)
4438{
4439        struct yahoo_input_data *yid =
4440                find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
4441        struct yahoo_data *yd;
4442        struct yahoo_packet *pkt;
4443        char checksum_str[10];
4444
4445        if (!yid)
4446                return;
4447
4448        yd = yid->yd;
4449
4450        snprintf(checksum_str, sizeof(checksum_str), "%d", checksum);
4451
4452        pkt = yahoo_packet_new(YAHOO_SERVICE_PICTURE, YPACKET_STATUS_DEFAULT,
4453                0);
4454        yahoo_packet_hash(pkt, 1, yd->user);
4455        yahoo_packet_hash(pkt, 4, yd->user);
4456        yahoo_packet_hash(pkt, 5, who);
4457        yahoo_packet_hash(pkt, 13, "2");
4458        yahoo_packet_hash(pkt, 20, url);
4459        yahoo_packet_hash(pkt, 192, checksum_str);
4460        yahoo_send_packet(yid, pkt, 0);
4461
4462        yahoo_packet_free(pkt);
4463}
4464
4465void yahoo_send_picture_update(int id, const char *who, int type)
4466{
4467        struct yahoo_input_data *yid =
4468                find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
4469        struct yahoo_data *yd;
4470        struct yahoo_packet *pkt;
4471        char type_str[10];
4472
4473        if (!yid)
4474                return;
4475
4476        yd = yid->yd;
4477
4478        snprintf(type_str, sizeof(type_str), "%d", type);
4479
4480        pkt = yahoo_packet_new(YAHOO_SERVICE_PICTURE_UPDATE,
4481                YPACKET_STATUS_DEFAULT, 0);
4482        yahoo_packet_hash(pkt, 1, yd->user);
4483        yahoo_packet_hash(pkt, 5, who);
4484        yahoo_packet_hash(pkt, 206, type_str);
4485        yahoo_send_packet(yid, pkt, 0);
4486
4487        yahoo_packet_free(pkt);
4488}
4489
4490void yahoo_send_picture_checksum(int id, const char *who, int checksum)
4491{
4492        struct yahoo_input_data *yid =
4493                find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
4494        struct yahoo_data *yd;
4495        struct yahoo_packet *pkt;
4496        char checksum_str[10];
4497
4498        if (!yid)
4499                return;
4500
4501        yd = yid->yd;
4502
4503        snprintf(checksum_str, sizeof(checksum_str), "%d", checksum);
4504
4505        pkt = yahoo_packet_new(YAHOO_SERVICE_PICTURE_CHECKSUM,
4506                YPACKET_STATUS_DEFAULT, 0);
4507        yahoo_packet_hash(pkt, 1, yd->user);
4508        if (who != 0)
4509                yahoo_packet_hash(pkt, 5, who);
4510        yahoo_packet_hash(pkt, 192, checksum_str);
4511        yahoo_packet_hash(pkt, 212, "1");
4512        yahoo_send_packet(yid, pkt, 0);
4513
4514        yahoo_packet_free(pkt);
4515}
4516
4517void yahoo_webcam_close_feed(int id, const char *who)
4518{
4519        struct yahoo_input_data *yid =
4520                find_input_by_id_and_webcam_user(id, who);
4521
4522        if (yid)
4523                yahoo_input_close(yid);
4524}
4525
4526void yahoo_webcam_get_feed(int id, const char *who)
4527{
4528        struct yahoo_input_data *yid =
4529                find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
4530        struct yahoo_data *yd;
4531        struct yahoo_packet *pkt;
4532
4533        if (!yid)
4534                return;
4535
4536        /*
4537         * add the user to the queue.  this is a dirty hack, since
4538         * the yahoo server doesn't tell us who's key it's returning,
4539         * we have to just hope that it sends back keys in the same
4540         * order that we request them.
4541         * The queue is popped in yahoo_process_webcam_key
4542         */
4543        webcam_queue = y_list_append(webcam_queue, who ? strdup(who) : NULL);
4544
4545        yd = yid->yd;
4546
4547        pkt = yahoo_packet_new(YAHOO_SERVICE_WEBCAM, YPACKET_STATUS_DEFAULT,
4548                yd->session_id);
4549
4550        yahoo_packet_hash(pkt, 1, yd->user);
4551        if (who != NULL)
4552                yahoo_packet_hash(pkt, 5, who);
4553        yahoo_send_packet(yid, pkt, 0);
4554
4555        yahoo_packet_free(pkt);
4556}
4557
4558void yahoo_webcam_send_image(int id, unsigned char *image, unsigned int length,
4559        unsigned int timestamp)
4560{
4561        struct yahoo_input_data *yid =
4562                find_input_by_id_and_type(id, YAHOO_CONNECTION_WEBCAM);
4563        unsigned char *packet;
4564        unsigned char header_len = 13;
4565        unsigned int pos = 0;
4566
4567        if (!yid)
4568                return;
4569
4570        packet = y_new0(unsigned char, header_len);
4571
4572        packet[pos++] = header_len;
4573        packet[pos++] = 0;
4574        packet[pos++] = 5;      /* version byte?? */
4575        packet[pos++] = 0;
4576        pos += yahoo_put32(packet + pos, length);
4577        packet[pos++] = 2;      /* packet type, image */
4578        pos += yahoo_put32(packet + pos, timestamp);
4579        yahoo_add_to_send_queue(yid, packet, header_len);
4580        FREE(packet);
4581
4582        if (length)
4583                yahoo_add_to_send_queue(yid, image, length);
4584}
4585
4586void yahoo_webcam_accept_viewer(int id, const char *who, int accept)
4587{
4588        struct yahoo_input_data *yid =
4589                find_input_by_id_and_type(id, YAHOO_CONNECTION_WEBCAM);
4590        char *packet = NULL;
4591        char *data = NULL;
4592        unsigned char header_len = 13;
4593        unsigned int pos = 0;
4594        unsigned int len = 0;
4595
4596        if (!yid)
4597                return;
4598
4599        data = strdup("u=");
4600        data = y_string_append(data, (char *)who);
4601        data = y_string_append(data, "\r\n");
4602        len = strlen(data);
4603
4604        packet = y_new0(char, header_len + len);
4605        packet[pos++] = header_len;
4606        packet[pos++] = 0;
4607        packet[pos++] = 5;      /* version byte?? */
4608        packet[pos++] = 0;
4609        pos += yahoo_put32(packet + pos, len);
4610        packet[pos++] = 0;      /* packet type */
4611        pos += yahoo_put32(packet + pos, accept);
4612        memcpy(packet + pos, data, len);
4613        FREE(data);
4614        yahoo_add_to_send_queue(yid, packet, header_len + len);
4615        FREE(packet);
4616}
4617
4618void yahoo_webcam_invite(int id, const char *who)
4619{
4620        struct yahoo_input_data *yid =
4621                find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
4622        struct yahoo_packet *pkt;
4623
4624        if (!yid)
4625                return;
4626
4627        pkt = yahoo_packet_new(YAHOO_SERVICE_NOTIFY, YPACKET_STATUS_NOTIFY,
4628                yid->yd->session_id);
4629
4630        yahoo_packet_hash(pkt, 49, "WEBCAMINVITE");
4631        yahoo_packet_hash(pkt, 14, " ");
4632        yahoo_packet_hash(pkt, 13, "0");
4633        yahoo_packet_hash(pkt, 1, yid->yd->user);
4634        yahoo_packet_hash(pkt, 5, who);
4635        yahoo_send_packet(yid, pkt, 0);
4636
4637        yahoo_packet_free(pkt);
4638}
4639
4640static void yahoo_search_internal(int id, int t, const char *text, int g,
4641        int ar, int photo, int yahoo_only, int startpos, int total)
4642{
4643        struct yahoo_data *yd = find_conn_by_id(id);
4644        struct yahoo_input_data *yid;
4645        char url[1024];
4646        char buff[1024];
4647        char *ctext, *p;
4648
4649        if (!yd)
4650                return;
4651
4652        yid = y_new0(struct yahoo_input_data, 1);
4653        yid->yd = yd;
4654        yid->type = YAHOO_CONNECTION_SEARCH;
4655
4656        /*
4657           age range
4658           .ar=1 - 13-18, 2 - 18-25, 3 - 25-35, 4 - 35-50, 5 - 50-70, 6 - 70+
4659         */
4660
4661        snprintf(buff, sizeof(buff), "&.sq=%%20&.tt=%d&.ss=%d", total,
4662                startpos);
4663
4664        ctext = strdup(text);
4665        while ((p = strchr(ctext, ' ')))
4666                *p = '+';
4667
4668        snprintf(url, 1024,
4669                "http://members.yahoo.com/interests?.oc=m&.kw=%s&.sb=%d&.g=%d&.ar=0%s%s%s",
4670                ctext, t, g, photo ? "&.p=y" : "", yahoo_only ? "&.pg=y" : "",
4671                startpos ? buff : "");
4672
4673        FREE(ctext);
4674
4675        snprintf(buff, sizeof(buff), "Y=%s; T=%s", yd->cookie_y, yd->cookie_t);
4676
4677        inputs = y_list_prepend(inputs, yid);
4678        yahoo_http_get(yid->yd->client_id, url, buff, 0, 0,
4679                _yahoo_http_connected, yid);
4680}
4681
4682void yahoo_search(int id, enum yahoo_search_type t, const char *text,
4683        enum yahoo_search_gender g, enum yahoo_search_agerange ar, int photo,
4684        int yahoo_only)
4685{
4686        struct yahoo_input_data *yid =
4687                find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
4688        struct yahoo_search_state *yss;
4689
4690        if (!yid)
4691                return;
4692
4693        if (!yid->ys)
4694                yid->ys = y_new0(struct yahoo_search_state, 1);
4695
4696        yss = yid->ys;
4697
4698        FREE(yss->lsearch_text);
4699        yss->lsearch_type = t;
4700        yss->lsearch_text = strdup(text);
4701        yss->lsearch_gender = g;
4702        yss->lsearch_agerange = ar;
4703        yss->lsearch_photo = photo;
4704        yss->lsearch_yahoo_only = yahoo_only;
4705
4706        yahoo_search_internal(id, t, text, g, ar, photo, yahoo_only, 0, 0);
4707}
4708
4709void yahoo_search_again(int id, int start)
4710{
4711        struct yahoo_input_data *yid =
4712                find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
4713        struct yahoo_search_state *yss;
4714
4715        if (!yid || !yid->ys)
4716                return;
4717
4718        yss = yid->ys;
4719
4720        if (start == -1)
4721                start = yss->lsearch_nstart + yss->lsearch_nfound;
4722
4723        yahoo_search_internal(id, yss->lsearch_type, yss->lsearch_text,
4724                yss->lsearch_gender, yss->lsearch_agerange,
4725                yss->lsearch_photo, yss->lsearch_yahoo_only,
4726                start, yss->lsearch_ntotal);
4727}
4728
4729void yahoo_send_picture(int id, const char *name, unsigned long size,
4730        yahoo_get_fd_callback callback, void *data)
4731{
4732        /* Not Implemented */
4733}
4734
4735/* File Transfer */
4736static YList *active_file_transfers = NULL;
4737
4738enum {
4739        FT_STATE_HEAD = 1,
4740        FT_STATE_RECV,
4741        FT_STATE_RECV_START,
4742        FT_STATE_SEND
4743};
4744
4745struct send_file_data {
4746        int client_id;
4747        char *id;
4748        char *who;
4749        char *filename;
4750        char *ip_addr;
4751        char *token;
4752        int size;
4753
4754        struct yahoo_input_data *yid;
4755        int state;
4756
4757        yahoo_get_fd_callback callback;
4758        void *data;
4759};
4760
4761static char *yahoo_get_random(void)
4762{
4763        int i = 0;
4764        int r = 0;
4765        int c = 0;
4766        char out[25];
4767
4768        out[24] = '\0';
4769        out[23] = '$';
4770        out[22] = '$';
4771       
4772        for (i = 0; i < 22; i++) {
4773                if(r == 0)
4774                        r = rand();
4775
4776                c = r%61;
4777
4778                if(c<26)
4779                        out[i] = c + 'a';
4780                else if (c<52)
4781                        out[i] = c - 26 + 'A';
4782                else
4783                        out[i] = c - 52 + '0';
4784
4785                r /= 61;
4786        }
4787
4788        return strdup(out);
4789}
4790
4791static int _are_same_id(const void *sfd1, const void *id)
4792{
4793        return strcmp(((struct send_file_data *)sfd1)->id, (char *)id);
4794}
4795
4796static int _are_same_yid(const void *sfd1, const void *yid)
4797{
4798        if(((struct send_file_data *)sfd1)->yid == yid)
4799                return 0;
4800        else
4801                return 1;
4802}
4803
4804static struct send_file_data *yahoo_get_active_transfer(char *id)
4805{
4806        YList *l = y_list_find_custom(active_file_transfers, id,
4807                _are_same_id);
4808
4809        if(l)
4810                return (struct send_file_data *)l->data;
4811       
4812        return NULL;
4813}
4814
4815static struct send_file_data *yahoo_get_active_transfer_with_yid(void *yid)
4816{
4817        YList *l = y_list_find_custom(active_file_transfers, yid,
4818                _are_same_yid);
4819
4820        if(l)
4821                return (struct send_file_data *)l->data;
4822       
4823        return NULL;
4824}
4825
4826static void yahoo_add_active_transfer(struct send_file_data *sfd)
4827{
4828        active_file_transfers = y_list_prepend(active_file_transfers, sfd);
4829}
4830
4831static void yahoo_remove_active_transfer(struct send_file_data *sfd)
4832{
4833        if (sfd == NULL)
4834                return;
4835       
4836        active_file_transfers = y_list_remove(active_file_transfers, sfd);
4837        free(sfd->id);
4838        free(sfd->who);
4839        free(sfd->filename);
4840        free(sfd->ip_addr);
4841        FREE(sfd);
4842}
4843
4844static void _yahoo_ft_upload_connected(int id, void *fd, int error, void *data)
4845{
4846        struct send_file_data *sfd = data;
4847        struct yahoo_input_data *yid = sfd->yid;
4848
4849        if (!fd) {
4850                inputs = y_list_remove(inputs, yid);
4851                FREE(yid);
4852                return;
4853        }
4854
4855        sfd->callback(id, fd, error, sfd->data);
4856
4857        yid->fd = fd;
4858        yid->read_tag =
4859                YAHOO_CALLBACK(ext_yahoo_add_handler) (yid->yd->client_id, fd,
4860                YAHOO_INPUT_READ, yid);
4861}
4862
4863static void yahoo_file_transfer_upload(struct yahoo_data *yd,
4864        struct send_file_data *sfd)
4865{
4866        char url[256];
4867        char buff[4096];
4868        char *sender_enc = NULL, *recv_enc = NULL, *token_enc = NULL;
4869
4870        struct yahoo_input_data *yid = y_new0(struct yahoo_input_data, 1);
4871
4872        yid->yd = yd;
4873        yid->type = YAHOO_CONNECTION_FT;
4874
4875        inputs = y_list_prepend(inputs, yid);
4876        sfd->yid = yid;
4877        sfd->state = FT_STATE_SEND;
4878
4879        token_enc = yahoo_urlencode(sfd->token);
4880        sender_enc = yahoo_urlencode(yd->user);
4881        recv_enc = yahoo_urlencode(sfd->who);
4882
4883        snprintf(url, sizeof(url), 
4884                "http://%s/relay?token=%s&sender=%s&recver=%s", sfd->ip_addr,
4885                token_enc, sender_enc, recv_enc);
4886
4887        snprintf(buff, sizeof(buff), "T=%s; Y=%s", yd->cookie_t, yd->cookie_y);
4888
4889        yahoo_http_post(yd->client_id, url, buff, sfd->size,
4890                _yahoo_ft_upload_connected, sfd);
4891
4892        FREE(token_enc);
4893        FREE(sender_enc);
4894        FREE(recv_enc);
4895}
4896
4897static void yahoo_init_ft_recv(struct yahoo_data *yd,
4898        struct send_file_data *sfd)
4899{
4900        char url[256];
4901        char buff[1024];
4902        char *sender_enc = NULL, *recv_enc = NULL, *token_enc = NULL;
4903
4904        struct yahoo_input_data *yid = y_new0(struct yahoo_input_data, 1);
4905
4906        yid->yd = yd;
4907        yid->type = YAHOO_CONNECTION_FT;
4908
4909        inputs = y_list_prepend(inputs, yid);
4910        sfd->yid = yid;
4911        sfd->state = FT_STATE_HEAD;
4912
4913        token_enc = yahoo_urlencode(sfd->token);
4914        sender_enc = yahoo_urlencode(sfd->who);
4915        recv_enc = yahoo_urlencode(yd->user);
4916
4917        snprintf(url, sizeof(url), 
4918                "http://%s/relay?token=%s&sender=%s&recver=%s", sfd->ip_addr,
4919                token_enc, sender_enc, recv_enc);
4920
4921        snprintf(buff, sizeof(buff), "Y=%s; T=%s", yd->cookie_y, yd->cookie_t);
4922
4923        yahoo_http_head(yid->yd->client_id, url, buff, 0, NULL,
4924                _yahoo_http_connected, yid);
4925
4926        FREE(token_enc);
4927        FREE(sender_enc);
4928        FREE(recv_enc);
4929}
4930
4931static void yahoo_file_transfer_accept(struct yahoo_input_data *yid,
4932        struct send_file_data *sfd)
4933{
4934        struct yahoo_packet *pkt;
4935
4936        pkt = yahoo_packet_new(YAHOO_SERVICE_Y7_FILETRANSFERACCEPT,
4937                YPACKET_STATUS_DEFAULT, yid->yd->session_id);
4938
4939        yahoo_packet_hash(pkt, 1, yid->yd->user);
4940        yahoo_packet_hash(pkt, 5, sfd->who);
4941        yahoo_packet_hash(pkt, 265, sfd->id);
4942        yahoo_packet_hash(pkt, 27, sfd->filename);
4943        yahoo_packet_hash(pkt, 249, "3");
4944        yahoo_packet_hash(pkt, 251, sfd->token);
4945
4946        yahoo_send_packet(yid, pkt, 0);
4947
4948        yahoo_packet_free(pkt);
4949
4950        yahoo_init_ft_recv(yid->yd, sfd);
4951}
4952
4953static void yahoo_process_filetransferaccept(struct yahoo_input_data *yid,
4954        struct yahoo_packet *pkt)
4955{
4956        YList *l;
4957        struct send_file_data *sfd;
4958        char *id = NULL;
4959        char *token = NULL;
4960
4961        for (l = pkt->hash; l; l = l->next) {
4962                struct yahoo_pair *pair = l->data;
4963                switch (pair->key) {
4964                case 4:
4965                        /* who */
4966                        break;
4967                case 5:
4968                        /* Me... don't care */
4969                        break;
4970                case 249:
4971                        break;
4972                case 265:
4973                        id = pair->value;
4974                        break;
4975                case 251:
4976                        token = pair->value;
4977                        break;
4978                case 27:
4979                        /* filename */
4980                        break;
4981                }
4982        }
4983
4984        sfd = yahoo_get_active_transfer(id);
4985
4986        if (sfd) {
4987                sfd->token = strdup(token);
4988
4989                yahoo_file_transfer_upload(yid->yd, sfd);
4990        }
4991        else {
4992                YAHOO_CALLBACK(ext_yahoo_file_transfer_done)
4993                        (yid->yd->client_id, YAHOO_FILE_TRANSFER_UNKNOWN,
4994                        sfd ? sfd->data : NULL);
4995
4996                yahoo_remove_active_transfer(sfd);
4997        }
4998}
4999
5000static void yahoo_process_filetransferinfo(struct yahoo_input_data *yid,
5001        struct yahoo_packet *pkt)
5002{
5003        YList *l;
5004        char *id = NULL;
5005        char *token = NULL;
5006        char *ip_addr = NULL;
5007
5008        struct send_file_data *sfd;
5009
5010        for (l = pkt->hash; l; l = l->next) {
5011                struct yahoo_pair *pair = l->data;
5012                switch (pair->key) {
5013                case 1:
5014                case 4:
5015                        /* who */
5016                        break;
5017                case 5:
5018                        /* Me... don't care */
5019                        break;
5020                case 249:
5021                        break;
5022                case 265:
5023                        id = pair->value;
5024                        break;
5025                case 250:
5026                        ip_addr = pair->value;
5027                        break;
5028                case 251:
5029                        token = pair->value;
5030                        break;
5031                case 27:
5032                        /* filename */
5033                        break;
5034                }
5035        }
5036
5037        sfd = yahoo_get_active_transfer(id);
5038
5039        if (sfd) {
5040                sfd->token = strdup(token);
5041                sfd->ip_addr = strdup(ip_addr);
5042
5043                yahoo_file_transfer_accept(yid, sfd);
5044        }
5045        else {
5046                YAHOO_CALLBACK(ext_yahoo_file_transfer_done)
5047                        (yid->yd->client_id, YAHOO_FILE_TRANSFER_UNKNOWN,
5048                        sfd ? sfd->data : NULL);
5049
5050                yahoo_remove_active_transfer(sfd);
5051        }
5052}
5053
5054static void yahoo_send_filetransferinfo(struct yahoo_data *yd,
5055        struct send_file_data *sfd)
5056{
5057        struct yahoo_input_data *yid;
5058        struct yahoo_packet *pkt;
5059
5060        yid = find_input_by_id_and_type(yd->client_id, YAHOO_CONNECTION_PAGER);
5061        sfd->ip_addr = YAHOO_CALLBACK(ext_yahoo_get_ip_addr)("relay.yahoo.com");
5062
5063        if (!sfd->ip_addr) {
5064                YAHOO_CALLBACK(ext_yahoo_file_transfer_done)
5065                        (yd->client_id, YAHOO_FILE_TRANSFER_RELAY, sfd->data);
5066
5067                yahoo_remove_active_transfer(sfd);
5068
5069                return;
5070        }
5071
5072        pkt = yahoo_packet_new(YAHOO_SERVICE_Y7_FILETRANSFERINFO,
5073                YPACKET_STATUS_DEFAULT, yd->session_id);
5074
5075        yahoo_packet_hash(pkt, 1, yd->user);
5076        yahoo_packet_hash(pkt, 5, sfd->who);
5077        yahoo_packet_hash(pkt, 265, sfd->id);
5078        yahoo_packet_hash(pkt, 27, sfd->filename);
5079        yahoo_packet_hash(pkt, 249, "3");
5080        yahoo_packet_hash(pkt, 250, sfd->ip_addr);
5081
5082        yahoo_send_packet(yid, pkt, 0);
5083
5084        yahoo_packet_free(pkt);
5085}
5086
5087static void yahoo_process_filetransfer(struct yahoo_input_data *yid,
5088        struct yahoo_packet *pkt)
5089{
5090        YList *l;
5091        char *who = NULL;
5092        char *filename = NULL;
5093        char *msg = NULL;
5094        char *id = NULL;
5095        int action = 0;
5096        int size = 0;
5097        struct yahoo_data *yd = yid->yd;
5098
5099        struct send_file_data *sfd;
5100
5101        for (l = pkt->hash; l; l = l->next) {
5102                struct yahoo_pair *pair = l->data;
5103                switch (pair->key) {
5104                case 4:
5105                        who = pair->value;
5106                        break;
5107                case 5:
5108                        /* Me... don't care */
5109                        break;
5110                case 222:
5111                        action = atoi(pair->value);
5112                        break;
5113                case 265:
5114                        id = pair->value;
5115                        break;
5116                case 266: /* Don't know */
5117                        break;
5118                case 302: /* Start Data? */
5119                        break;
5120                case 300:
5121                        break;
5122                case 27:
5123                        filename = pair->value;
5124                        break;
5125                case 28:
5126                        size = atoi(pair->value);
5127                        break;
5128                case 14:
5129                        msg = pair->value;
5130                case 301: /* End Data? */
5131                        break;
5132                case 303:
5133                        break;
5134
5135                }
5136        }
5137
5138        if (action == YAHOO_FILE_TRANSFER_INIT) {
5139                /* Received a FT request from buddy */
5140                sfd = y_new0(struct send_file_data, 1);
5141       
5142                sfd->client_id = yd->client_id;
5143                sfd->id = strdup(id);
5144                sfd->who = strdup(who);
5145                sfd->filename = strdup(filename);
5146                sfd->size = size;
5147       
5148                yahoo_add_active_transfer(sfd);
5149
5150                YAHOO_CALLBACK(ext_yahoo_got_file) (yd->client_id, yd->user,
5151                        who, msg, filename, size, sfd->id);
5152        }
5153        else {
5154                /* Response to our request */
5155                sfd = yahoo_get_active_transfer(id);
5156
5157                if (sfd && action == YAHOO_FILE_TRANSFER_ACCEPT) {
5158                        yahoo_send_filetransferinfo(yd, sfd);
5159                }
5160                else if (!sfd || action == YAHOO_FILE_TRANSFER_REJECT) {
5161                        YAHOO_CALLBACK(ext_yahoo_file_transfer_done)
5162                                (yd->client_id, YAHOO_FILE_TRANSFER_REJECT,
5163                                sfd ? sfd->data : NULL);
5164
5165                        yahoo_remove_active_transfer(sfd);
5166                }
5167        }
5168}
5169
5170void yahoo_send_file(int id, const char *who, const char *msg,
5171        const char *name, unsigned long size,
5172        yahoo_get_fd_callback callback, void *data)
5173{
5174        struct yahoo_packet *pkt = NULL;
5175        char size_str[10];
5176        struct yahoo_input_data *yid;
5177        struct yahoo_data *yd;
5178        struct send_file_data *sfd;
5179       
5180        yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
5181        yd = find_conn_by_id(id);
5182        sfd = y_new0(struct send_file_data, 1);
5183
5184        sfd->client_id = id;
5185        sfd->id = yahoo_get_random();
5186        sfd->who = strdup(who);
5187        sfd->filename = strdup(name);
5188        sfd->size = size;
5189        sfd->callback = callback;
5190        sfd->data = data;
5191
5192        yahoo_add_active_transfer(sfd);
5193
5194        if (!yd)
5195                return;
5196
5197        pkt = yahoo_packet_new(YAHOO_SERVICE_Y7_FILETRANSFER,
5198                YPACKET_STATUS_DEFAULT, yd->session_id);
5199
5200        snprintf(size_str, sizeof(size_str), "%ld", size);
5201
5202        yahoo_packet_hash(pkt, 1, yd->user);
5203        yahoo_packet_hash(pkt, 5, who);
5204        yahoo_packet_hash(pkt, 265, sfd->id);
5205        yahoo_packet_hash(pkt, 222, "1");
5206        yahoo_packet_hash(pkt, 266, "1");
5207        yahoo_packet_hash(pkt, 302, "268");
5208        yahoo_packet_hash(pkt, 300, "268");
5209        yahoo_packet_hash(pkt, 27, name);
5210        yahoo_packet_hash(pkt, 28, size_str);
5211        yahoo_packet_hash(pkt, 301, "268");
5212        yahoo_packet_hash(pkt, 303, "268");
5213
5214        yahoo_send_packet(yid, pkt, 0);
5215
5216        yahoo_packet_free(pkt);
5217}
5218
5219void yahoo_send_file_transfer_response(int client_id, int response, char *id, void *data)
5220{
5221        struct yahoo_packet *pkt = NULL;
5222        char resp[2];
5223        struct yahoo_input_data *yid;
5224       
5225        struct send_file_data *sfd = yahoo_get_active_transfer(id);
5226
5227        sfd->data = data;
5228
5229        yid = find_input_by_id_and_type(client_id, YAHOO_CONNECTION_PAGER);
5230
5231        pkt = yahoo_packet_new(YAHOO_SERVICE_Y7_FILETRANSFER,
5232                YPACKET_STATUS_DEFAULT, yid->yd->session_id);
5233
5234        snprintf(resp, sizeof(resp), "%d", response);
5235
5236        yahoo_packet_hash(pkt, 1, yid->yd->user);
5237        yahoo_packet_hash(pkt, 5, sfd->who);
5238        yahoo_packet_hash(pkt, 265, sfd->id);
5239        yahoo_packet_hash(pkt, 222, resp);
5240
5241        yahoo_send_packet(yid, pkt, 0);
5242
5243        yahoo_packet_free(pkt);
5244
5245        if(response == YAHOO_FILE_TRANSFER_REJECT)
5246                yahoo_remove_active_transfer(sfd);
5247}
5248
5249static void yahoo_process_ft_connection(struct yahoo_input_data *yid, int over)
5250{
5251        struct send_file_data *sfd;
5252        struct yahoo_data *yd = yid->yd;
5253       
5254        sfd = yahoo_get_active_transfer_with_yid(yid);
5255
5256        if (!sfd) {
5257                LOG(("Something funny happened. yid %p has no sfd.\n", yid));
5258                return;
5259        }
5260
5261        /*
5262         * We want to handle only the complete data with HEAD since we don't
5263         * want a situation where both the GET and HEAD are active.
5264         * With SEND, we really can't do much with partial response
5265         */
5266        if ((sfd->state == FT_STATE_HEAD || sfd->state == FT_STATE_SEND) 
5267                        && !over)
5268                return;
5269
5270        if (sfd->state == FT_STATE_HEAD) {
5271                /* Do a GET */
5272                char url[256];
5273                char buff[1024];
5274                char *sender_enc = NULL, *recv_enc = NULL, *token_enc = NULL;
5275
5276                struct yahoo_input_data *yid_ft = 
5277                        y_new0(struct yahoo_input_data, 1);
5278       
5279                yid_ft->yd = yid->yd;
5280                yid_ft->type = YAHOO_CONNECTION_FT;
5281       
5282                inputs = y_list_prepend(inputs, yid_ft);
5283                sfd->yid = yid_ft;
5284                sfd->state = FT_STATE_RECV;
5285
5286                token_enc = yahoo_urlencode(sfd->token);
5287                sender_enc = yahoo_urlencode(sfd->who);
5288                recv_enc = yahoo_urlencode(yd->user);
5289       
5290                snprintf(url, sizeof(url), 
5291                        "http://%s/relay?token=%s&sender=%s&recver=%s", sfd->ip_addr,
5292                        token_enc, sender_enc, recv_enc);
5293
5294                snprintf(buff, sizeof(buff), "Y=%s; T=%s", yd->cookie_y,
5295                        yd->cookie_t);
5296
5297
5298                yahoo_http_get(yd->client_id, url, buff, 1, 1,
5299                        _yahoo_http_connected, yid_ft);
5300
5301                FREE(token_enc);
5302                FREE(sender_enc);
5303                FREE(recv_enc);
5304        }
5305        else if (sfd->state == FT_STATE_RECV || 
5306                sfd->state == FT_STATE_RECV_START) {
5307
5308                unsigned char *data_begin = NULL;
5309
5310                if (yid->rxlen == 0)
5311                        yahoo_remove_active_transfer(sfd);
5312
5313                if (sfd->state != FT_STATE_RECV_START &&
5314                        (data_begin = 
5315                                (unsigned char *)strstr((char *)yid->rxqueue,
5316                                "\r\n\r\n"))) {
5317
5318                        sfd->state = FT_STATE_RECV_START;
5319
5320                        yid->rxlen -= 4+(data_begin-yid->rxqueue)/sizeof(char);
5321                        data_begin += 4;
5322
5323                        if (yid->rxlen > 0)
5324                                YAHOO_CALLBACK(ext_yahoo_got_ft_data) 
5325                                        (yd->client_id, data_begin,
5326                                        yid->rxlen, sfd->data);
5327                }
5328                else if (sfd->state == FT_STATE_RECV_START)
5329                        YAHOO_CALLBACK(ext_yahoo_got_ft_data) (yd->client_id,
5330                                yid->rxqueue, yid->rxlen, sfd->data);
5331
5332                FREE(yid->rxqueue);
5333                yid->rxqueue = NULL;
5334                yid->rxlen = 0;
5335        }
5336        else if (sfd->state == FT_STATE_SEND) {
5337                /* Sent file completed */
5338                int len = 0;
5339                char *off = strstr((char *)yid->rxqueue, "Content-Length: ");
5340
5341                if (off) {
5342                        off += 16;
5343                        len = atoi(off);
5344                }
5345
5346                if (len < sfd->size)
5347                        YAHOO_CALLBACK(ext_yahoo_file_transfer_done)
5348                                (yd->client_id,
5349                                YAHOO_FILE_TRANSFER_FAILED, sfd->data);
5350                else
5351                        YAHOO_CALLBACK(ext_yahoo_file_transfer_done)
5352                                (yd->client_id,
5353                                YAHOO_FILE_TRANSFER_DONE, sfd->data);
5354
5355                yahoo_remove_active_transfer(sfd);
5356        }
5357}
5358
5359/* End File Transfer */
5360
5361enum yahoo_status yahoo_current_status(int id)
5362{
5363        struct yahoo_data *yd = find_conn_by_id(id);
5364        if (!yd)
5365                return YAHOO_STATUS_OFFLINE;
5366        return yd->current_status;
5367}
5368
5369const YList *yahoo_get_buddylist(int id)
5370{
5371        struct yahoo_data *yd = find_conn_by_id(id);
5372        if (!yd)
5373                return NULL;
5374        return yd->buddies;
5375}
5376
5377const YList *yahoo_get_ignorelist(int id)
5378{
5379        struct yahoo_data *yd = find_conn_by_id(id);
5380        if (!yd)
5381                return NULL;
5382        return yd->ignore;
5383}
5384
5385const YList *yahoo_get_identities(int id)
5386{
5387        struct yahoo_data *yd = find_conn_by_id(id);
5388        if (!yd)
5389                return NULL;
5390        return yd->identities;
5391}
5392
5393const char *yahoo_get_cookie(int id, const char *which)
5394{
5395        struct yahoo_data *yd = find_conn_by_id(id);
5396        if (!yd)
5397                return NULL;
5398        if (!strncasecmp(which, "y", 1))
5399                return yd->cookie_y;
5400        if (!strncasecmp(which, "b", 1))
5401                return yd->cookie_b;
5402        if (!strncasecmp(which, "t", 1))
5403                return yd->cookie_t;
5404        if (!strncasecmp(which, "c", 1))
5405                return yd->cookie_c;
5406        if (!strncasecmp(which, "login", 5))
5407                return yd->login_cookie;
5408        return NULL;
5409}
5410
5411const char *yahoo_get_profile_url(void)
5412{
5413        return profile_url;
5414}
Note: See TracBrowser for help on using the repository browser.