source: protocols/yahoo/libyahoo2.c @ daae10f

Last change on this file since daae10f was daae10f, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-08-07T16:33:02Z

OpenSolaris (non-gcc) fixes, patches from Dagobert Michelsen <dam@…>
with some changes.

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