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

Last change on this file since 3ca001b was 3cd37d7, checked in by Wilmer van der Gaast <wilmer@…>, at 2012-10-20T09:44:59Z

Cleaning up some more Yahoo! symbols.

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