source: protocols/yahoo/libyahoo2.c @ f68fd5f

3.0.2
Last change on this file since f68fd5f was 241f9f6, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-08-22T13:31:53Z

Better handling of yahoo add responses: libyahoo2 seems to (IMHO) wrongly
assume that a non-0 status means the contact is on the list already and
on-line. This causes ugly "imcb_buddy_status() for unknown handle" msgs.

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