source: protocols/yahoo/libyahoo2.c @ 509cf60

Last change on this file since 509cf60 was 509cf60, checked in by Wilmer van der Gaast <wilmer@…>, at 2012-10-20T00:02:45Z

Some more removals. Using ifdefs instead of read deletions in Yahoo! dir to
not make merges too complicated (although upstream really seems dead at this
point :-/).

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