source: protocols/yahoo/libyahoo2.c @ 0a3c243

Last change on this file since 0a3c243 was 7ed3199, checked in by Wilmer van der Gaast <wilmer@…>, at 2006-06-25T14:07:01Z

Moved Base64-related functions to a separate file and added decode funtions.

  • Property mode set to 100644
File size: 109.0 KB
Line 
1/*
2 * libyahoo2: libyahoo2.c
3 *
4 * Some code copyright (C) 2002-2004, Philip S Tellis <philip.tellis AT gmx.net>
5 *
6 * Yahoo Search copyright (C) 2003, Konstantin Klyagin <konst AT konst.org.ua>
7 *
8 * Much of this code was taken and adapted from the yahoo module for
9 * gaim released under the GNU GPL.  This code is also released under the
10 * GNU GPL.
11 *
12 * This code is derivitive of Gaim <http://gaim.sourceforge.net>
13 * copyright (C) 1998-1999, Mark Spencer <markster@marko.net>
14 *             1998-1999, Adam Fritzler <afritz@marko.net>
15 *             1998-2002, Rob Flynn <rob@marko.net>
16 *             2000-2002, Eric Warmenhoven <eric@warmenhoven.org>
17 *             2001-2002, Brian Macke <macke@strangelove.net>
18 *                  2001, Anand Biligiri S <abiligiri@users.sf.net>
19 *                  2001, Valdis Kletnieks
20 *                  2002, Sean Egan <bj91704@binghamton.edu>
21 *                  2002, Toby Gray <toby.gray@ntlworld.com>
22 *
23 * This library also uses code from other libraries, namely:
24 *     Portions from libfaim copyright 1998, 1999 Adam Fritzler
25 *     <afritz@auk.cx>
26 *     Portions of Sylpheed copyright 2000-2002 Hiroyuki Yamamoto
27 *     <hiro-y@kcn.ne.jp>
28 *
29 *
30 * This program is free software; you can redistribute it and/or modify
31 * it under the terms of the GNU General Public License as published by
32 * the Free Software Foundation; either version 2 of the License, or
33 * (at your option) any later version.
34 *
35 * This program is distributed in the hope that it will be useful,
36 * but WITHOUT ANY WARRANTY; without even the implied warranty of
37 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
38 * GNU General Public License for more details.
39 *
40 * You should have received a copy of the GNU General Public License
41 * along with this program; if not, write to the Free Software
42 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
43 *
44 */
45
46#ifndef _WIN32
47#include <unistd.h>
48#endif
49#include <errno.h>
50#include <stdio.h>
51#include <stdarg.h>
52
53#if STDC_HEADERS
54# include <string.h>
55#else
56# if !HAVE_STRCHR
57#  define strchr index
58#  define strrchr rindex
59# endif
60char *strchr (), *strrchr ();
61# if !HAVE_MEMCPY
62#  define memcpy(d, s, n) bcopy ((s), (d), (n))
63#  define memmove(d, s, n) bcopy ((s), (d), (n))
64# endif
65#endif
66
67#include <sys/types.h>
68
69#ifdef __MINGW32__
70# include <winsock2.h>
71# define write(a,b,c) send(a,b,c,0)
72# define read(a,b,c)  recv(a,b,c,0)
73#endif
74
75#include <stdlib.h>
76#include <ctype.h>
77
78#include "sha.h"
79#include "md5.h"
80#include "yahoo2.h"
81#include "yahoo_httplib.h"
82#include "yahoo_util.h"
83#include "yahoo_fn.h"
84
85#include "yahoo2_callbacks.h"
86#include "yahoo_debug.h"
87#if defined(__MINGW32__) && !defined(HAVE_GLIB)
88#define snprintf _snprintf
89#define vsnprintf _vsnprintf
90#endif
91
92#include "base64.h"
93
94#ifdef USE_STRUCT_CALLBACKS
95struct yahoo_callbacks *yc=NULL;
96
97void yahoo_register_callbacks(struct yahoo_callbacks * tyc)
98{
99        yc = tyc;
100}
101
102#define YAHOO_CALLBACK(x)       yc->x
103#else
104#define YAHOO_CALLBACK(x)       x
105#endif
106
107int yahoo_log_message(char * fmt, ...)
108{
109        char out[1024];
110        va_list ap;
111        va_start(ap, fmt);
112        vsnprintf(out, sizeof(out), fmt, ap);
113        va_end(ap);
114        return YAHOO_CALLBACK(ext_yahoo_log)("%s", out);
115}
116
117int yahoo_connect(char * host, int port)
118{
119        return YAHOO_CALLBACK(ext_yahoo_connect)(host, port);
120}
121
122static enum yahoo_log_level log_level = YAHOO_LOG_NONE;
123
124enum yahoo_log_level yahoo_get_log_level()
125{
126        return log_level;
127}
128
129int yahoo_set_log_level(enum yahoo_log_level level)
130{
131        enum yahoo_log_level l = log_level;
132        log_level = level;
133        return l;
134}
135
136/* default values for servers */
137static char pager_host[] = "scs.msg.yahoo.com"; 
138static int pager_port = 5050;
139static int fallback_ports[]={23, 25, 80, 20, 119, 8001, 8002, 5050, 0};
140static char filetransfer_host[]="filetransfer.msg.yahoo.com";
141static int filetransfer_port=80;
142static char webcam_host[]="webcam.yahoo.com";
143static int webcam_port=5100;
144static char webcam_description[]="";
145static char local_host[]="";
146static int conn_type=Y_WCM_DSL;
147
148static char profile_url[] = "http://profiles.yahoo.com/";
149
150enum yahoo_service { /* these are easier to see in hex */
151        YAHOO_SERVICE_LOGON = 1,
152        YAHOO_SERVICE_LOGOFF,
153        YAHOO_SERVICE_ISAWAY,
154        YAHOO_SERVICE_ISBACK,
155        YAHOO_SERVICE_IDLE, /* 5 (placemarker) */
156        YAHOO_SERVICE_MESSAGE,
157        YAHOO_SERVICE_IDACT,
158        YAHOO_SERVICE_IDDEACT,
159        YAHOO_SERVICE_MAILSTAT,
160        YAHOO_SERVICE_USERSTAT, /* 0xa */
161        YAHOO_SERVICE_NEWMAIL,
162        YAHOO_SERVICE_CHATINVITE,
163        YAHOO_SERVICE_CALENDAR,
164        YAHOO_SERVICE_NEWPERSONALMAIL,
165        YAHOO_SERVICE_NEWCONTACT,
166        YAHOO_SERVICE_ADDIDENT, /* 0x10 */
167        YAHOO_SERVICE_ADDIGNORE,
168        YAHOO_SERVICE_PING,
169        YAHOO_SERVICE_GOTGROUPRENAME, /* < 1, 36(old), 37(new) */
170        YAHOO_SERVICE_SYSMESSAGE = 0x14,
171        YAHOO_SERVICE_PASSTHROUGH2 = 0x16,
172        YAHOO_SERVICE_CONFINVITE = 0x18,
173        YAHOO_SERVICE_CONFLOGON,
174        YAHOO_SERVICE_CONFDECLINE,
175        YAHOO_SERVICE_CONFLOGOFF,
176        YAHOO_SERVICE_CONFADDINVITE,
177        YAHOO_SERVICE_CONFMSG,
178        YAHOO_SERVICE_CHATLOGON,
179        YAHOO_SERVICE_CHATLOGOFF,
180        YAHOO_SERVICE_CHATMSG = 0x20,
181        YAHOO_SERVICE_GAMELOGON = 0x28,
182        YAHOO_SERVICE_GAMELOGOFF,
183        YAHOO_SERVICE_GAMEMSG = 0x2a,
184        YAHOO_SERVICE_FILETRANSFER = 0x46,
185        YAHOO_SERVICE_VOICECHAT = 0x4A,
186        YAHOO_SERVICE_NOTIFY,
187        YAHOO_SERVICE_VERIFY,
188        YAHOO_SERVICE_P2PFILEXFER,
189        YAHOO_SERVICE_PEERTOPEER = 0x4F,        /* Checks if P2P possible */
190        YAHOO_SERVICE_WEBCAM,
191        YAHOO_SERVICE_AUTHRESP = 0x54,
192        YAHOO_SERVICE_LIST,
193        YAHOO_SERVICE_AUTH = 0x57,
194        YAHOO_SERVICE_ADDBUDDY = 0x83,
195        YAHOO_SERVICE_REMBUDDY,
196        YAHOO_SERVICE_IGNORECONTACT,    /* > 1, 7, 13 < 1, 66, 13, 0*/
197        YAHOO_SERVICE_REJECTCONTACT,
198        YAHOO_SERVICE_GROUPRENAME = 0x89, /* > 1, 65(new), 66(0), 67(old) */
199        YAHOO_SERVICE_CHATONLINE = 0x96, /* > 109(id), 1, 6(abcde) < 0,1*/
200        YAHOO_SERVICE_CHATGOTO,
201        YAHOO_SERVICE_CHATJOIN, /* > 1 104-room 129-1600326591 62-2 */
202        YAHOO_SERVICE_CHATLEAVE,
203        YAHOO_SERVICE_CHATEXIT = 0x9b,
204        YAHOO_SERVICE_CHATLOGOUT = 0xa0,
205        YAHOO_SERVICE_CHATPING,
206        YAHOO_SERVICE_COMMENT = 0xa8
207};
208
209struct yahoo_pair {
210        int key;
211        char *value;
212};
213
214struct yahoo_packet {
215        unsigned short int service;
216        unsigned int status;
217        unsigned int id;
218        YList *hash;
219};
220
221struct yahoo_search_state {
222        int   lsearch_type;
223        char  *lsearch_text;
224        int   lsearch_gender;
225        int   lsearch_agerange;
226        int   lsearch_photo;
227        int   lsearch_yahoo_only;
228        int   lsearch_nstart;
229        int   lsearch_nfound;
230        int   lsearch_ntotal;
231};
232
233struct data_queue {
234        unsigned char *queue;
235        int len;
236};
237
238struct yahoo_input_data {
239        struct yahoo_data *yd;
240        struct yahoo_webcam *wcm;
241        struct yahoo_webcam_data *wcd;
242        struct yahoo_search_state *ys;
243
244        int   fd;
245        enum yahoo_connection_type type;
246       
247        unsigned char   *rxqueue;
248        int   rxlen;
249        int   read_tag;
250
251        YList *txqueues;
252        int   write_tag;
253};
254
255struct yahoo_server_settings {
256        char *pager_host;
257        int   pager_port;
258        char *filetransfer_host;
259        int   filetransfer_port;
260        char *webcam_host;
261        int   webcam_port;
262        char *webcam_description;
263        char *local_host;
264        int   conn_type;
265};
266
267static void * _yahoo_default_server_settings()
268{
269        struct yahoo_server_settings *yss = y_new0(struct yahoo_server_settings, 1);
270
271        yss->pager_host = strdup(pager_host);
272        yss->pager_port = pager_port;
273        yss->filetransfer_host = strdup(filetransfer_host);
274        yss->filetransfer_port = filetransfer_port;
275        yss->webcam_host = strdup(webcam_host);
276        yss->webcam_port = webcam_port;
277        yss->webcam_description = strdup(webcam_description);
278        yss->local_host = strdup(local_host);
279        yss->conn_type = conn_type;
280
281        return yss;
282}
283
284static void * _yahoo_assign_server_settings(va_list ap)
285{
286        struct yahoo_server_settings *yss = _yahoo_default_server_settings();
287        char *key;
288        char *svalue;
289        int   nvalue;
290
291        while(1) {
292                key = va_arg(ap, char *);
293                if(key == NULL)
294                        break;
295
296                if(!strcmp(key, "pager_host")) {
297                        svalue = va_arg(ap, char *);
298                        free(yss->pager_host);
299                        yss->pager_host = strdup(svalue);
300                } else if(!strcmp(key, "pager_port")) {
301                        nvalue = va_arg(ap, int);
302                        yss->pager_port = nvalue;
303                } else if(!strcmp(key, "filetransfer_host")) {
304                        svalue = va_arg(ap, char *);
305                        free(yss->filetransfer_host);
306                        yss->filetransfer_host = strdup(svalue);
307                } else if(!strcmp(key, "filetransfer_port")) {
308                        nvalue = va_arg(ap, int);
309                        yss->filetransfer_port = nvalue;
310                } else if(!strcmp(key, "webcam_host")) {
311                        svalue = va_arg(ap, char *);
312                        free(yss->webcam_host);
313                        yss->webcam_host = strdup(svalue);
314                } else if(!strcmp(key, "webcam_port")) {
315                        nvalue = va_arg(ap, int);
316                        yss->webcam_port = nvalue;
317                } else if(!strcmp(key, "webcam_description")) {
318                        svalue = va_arg(ap, char *);
319                        free(yss->webcam_description);
320                        yss->webcam_description = strdup(svalue);
321                } else if(!strcmp(key, "local_host")) {
322                        svalue = va_arg(ap, char *);
323                        free(yss->local_host);
324                        yss->local_host = strdup(svalue);
325                } else if(!strcmp(key, "conn_type")) {
326                        nvalue = va_arg(ap, int);
327                        yss->conn_type = nvalue;
328                } else {
329                        WARNING(("Unknown key passed to yahoo_init, "
330                                "perhaps you didn't terminate the list "
331                                "with NULL"));
332                }
333        }
334
335        return yss;
336}
337
338static void yahoo_free_server_settings(struct yahoo_server_settings *yss)
339{
340        if(!yss)
341                return;
342
343        free(yss->pager_host);
344        free(yss->filetransfer_host);
345        free(yss->webcam_host);
346        free(yss->webcam_description);
347        free(yss->local_host);
348
349        free(yss);
350}
351
352static YList *conns=NULL;
353static YList *inputs=NULL;
354static int last_id=0;
355
356static void add_to_list(struct yahoo_data *yd)
357{
358        conns = y_list_prepend(conns, yd);
359}
360static struct yahoo_data * find_conn_by_id(int id)
361{
362        YList *l;
363        for(l = conns; l; l = y_list_next(l)) {
364                struct yahoo_data *yd = l->data;
365                if(yd->client_id == id)
366                        return yd;
367        }
368        return NULL;
369}
370static void del_from_list(struct yahoo_data *yd)
371{
372        conns = y_list_remove(conns, yd);
373}
374
375/* call repeatedly to get the next one */
376/*
377static struct yahoo_input_data * find_input_by_id(int id)
378{
379        YList *l;
380        for(l = inputs; l; l = y_list_next(l)) {
381                struct yahoo_input_data *yid = l->data;
382                if(yid->yd->client_id == id)
383                        return yid;
384        }
385        return NULL;
386}
387*/
388
389static struct yahoo_input_data * find_input_by_id_and_webcam_user(int id, const char * who)
390{
391        YList *l;
392        LOG(("find_input_by_id_and_webcam_user"));
393        for(l = inputs; l; l = y_list_next(l)) {
394                struct yahoo_input_data *yid = l->data;
395                if(yid->type == YAHOO_CONNECTION_WEBCAM && yid->yd->client_id == id
396                                && yid->wcm && 
397                                ((who && yid->wcm->user && !strcmp(who, yid->wcm->user)) ||
398                                 !(yid->wcm->user && !who)))
399                        return yid;
400        }
401        return NULL;
402}
403
404static struct yahoo_input_data * find_input_by_id_and_type(int id, enum yahoo_connection_type type)
405{
406        YList *l;
407        LOG(("find_input_by_id_and_type"));
408        for(l = inputs; l; l = y_list_next(l)) {
409                struct yahoo_input_data *yid = l->data;
410                if(yid->type == type && yid->yd->client_id == id)
411                        return yid;
412        }
413        return NULL;
414}
415
416static struct yahoo_input_data * find_input_by_id_and_fd(int id, int fd)
417{
418        YList *l;
419        LOG(("find_input_by_id_and_fd"));
420        for(l = inputs; l; l = y_list_next(l)) {
421                struct yahoo_input_data *yid = l->data;
422                if(yid->fd == fd && yid->yd->client_id == id)
423                        return yid;
424        }
425        return NULL;
426}
427
428static int count_inputs_with_id(int id)
429{
430        int c=0;
431        YList *l;
432        LOG(("counting %d", id));
433        for(l = inputs; l; l = y_list_next(l)) {
434                struct yahoo_input_data *yid = l->data;
435                if(yid->yd->client_id == id)
436                        c++;
437        }
438        LOG(("%d", c));
439        return c;
440}
441
442
443extern char *yahoo_crypt(char *, char *);
444
445/* Free a buddy list */
446static void yahoo_free_buddies(YList * list)
447{
448        YList *l;
449
450        for(l = list; l; l = l->next)
451        {
452                struct yahoo_buddy *bud = l->data;
453                if(!bud)
454                        continue;
455
456                FREE(bud->group);
457                FREE(bud->id);
458                FREE(bud->real_name);
459                if(bud->yab_entry) {
460                        FREE(bud->yab_entry->fname);
461                        FREE(bud->yab_entry->lname);
462                        FREE(bud->yab_entry->nname);
463                        FREE(bud->yab_entry->id);
464                        FREE(bud->yab_entry->email);
465                        FREE(bud->yab_entry->hphone);
466                        FREE(bud->yab_entry->wphone);
467                        FREE(bud->yab_entry->mphone);
468                        FREE(bud->yab_entry);
469                }
470                FREE(bud);
471                l->data = bud = NULL;
472        }
473
474        y_list_free(list);
475}
476
477/* Free an identities list */
478static void yahoo_free_identities(YList * list)
479{
480        while (list) {
481                YList *n = list;
482                FREE(list->data);
483                list = y_list_remove_link(list, list);
484                y_list_free_1(n);
485        }
486}
487
488/* Free webcam data */
489static void yahoo_free_webcam(struct yahoo_webcam *wcm)
490{
491        if (wcm) {
492                FREE(wcm->user);
493                FREE(wcm->server);
494                FREE(wcm->key);
495                FREE(wcm->description);
496                FREE(wcm->my_ip);
497        }
498        FREE(wcm);
499}
500
501static void yahoo_free_data(struct yahoo_data *yd)
502{
503        FREE(yd->user);
504        FREE(yd->password);
505        FREE(yd->cookie_y);
506        FREE(yd->cookie_t);
507        FREE(yd->cookie_c);
508        FREE(yd->login_cookie);
509        FREE(yd->login_id);
510
511        yahoo_free_buddies(yd->buddies);
512        yahoo_free_buddies(yd->ignore);
513        yahoo_free_identities(yd->identities);
514
515        yahoo_free_server_settings(yd->server_settings);
516
517        FREE(yd);
518}
519
520#define YAHOO_PACKET_HDRLEN (4 + 2 + 2 + 2 + 2 + 4 + 4)
521
522static struct yahoo_packet *yahoo_packet_new(enum yahoo_service service, 
523                enum yahoo_status status, int id)
524{
525        struct yahoo_packet *pkt = y_new0(struct yahoo_packet, 1);
526
527        pkt->service = service;
528        pkt->status = status;
529        pkt->id = id;
530
531        return pkt;
532}
533
534static void yahoo_packet_hash(struct yahoo_packet *pkt, int key, const char *value)
535{
536        struct yahoo_pair *pair = y_new0(struct yahoo_pair, 1);
537        pair->key = key;
538        pair->value = strdup(value);
539        pkt->hash = y_list_append(pkt->hash, pair);
540}
541
542static int yahoo_packet_length(struct yahoo_packet *pkt)
543{
544        YList *l;
545
546        int len = 0;
547
548        for (l = pkt->hash; l; l = l->next) {
549                struct yahoo_pair *pair = l->data;
550                int tmp = pair->key;
551                do {
552                        tmp /= 10;
553                        len++;
554                } while (tmp);
555                len += 2;
556                len += strlen(pair->value);
557                len += 2;
558        }
559
560        return len;
561}
562
563#define yahoo_put16(buf, data) ( \
564                (*(buf) = (unsigned char)((data)>>8)&0xff), \
565                (*((buf)+1) = (unsigned char)(data)&0xff),  \
566                2)
567#define yahoo_get16(buf) ((((*(buf))&0xff)<<8) + ((*((buf)+1)) & 0xff))
568#define yahoo_put32(buf, data) ( \
569                (*((buf)) = (unsigned char)((data)>>24)&0xff), \
570                (*((buf)+1) = (unsigned char)((data)>>16)&0xff), \
571                (*((buf)+2) = (unsigned char)((data)>>8)&0xff), \
572                (*((buf)+3) = (unsigned char)(data)&0xff), \
573                4)
574#define yahoo_get32(buf) ((((*(buf)   )&0xff)<<24) + \
575                         (((*((buf)+1))&0xff)<<16) + \
576                         (((*((buf)+2))&0xff)<< 8) + \
577                         (((*((buf)+3))&0xff)))
578
579static void yahoo_packet_read(struct yahoo_packet *pkt, unsigned char *data, int len)
580{
581        int pos = 0;
582
583        while (pos + 1 < len) {
584                char *key, *value = NULL;
585                int accept;
586                int x;
587
588                struct yahoo_pair *pair = y_new0(struct yahoo_pair, 1);
589
590                key = malloc(len + 1);
591                x = 0;
592                while (pos + 1 < len) {
593                        if (data[pos] == 0xc0 && data[pos + 1] == 0x80)
594                                break;
595                        key[x++] = data[pos++];
596                }
597                key[x] = 0;
598                pos += 2;
599                pair->key = strtol(key, NULL, 10);
600                free(key);
601               
602                /* Libyahoo2 developer(s) don't seem to have the time to fix
603                   this problem, so for now try to work around it:
604                   
605                   Sometimes we receive an invalid packet with not any more
606                   data at this point. I don't know how to handle this in a
607                   clean way, but let's hope this is clean enough: */
608               
609                if (pos + 1 < len) {
610                        accept = x; 
611                        /* if x is 0 there was no key, so don't accept it */
612                        if (accept)
613                                value = malloc(len - pos + 1);
614                        x = 0;
615                        while (pos + 1 < len) {
616                                if (data[pos] == 0xc0 && data[pos + 1] == 0x80)
617                                        break;
618                                if (accept)
619                                        value[x++] = data[pos++];
620                        }
621                        if (accept)
622                                value[x] = 0;
623                        pos += 2;
624                } else {
625                        accept = 0;
626                }
627               
628                if (accept) {
629                        pair->value = strdup(value);
630                        FREE(value);
631                        pkt->hash = y_list_append(pkt->hash, pair);
632                        DEBUG_MSG(("Key: %d  \tValue: %s", pair->key, pair->value));
633                } else {
634                        FREE(pair);
635                }
636        }
637}
638
639static void yahoo_packet_write(struct yahoo_packet *pkt, unsigned char *data)
640{
641        YList *l;
642        int pos = 0;
643
644        for (l = pkt->hash; l; l = l->next) {
645                struct yahoo_pair *pair = l->data;
646                unsigned char buf[100];
647
648                snprintf((char *)buf, sizeof(buf), "%d", pair->key);
649                strcpy((char *)data + pos, (char *)buf);
650                pos += strlen((char *)buf);
651                data[pos++] = 0xc0;
652                data[pos++] = 0x80;
653
654                strcpy((char *)data + pos, pair->value);
655                pos += strlen(pair->value);
656                data[pos++] = 0xc0;
657                data[pos++] = 0x80;
658        }
659}
660
661static void yahoo_dump_unhandled(struct yahoo_packet *pkt)
662{
663        YList *l;
664
665        NOTICE(("Service: 0x%02x\tStatus: %d", pkt->service, pkt->status));
666        for (l = pkt->hash; l; l = l->next) {
667                struct yahoo_pair *pair = l->data;
668                NOTICE(("\t%d => %s", pair->key, pair->value));
669        }
670}
671
672
673static void yahoo_packet_dump(unsigned char *data, int len)
674{
675        if(yahoo_get_log_level() >= YAHOO_LOG_DEBUG) {
676                int i;
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                        YAHOO_CALLBACK(ext_yahoo_log)("%02x ", data[i]);
683                }
684                YAHOO_CALLBACK(ext_yahoo_log)("\n");
685                for (i = 0; i < len; i++) {
686                        if ((i % 8 == 0) && i)
687                                YAHOO_CALLBACK(ext_yahoo_log)(" ");
688                        if ((i % 16 == 0) && i)
689                                YAHOO_CALLBACK(ext_yahoo_log)("\n");
690                        if (isprint(data[i]))
691                                YAHOO_CALLBACK(ext_yahoo_log)(" %c ", data[i]);
692                        else
693                                YAHOO_CALLBACK(ext_yahoo_log)(" . ");
694                }
695                YAHOO_CALLBACK(ext_yahoo_log)("\n");
696        }
697}
698
699/* raw bytes in quasi-big-endian order to base 64 string (NUL-terminated) */
700static void to_y64(unsigned char *out, const unsigned char *in, int inlen)
701{
702        base64_encode_real(in, inlen, out, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789._-");
703}
704
705static void yahoo_add_to_send_queue(struct yahoo_input_data *yid, void *data, int length)
706{
707        struct data_queue *tx = y_new0(struct data_queue, 1);
708        tx->queue = y_new0(unsigned char, length);
709        tx->len = length;
710        memcpy(tx->queue, data, length);
711
712        yid->txqueues = y_list_append(yid->txqueues, tx);
713
714        if(!yid->write_tag)
715                yid->write_tag=YAHOO_CALLBACK(ext_yahoo_add_handler)(yid->yd->client_id, yid->fd, YAHOO_INPUT_WRITE, yid);
716}
717
718static void yahoo_send_packet(struct yahoo_input_data *yid, struct yahoo_packet *pkt, int extra_pad)
719{
720        int pktlen = yahoo_packet_length(pkt);
721        int len = YAHOO_PACKET_HDRLEN + pktlen;
722
723        unsigned char *data;
724        int pos = 0;
725
726        if (yid->fd < 0)
727                return;
728
729        data = y_new0(unsigned char, len + 1);
730
731        memcpy(data + pos, "YMSG", 4); pos += 4;
732        pos += yahoo_put16(data + pos, 0x0a00);
733        pos += yahoo_put16(data + pos, 0x0000);
734        pos += yahoo_put16(data + pos, pktlen + extra_pad);
735        pos += yahoo_put16(data + pos, pkt->service);
736        pos += yahoo_put32(data + pos, pkt->status);
737        pos += yahoo_put32(data + pos, pkt->id);
738
739        yahoo_packet_write(pkt, data + pos);
740
741        yahoo_packet_dump(data, len);
742       
743        yahoo_add_to_send_queue(yid, data, len);
744        FREE(data);
745}
746
747static void yahoo_packet_free(struct yahoo_packet *pkt)
748{
749        while (pkt->hash) {
750                struct yahoo_pair *pair = pkt->hash->data;
751                YList *tmp;
752                FREE(pair->value);
753                FREE(pair);
754                tmp = pkt->hash;
755                pkt->hash = y_list_remove_link(pkt->hash, pkt->hash);
756                y_list_free_1(tmp);
757        }
758        FREE(pkt);
759}
760
761static int yahoo_send_data(int fd, void *data, int len)
762{
763        int ret;
764        int e;
765
766        if (fd < 0)
767                return -1;
768
769        yahoo_packet_dump(data, len);
770
771        do {
772                ret = write(fd, data, len);
773        } while(ret == -1 && errno==EINTR);
774        e=errno;
775
776        if (ret == -1)  {
777                LOG(("wrote data: ERR %s", strerror(errno)));
778        } else {
779                LOG(("wrote data: OK"));
780        }
781
782        errno=e;
783        return ret;
784}
785
786void yahoo_close(int id) 
787{
788        struct yahoo_data *yd = find_conn_by_id(id);
789        if(!yd)
790                return;
791
792        del_from_list(yd);
793
794        yahoo_free_data(yd);
795        if(id == last_id)
796                last_id--;
797}
798
799static void yahoo_input_close(struct yahoo_input_data *yid) 
800{
801        inputs = y_list_remove(inputs, yid);
802
803        LOG(("yahoo_input_close(read)")); 
804        YAHOO_CALLBACK(ext_yahoo_remove_handler)(yid->yd->client_id, yid->read_tag);
805        LOG(("yahoo_input_close(write)")); 
806        YAHOO_CALLBACK(ext_yahoo_remove_handler)(yid->yd->client_id, yid->write_tag);
807        yid->read_tag = yid->write_tag = 0;
808        if(yid->fd)
809                close(yid->fd);
810        yid->fd = 0;
811        FREE(yid->rxqueue);
812        if(count_inputs_with_id(yid->yd->client_id) == 0) {
813                LOG(("closing %d", yid->yd->client_id));
814                yahoo_close(yid->yd->client_id);
815        }
816        yahoo_free_webcam(yid->wcm);
817        if(yid->wcd)
818                FREE(yid->wcd);
819        if(yid->ys) {
820                FREE(yid->ys->lsearch_text);
821                FREE(yid->ys);
822        }
823        FREE(yid);
824}
825
826static int is_same_bud(const void * a, const void * b) {
827        const struct yahoo_buddy *subject = a;
828        const struct yahoo_buddy *object = b;
829
830        return strcmp(subject->id, object->id);
831}
832
833static YList * bud_str2list(char *rawlist)
834{
835        YList * l = NULL;
836
837        char **lines;
838        char **split;
839        char **buddies;
840        char **tmp, **bud;
841
842        lines = y_strsplit(rawlist, "\n", -1);
843        for (tmp = lines; *tmp; tmp++) {
844                struct yahoo_buddy *newbud;
845
846                split = y_strsplit(*tmp, ":", 2);
847                if (!split)
848                        continue;
849                if (!split[0] || !split[1]) {
850                        y_strfreev(split);
851                        continue;
852                }
853                buddies = y_strsplit(split[1], ",", -1);
854
855                for (bud = buddies; bud && *bud; bud++) {
856                        newbud = y_new0(struct yahoo_buddy, 1);
857                        newbud->id = strdup(*bud);
858                        newbud->group = strdup(split[0]);
859
860                        if(y_list_find_custom(l, newbud, is_same_bud)) {
861                                FREE(newbud->id);
862                                FREE(newbud->group);
863                                FREE(newbud);
864                                continue;
865                        }
866
867                        newbud->real_name = NULL;
868
869                        l = y_list_append(l, newbud);
870
871                        NOTICE(("Added buddy %s to group %s", newbud->id, newbud->group));
872                }
873
874                y_strfreev(buddies);
875                y_strfreev(split);
876        }
877        y_strfreev(lines);
878
879        return l;
880}
881
882static char * getcookie(char *rawcookie)
883{
884        char * cookie=NULL;
885        char * tmpcookie; 
886        char * cookieend;
887
888        if (strlen(rawcookie) < 2) 
889                return NULL;
890
891        tmpcookie = strdup(rawcookie+2);
892        cookieend = strchr(tmpcookie, ';');
893
894        if(cookieend)
895                *cookieend = '\0';
896
897        cookie = strdup(tmpcookie);
898        FREE(tmpcookie);
899        /* cookieend=NULL;  not sure why this was there since the value is not preserved in the stack -dd */
900
901        return cookie;
902}
903
904static char * getlcookie(char *cookie)
905{
906        char *tmp;
907        char *tmpend;
908        char *login_cookie = NULL;
909
910        tmpend = strstr(cookie, "n=");
911        if(tmpend) {
912                tmp = strdup(tmpend+2);
913                tmpend = strchr(tmp, '&');
914                if(tmpend)
915                        *tmpend='\0';
916                login_cookie = strdup(tmp);
917                FREE(tmp);
918        }
919
920        return login_cookie;
921}
922
923static void yahoo_process_notify(struct yahoo_input_data *yid, struct yahoo_packet *pkt)
924{
925        struct yahoo_data *yd = yid->yd;
926        char *msg = NULL;
927        char *from = NULL;
928        int stat = 0;
929        int accept = 0;
930        char *ind = NULL;
931        YList *l;
932        for (l = pkt->hash; l; l = l->next) {
933                struct yahoo_pair *pair = l->data;
934                if (pair->key == 4)
935                        from = pair->value;
936                if (pair->key == 49)
937                        msg = pair->value;
938                if (pair->key == 13)
939                        stat = atoi(pair->value);
940                if (pair->key == 14)
941                        ind = pair->value;
942                if (pair->key == 16) {  /* status == -1 */
943                        NOTICE((pair->value));
944                        return;
945                }
946
947        }
948
949        if (!msg)
950                return;
951       
952        if (!strncasecmp(msg, "TYPING", strlen("TYPING"))) 
953                YAHOO_CALLBACK(ext_yahoo_typing_notify)(yd->client_id, from, stat);
954        else if (!strncasecmp(msg, "GAME", strlen("GAME"))) 
955                YAHOO_CALLBACK(ext_yahoo_game_notify)(yd->client_id, from, stat);
956        else if (!strncasecmp(msg, "WEBCAMINVITE", strlen("WEBCAMINVITE"))) 
957        {
958                if (!strcmp(ind, " ")) {
959                        YAHOO_CALLBACK(ext_yahoo_webcam_invite)(yd->client_id, from);
960                } else {
961                        accept = atoi(ind);
962                        /* accept the invitation (-1 = deny 1 = accept) */
963                        if (accept < 0)
964                                accept = 0;
965                        YAHOO_CALLBACK(ext_yahoo_webcam_invite_reply)(yd->client_id, from, accept);
966                }
967        }
968        else
969                LOG(("Got unknown notification: %s", msg));
970}
971
972static void yahoo_process_filetransfer(struct yahoo_input_data *yid, struct yahoo_packet *pkt)
973{
974        struct yahoo_data *yd = yid->yd;
975        char *from=NULL;
976        char *to=NULL;
977        char *msg=NULL;
978        char *url=NULL;
979        long expires=0;
980
981        char *service=NULL;
982
983        char *filename=NULL;
984        unsigned long filesize=0L;
985
986        YList *l;
987        for (l = pkt->hash; l; l = l->next) {
988                struct yahoo_pair *pair = l->data;
989                if (pair->key == 4)
990                        from = pair->value;
991                if (pair->key == 5)
992                        to = pair->value;
993                if (pair->key == 14)
994                        msg = pair->value;
995                if (pair->key == 20)
996                        url = pair->value;
997                if (pair->key == 38)
998                        expires = atol(pair->value);
999
1000                if (pair->key == 27)
1001                        filename = pair->value;
1002                if (pair->key == 28)
1003                        filesize = atol(pair->value);
1004
1005                if (pair->key == 49)
1006                        service = pair->value;
1007        }
1008
1009        if(pkt->service == YAHOO_SERVICE_P2PFILEXFER) {
1010                if(strcmp("FILEXFER", service) != 0) {
1011                        WARNING(("unhandled service 0x%02x", pkt->service));
1012                        yahoo_dump_unhandled(pkt);
1013                        return;
1014                }
1015        }
1016
1017        if(msg) {
1018                char *tmp;
1019                tmp = strchr(msg, '\006');
1020                if(tmp)
1021                        *tmp = '\0';
1022        }
1023        if(url && from)
1024                YAHOO_CALLBACK(ext_yahoo_got_file)(yd->client_id, from, url, expires, msg, filename, filesize);
1025
1026}
1027
1028static void yahoo_process_conference(struct yahoo_input_data *yid, struct yahoo_packet *pkt)
1029{
1030        struct yahoo_data *yd = yid->yd;
1031        char *msg = NULL;
1032        char *host = NULL;
1033        char *who = NULL;
1034        char *room = NULL;
1035        char *id = NULL;
1036        int  utf8 = 0;
1037        YList *members = NULL;
1038        YList *l;
1039       
1040        for (l = pkt->hash; l; l = l->next) {
1041                struct yahoo_pair *pair = l->data;
1042                if (pair->key == 50)
1043                        host = pair->value;
1044               
1045                if (pair->key == 52) {          /* invite */
1046                        members = y_list_append(members, strdup(pair->value));
1047                }
1048                if (pair->key == 53)            /* logon */
1049                        who = pair->value;
1050                if (pair->key == 54)            /* decline */
1051                        who = pair->value;
1052                if (pair->key == 55)            /* unavailable (status == 2) */
1053                        who = pair->value;
1054                if (pair->key == 56)            /* logoff */
1055                        who = pair->value;
1056
1057                if (pair->key == 57)
1058                        room = pair->value;
1059
1060                if (pair->key == 58)            /* join message */
1061                        msg = pair->value;
1062                if (pair->key == 14)            /* decline/conf message */
1063                        msg = pair->value;
1064
1065                if (pair->key == 13)
1066                        ;
1067                if (pair->key == 16)            /* error */
1068                        msg = pair->value;
1069
1070                if (pair->key == 1)             /* my id */
1071                        id = pair->value;
1072                if (pair->key == 3)             /* message sender */
1073                        who = pair->value;
1074
1075                if (pair->key == 97)
1076                        utf8 = atoi(pair->value);
1077        }
1078
1079        if(!room)
1080                return;
1081
1082        if(host) {
1083                for(l = members; l; l = l->next) {
1084                        char * w = l->data;
1085                        if(!strcmp(w, host))
1086                                break;
1087                }
1088                if(!l)
1089                        members = y_list_append(members, strdup(host));
1090        }
1091        /* invite, decline, join, left, message -> status == 1 */
1092
1093        switch(pkt->service) {
1094        case YAHOO_SERVICE_CONFINVITE:
1095                if(pkt->status == 2)
1096                        ;
1097                else if(members)
1098                        YAHOO_CALLBACK(ext_yahoo_got_conf_invite)(yd->client_id, host, room, msg, members);
1099                else if(msg)
1100                        YAHOO_CALLBACK(ext_yahoo_error)(yd->client_id, msg, 0);
1101                break;
1102        case YAHOO_SERVICE_CONFADDINVITE:
1103                if(pkt->status == 2)
1104                        ;
1105                else
1106                        YAHOO_CALLBACK(ext_yahoo_got_conf_invite)(yd->client_id, host, room, msg, members);
1107                break;
1108        case YAHOO_SERVICE_CONFDECLINE:
1109                if(who)
1110                        YAHOO_CALLBACK(ext_yahoo_conf_userdecline)(yd->client_id, who, room, msg);
1111                break;
1112        case YAHOO_SERVICE_CONFLOGON:
1113                if(who)
1114                        YAHOO_CALLBACK(ext_yahoo_conf_userjoin)(yd->client_id, who, room);
1115                break;
1116        case YAHOO_SERVICE_CONFLOGOFF:
1117                if(who)
1118                        YAHOO_CALLBACK(ext_yahoo_conf_userleave)(yd->client_id, who, room);
1119                break;
1120        case YAHOO_SERVICE_CONFMSG:
1121                if(who)
1122                        YAHOO_CALLBACK(ext_yahoo_conf_message)(yd->client_id, who, room, msg, utf8);
1123                break;
1124        }
1125}
1126
1127static void yahoo_process_chat(struct yahoo_input_data *yid, struct yahoo_packet *pkt)
1128{
1129        char *msg = NULL;
1130        char *who = NULL;
1131        char *room = NULL;
1132        char *topic = NULL;
1133        YList *members = NULL;
1134        struct yahoo_chat_member *currentmember = NULL;
1135        int  msgtype = 1;
1136        int  utf8 = 0;
1137        int  firstjoin = 0;
1138        int  membercount = 0;
1139        int  chaterr=0;
1140        YList *l;
1141       
1142        yahoo_dump_unhandled(pkt);
1143        for (l = pkt->hash; l; l = l->next) {
1144                struct yahoo_pair *pair = l->data;
1145
1146                if (pair->key == 104) {
1147                        /* Room name */
1148                        room = pair->value;
1149                }
1150
1151                if (pair->key == 105) {
1152                        /* Room topic */
1153                        topic = pair->value;
1154                }
1155
1156                if (pair->key == 108) {
1157                        /* Number of members in this packet */
1158                        membercount = atoi(pair->value);
1159                }
1160
1161                if (pair->key == 109) {
1162                        /* message sender */
1163                        who = pair->value;
1164
1165                        if (pkt->service == YAHOO_SERVICE_CHATJOIN) {
1166                                currentmember = y_new0(struct yahoo_chat_member, 1);
1167                                currentmember->id = strdup(pair->value);
1168                                members = y_list_append(members, currentmember);
1169                        }
1170                }
1171
1172                if (pair->key == 110) {
1173                        /* age */
1174                        if (pkt->service == YAHOO_SERVICE_CHATJOIN)
1175                                currentmember->age = atoi(pair->value);
1176                }
1177
1178                if (pair->key == 113) {
1179                        /* attribs */
1180                        if (pkt->service == YAHOO_SERVICE_CHATJOIN)
1181                                currentmember->attribs = atoi(pair->value);
1182                }
1183
1184                if (pair->key == 141) {
1185                        /* alias */
1186                        if (pkt->service == YAHOO_SERVICE_CHATJOIN)
1187                                currentmember->alias = strdup(pair->value);
1188                }
1189
1190                if (pair->key == 142) {
1191                        /* location */
1192                        if (pkt->service == YAHOO_SERVICE_CHATJOIN)
1193                                currentmember->location = strdup(pair->value);
1194                }
1195
1196
1197                if (pair->key == 130) {
1198                        /* first join */
1199                        firstjoin = 1;
1200                }
1201
1202                if (pair->key == 117) {
1203                        /* message */
1204                        msg = pair->value;
1205                }
1206
1207                if (pair->key == 124) {
1208                        /* Message type */
1209                        msgtype = atoi(pair->value);
1210                }
1211                if (pair->key == 114) {
1212                        /* message error not sure what all the pair values mean */
1213                        /* but -1 means no session in room */
1214                        chaterr= atoi(pair->value);
1215                }
1216        }
1217
1218        if(!room) {
1219                if (pkt->service == YAHOO_SERVICE_CHATLOGOUT) { /* yahoo originated chat logout */
1220                        YAHOO_CALLBACK(ext_yahoo_chat_yahoologout)(yid->yd->client_id);
1221                        return ;
1222                }
1223                if (pkt->service == YAHOO_SERVICE_COMMENT && chaterr)  {
1224                        YAHOO_CALLBACK(ext_yahoo_chat_yahooerror)(yid->yd->client_id);
1225                        return ;
1226                }
1227
1228                WARNING(("We didn't get a room name, ignoring packet"));
1229                return;
1230        }
1231
1232        switch(pkt->service) {
1233        case YAHOO_SERVICE_CHATJOIN:
1234                if(y_list_length(members) != membercount) {
1235                        WARNING(("Count of members doesn't match No. of members we got"));
1236                }
1237                if(firstjoin && members) {
1238                        YAHOO_CALLBACK(ext_yahoo_chat_join)(yid->yd->client_id, room, topic, members, yid->fd);
1239                } else if(who) {
1240                        if(y_list_length(members) != 1) {
1241                                WARNING(("Got more than 1 member on a normal join"));
1242                        }
1243                        /* this should only ever have one, but just in case */
1244                        while(members) {
1245                                YList *n = members->next;
1246                                currentmember = members->data;
1247                                YAHOO_CALLBACK(ext_yahoo_chat_userjoin)(yid->yd->client_id, room, currentmember);
1248                                y_list_free_1(members);
1249                                members=n;
1250                        }
1251                }
1252                break;
1253        case YAHOO_SERVICE_CHATEXIT:
1254                if(who) {
1255                        YAHOO_CALLBACK(ext_yahoo_chat_userleave)(yid->yd->client_id, room, who);
1256                }
1257                break;
1258        case YAHOO_SERVICE_COMMENT:
1259                if(who) {
1260                        YAHOO_CALLBACK(ext_yahoo_chat_message)(yid->yd->client_id, who, room, msg, msgtype, utf8);
1261                }
1262                break;
1263        }
1264}
1265
1266static void yahoo_process_message(struct yahoo_input_data *yid, struct yahoo_packet *pkt)
1267{
1268        struct yahoo_data *yd = yid->yd;
1269        YList *l;
1270        YList * messages = NULL;
1271
1272        struct m {
1273                int  i_31;
1274                int  i_32;
1275                char *to;
1276                char *from;
1277                long tm;
1278                char *msg;
1279                int  utf8;
1280        } *message = y_new0(struct m, 1);
1281
1282        for (l = pkt->hash; l; l = l->next) {
1283                struct yahoo_pair *pair = l->data;
1284                if (pair->key == 1 || pair->key == 4)
1285                {
1286                        if(!message->from)
1287                                message->from = pair->value;
1288                }
1289                else if (pair->key == 5)
1290                        message->to = pair->value;
1291                else if (pair->key == 15)
1292                        message->tm = strtol(pair->value, NULL, 10);
1293                else if (pair->key == 97)
1294                        message->utf8 = atoi(pair->value);
1295                        /* user message */  /* sys message */
1296                else if (pair->key == 14 || pair->key == 16)
1297                        message->msg = pair->value;
1298                else if (pair->key == 31) {
1299                        if(message->i_31) {
1300                                messages = y_list_append(messages, message);
1301                                message = y_new0(struct m, 1);
1302                        }
1303                        message->i_31 = atoi(pair->value);
1304                }
1305                else if (pair->key == 32)
1306                        message->i_32 = atoi(pair->value);
1307                else
1308                        LOG(("yahoo_process_message: status: %d, key: %d, value: %s",
1309                                        pkt->status, pair->key, pair->value));
1310        }
1311
1312        messages = y_list_append(messages, message);
1313
1314        for (l = messages; l; l=l->next) {
1315                message = l->data;
1316                if (pkt->service == YAHOO_SERVICE_SYSMESSAGE) {
1317                        YAHOO_CALLBACK(ext_yahoo_system_message)(yd->client_id, message->msg);
1318                } else if (pkt->status <= 2 || pkt->status == 5) {
1319                        YAHOO_CALLBACK(ext_yahoo_got_im)(yd->client_id, message->from, message->msg, message->tm, pkt->status, message->utf8);
1320                } else if (pkt->status == 0xffffffff) {
1321                        YAHOO_CALLBACK(ext_yahoo_error)(yd->client_id, message->msg, 0);
1322                }
1323                free(message);
1324        }
1325
1326        y_list_free(messages);
1327}
1328
1329
1330static void yahoo_process_status(struct yahoo_input_data *yid, struct yahoo_packet *pkt)
1331{
1332        YList *l;
1333        struct yahoo_data *yd = yid->yd;
1334        char *name = NULL;
1335        int state = 0;
1336        int away = 0;
1337        int idle = 0;
1338        char *msg = NULL;
1339       
1340        if(pkt->service == YAHOO_SERVICE_LOGOFF && pkt->status == -1) {
1341                YAHOO_CALLBACK(ext_yahoo_login_response)(yd->client_id, YAHOO_LOGIN_DUPL, NULL);
1342                return;
1343        }
1344
1345        for (l = pkt->hash; l; l = l->next) {
1346                struct yahoo_pair *pair = l->data;
1347
1348                switch (pair->key) {
1349                case 0: /* we won't actually do anything with this */
1350                        NOTICE(("key %d:%s", pair->key, pair->value));
1351                        break;
1352                case 1: /* we don't get the full buddy list here. */
1353                        if (!yd->logged_in) {
1354                                yd->logged_in = TRUE;
1355                                if(yd->current_status < 0)
1356                                        yd->current_status = yd->initial_status;
1357                                YAHOO_CALLBACK(ext_yahoo_login_response)(yd->client_id, YAHOO_LOGIN_OK, NULL);
1358                        }
1359                        break;
1360                case 8: /* how many online buddies we have */
1361                        NOTICE(("key %d:%s", pair->key, pair->value));
1362                        break;
1363                case 7: /* the current buddy */
1364                        name = pair->value;
1365                        break;
1366                case 10: /* state */
1367                        state = strtol(pair->value, NULL, 10);
1368                        break;
1369                case 19: /* custom status message */
1370                        msg = pair->value;
1371                        break;
1372                case 47: /* is it an away message or not */
1373                        away = atoi(pair->value);
1374                        break;
1375                case 137: /* seconds idle */
1376                        idle = atoi(pair->value);
1377                        break;
1378                case 11: /* what is this? */
1379                        NOTICE(("key %d:%s", pair->key, pair->value));
1380                        break;
1381                case 17: /* in chat? */
1382                        break;
1383                case 13: /* in pager? */
1384                        if (pkt->service == YAHOO_SERVICE_LOGOFF || strtol(pair->value, NULL, 10) == 0) {
1385                                YAHOO_CALLBACK(ext_yahoo_status_changed)(yd->client_id, name, YAHOO_STATUS_OFFLINE, NULL, 1);
1386                                break;
1387                        }
1388                        if (state == YAHOO_STATUS_AVAILABLE) {
1389                                YAHOO_CALLBACK(ext_yahoo_status_changed)(yd->client_id, name, state, NULL, 0);
1390                        } else if (state == YAHOO_STATUS_CUSTOM) {
1391                                YAHOO_CALLBACK(ext_yahoo_status_changed)(yd->client_id, name, state, msg, away);
1392                        } else {
1393                                YAHOO_CALLBACK(ext_yahoo_status_changed)(yd->client_id, name, state, NULL, idle);
1394                        }
1395
1396                        break;
1397                case 60: 
1398                        /* sometimes going offline makes this 2, but invisible never sends it */
1399                        NOTICE(("key %d:%s", pair->key, pair->value));
1400                         break;
1401                case 16: /* Custom error message */
1402                        YAHOO_CALLBACK(ext_yahoo_error)(yd->client_id, pair->value, 0);
1403                        break;
1404                default:
1405                        WARNING(("unknown status key %d:%s", pair->key, pair->value));
1406                        break;
1407                }
1408        }
1409}
1410
1411static void yahoo_process_list(struct yahoo_input_data *yid, struct yahoo_packet *pkt)
1412{
1413        struct yahoo_data *yd = yid->yd;
1414        YList *l;
1415
1416        if (!yd->logged_in) {
1417                yd->logged_in = TRUE;
1418                if(yd->current_status < 0)
1419                        yd->current_status = yd->initial_status;
1420                YAHOO_CALLBACK(ext_yahoo_login_response)(yd->client_id, YAHOO_LOGIN_OK, NULL);
1421        }
1422
1423        for (l = pkt->hash; l; l = l->next) {
1424                struct yahoo_pair *pair = l->data;
1425
1426                switch(pair->key) {
1427                case 87: /* buddies */
1428                        if(!yd->rawbuddylist)
1429                                yd->rawbuddylist = strdup(pair->value);
1430                        else {
1431                                yd->rawbuddylist = y_string_append(yd->rawbuddylist, pair->value);
1432                        }
1433                        break;
1434
1435                case 88: /* ignore list */
1436                        if(!yd->ignorelist)
1437                                yd->ignorelist = strdup("Ignore:");
1438                        yd->ignorelist = y_string_append(yd->ignorelist, pair->value);
1439                        break;
1440
1441                case 89: /* identities */
1442                        {
1443                        char **identities = y_strsplit(pair->value, ",", -1);
1444                        int i;
1445                        for(i=0; identities[i]; i++)
1446                                yd->identities = y_list_append(yd->identities, 
1447                                                strdup(identities[i]));
1448                        y_strfreev(identities);
1449                        }
1450                        YAHOO_CALLBACK(ext_yahoo_got_identities)(yd->client_id, yd->identities);
1451                        break;
1452                case 59: /* cookies */
1453                        if(yd->ignorelist) {
1454                                yd->ignore = bud_str2list(yd->ignorelist);
1455                                FREE(yd->ignorelist);
1456                                YAHOO_CALLBACK(ext_yahoo_got_ignore)(yd->client_id, yd->ignore);
1457                        }
1458                        if(yd->rawbuddylist) {
1459                                yd->buddies = bud_str2list(yd->rawbuddylist);
1460                                FREE(yd->rawbuddylist);
1461                                YAHOO_CALLBACK(ext_yahoo_got_buddies)(yd->client_id, yd->buddies);
1462                        }
1463
1464                        if(pair->value[0]=='Y') {
1465                                FREE(yd->cookie_y);
1466                                FREE(yd->login_cookie);
1467
1468                                yd->cookie_y = getcookie(pair->value);
1469                                yd->login_cookie = getlcookie(yd->cookie_y);
1470
1471                        } else if(pair->value[0]=='T') {
1472                                FREE(yd->cookie_t);
1473                                yd->cookie_t = getcookie(pair->value);
1474
1475                        } else if(pair->value[0]=='C') {
1476                                FREE(yd->cookie_c);
1477                                yd->cookie_c = getcookie(pair->value);
1478                        } 
1479
1480                        if(yd->cookie_y && yd->cookie_t && yd->cookie_c)
1481                                YAHOO_CALLBACK(ext_yahoo_got_cookies)(yd->client_id);
1482
1483                        break;
1484                case 3: /* my id */
1485                case 90: /* 1 */
1486                case 100: /* 0 */
1487                case 101: /* NULL */
1488                case 102: /* NULL */
1489                case 93: /* 86400/1440 */
1490                        break;
1491                }
1492        }
1493}
1494
1495static void yahoo_process_verify(struct yahoo_input_data *yid, struct yahoo_packet *pkt)
1496{
1497        struct yahoo_data *yd = yid->yd;
1498
1499        if(pkt->status != 0x01) {
1500                DEBUG_MSG(("expected status: 0x01, got: %d", pkt->status));
1501                YAHOO_CALLBACK(ext_yahoo_login_response)(yd->client_id, YAHOO_LOGIN_LOCK, "");
1502                return;
1503        }
1504
1505        pkt = yahoo_packet_new(YAHOO_SERVICE_AUTH, YAHOO_STATUS_AVAILABLE, yd->session_id);
1506
1507        yahoo_packet_hash(pkt, 1, yd->user);
1508        yahoo_send_packet(yid, pkt, 0);
1509
1510        yahoo_packet_free(pkt);
1511
1512}
1513
1514static void yahoo_process_auth_pre_0x0b(struct yahoo_input_data *yid, 
1515                const char *seed, const char *sn)
1516{
1517        struct yahoo_data *yd = yid->yd;
1518       
1519        /* So, Yahoo has stopped supporting its older clients in India, and
1520         * undoubtedly will soon do so in the rest of the world.
1521         *
1522         * The new clients use this authentication method.  I warn you in
1523         * advance, it's bizzare, convoluted, inordinately complicated. 
1524         * It's also no more secure than crypt() was.  The only purpose this
1525         * scheme could serve is to prevent third part clients from connecting
1526         * to their servers.
1527         *
1528         * Sorry, Yahoo.
1529         */
1530
1531        struct yahoo_packet *pack;
1532       
1533        md5_byte_t result[16];
1534        md5_state_t ctx;
1535        char *crypt_result;
1536        unsigned char *password_hash = malloc(25);
1537        unsigned char *crypt_hash = malloc(25);
1538        unsigned char *hash_string_p = malloc(50 + strlen(sn));
1539        unsigned char *hash_string_c = malloc(50 + strlen(sn));
1540       
1541        char checksum;
1542       
1543        int sv;
1544       
1545        unsigned char *result6 = malloc(25);
1546        unsigned char *result96 = malloc(25);
1547
1548        sv = seed[15];
1549        sv = (sv % 8) % 5;
1550
1551        md5_init(&ctx);
1552        md5_append(&ctx, (md5_byte_t *)yd->password, strlen(yd->password));
1553        md5_finish(&ctx, result);
1554        to_y64(password_hash, result, 16);
1555       
1556        md5_init(&ctx);
1557        crypt_result = yahoo_crypt(yd->password, "$1$_2S43d5f$"); 
1558        md5_append(&ctx, (md5_byte_t *)crypt_result, strlen(crypt_result));
1559        md5_finish(&ctx, result);
1560        to_y64(crypt_hash, result, 16);
1561        free(crypt_result);
1562
1563        switch (sv) {
1564        case 0:
1565                checksum = seed[seed[7] % 16];
1566                snprintf((char *)hash_string_p, strlen(sn) + 50,
1567                        "%c%s%s%s", checksum, password_hash, yd->user, seed);
1568                snprintf((char *)hash_string_c, strlen(sn) + 50,
1569                        "%c%s%s%s", checksum, crypt_hash, yd->user, seed);
1570                break;
1571        case 1:
1572                checksum = seed[seed[9] % 16];
1573                snprintf((char *)hash_string_p, strlen(sn) + 50,
1574                        "%c%s%s%s", checksum, yd->user, seed, password_hash);
1575                snprintf((char *)hash_string_c, strlen(sn) + 50,
1576                        "%c%s%s%s", checksum, yd->user, seed, crypt_hash);
1577                break;
1578        case 2:
1579                checksum = seed[seed[15] % 16];
1580                snprintf((char *)hash_string_p, strlen(sn) + 50,
1581                        "%c%s%s%s", checksum, seed, password_hash, yd->user);
1582                snprintf((char *)hash_string_c, strlen(sn) + 50,
1583                        "%c%s%s%s", checksum, seed, crypt_hash, yd->user);
1584                break;
1585        case 3:
1586                checksum = seed[seed[1] % 16];
1587                snprintf((char *)hash_string_p, strlen(sn) + 50,
1588                        "%c%s%s%s", checksum, yd->user, password_hash, seed);
1589                snprintf((char *)hash_string_c, strlen(sn) + 50,
1590                        "%c%s%s%s", checksum, yd->user, crypt_hash, seed);
1591                break;
1592        case 4:
1593                checksum = seed[seed[3] % 16];
1594                snprintf((char *)hash_string_p, strlen(sn) + 50,
1595                        "%c%s%s%s", checksum, password_hash, seed, yd->user);
1596                snprintf((char *)hash_string_c, strlen(sn) + 50,
1597                        "%c%s%s%s", checksum, crypt_hash, seed, yd->user);
1598                break;
1599        }
1600               
1601        md5_init(&ctx); 
1602        md5_append(&ctx, (md5_byte_t *)hash_string_p, strlen((char *)hash_string_p));
1603        md5_finish(&ctx, result);
1604        to_y64(result6, result, 16);
1605
1606        md5_init(&ctx); 
1607        md5_append(&ctx, (md5_byte_t *)hash_string_c, strlen((char *)hash_string_c));
1608        md5_finish(&ctx, result);
1609        to_y64(result96, result, 16);
1610
1611        pack = yahoo_packet_new(YAHOO_SERVICE_AUTHRESP, yd->initial_status, yd->session_id);
1612        yahoo_packet_hash(pack, 0, yd->user);
1613        yahoo_packet_hash(pack, 6, (char *)result6);
1614        yahoo_packet_hash(pack, 96, (char *)result96);
1615        yahoo_packet_hash(pack, 1, yd->user);
1616               
1617        yahoo_send_packet(yid, pack, 0);
1618               
1619        FREE(result6);
1620        FREE(result96);
1621        FREE(password_hash);
1622        FREE(crypt_hash);
1623        FREE(hash_string_p);
1624        FREE(hash_string_c);
1625
1626        yahoo_packet_free(pack);
1627
1628}
1629
1630/*
1631 * New auth protocol cracked by Cerulean Studios and sent in to Gaim
1632 */
1633static void yahoo_process_auth_0x0b(struct yahoo_input_data *yid, const char *seed, const char *sn)
1634{
1635        struct yahoo_packet *pack = NULL;
1636        struct yahoo_data *yd = yid->yd;
1637
1638        md5_byte_t         result[16];
1639        md5_state_t        ctx;
1640
1641        SHA_CTX            ctx1;
1642        SHA_CTX            ctx2;
1643
1644        char *alphabet1 = "FBZDWAGHrJTLMNOPpRSKUVEXYChImkwQ";
1645        char *alphabet2 = "F0E1D2C3B4A59687abcdefghijklmnop";
1646
1647        char *challenge_lookup = "qzec2tb3um1olpar8whx4dfgijknsvy5";
1648        char *operand_lookup = "+|&%/*^-";
1649        char *delimit_lookup = ",;";
1650
1651        unsigned char *password_hash = malloc(25);
1652        unsigned char *crypt_hash = malloc(25);
1653        char *crypt_result = NULL;
1654        unsigned char pass_hash_xor1[64];
1655        unsigned char pass_hash_xor2[64];
1656        unsigned char crypt_hash_xor1[64];
1657        unsigned char crypt_hash_xor2[64];
1658        unsigned char chal[7];
1659        char resp_6[100];
1660        char resp_96[100];
1661
1662        unsigned char digest1[20];
1663        unsigned char digest2[20];
1664        unsigned char magic_key_char[4];
1665        const unsigned char *magic_ptr;
1666
1667        unsigned int  magic[64];
1668        unsigned int  magic_work=0;
1669
1670        char comparison_src[20];
1671
1672        int x, j, i;
1673        int cnt = 0;
1674        int magic_cnt = 0;
1675        int magic_len;
1676        int depth =0, table =0;
1677
1678        memset(&pass_hash_xor1, 0, 64);
1679        memset(&pass_hash_xor2, 0, 64);
1680        memset(&crypt_hash_xor1, 0, 64);
1681        memset(&crypt_hash_xor2, 0, 64);
1682        memset(&digest1, 0, 20);
1683        memset(&digest2, 0, 20);
1684        memset(&magic, 0, 64);
1685        memset(&resp_6, 0, 100);
1686        memset(&resp_96, 0, 100);
1687        memset(&magic_key_char, 0, 4);
1688
1689        /*
1690         * Magic: Phase 1.  Generate what seems to be a 30
1691         * byte value (could change if base64
1692         * ends up differently?  I don't remember and I'm
1693         * tired, so use a 64 byte buffer.
1694         */
1695
1696        magic_ptr = (unsigned char *)seed;
1697
1698        while (*magic_ptr != (int)NULL) {
1699                char *loc;
1700
1701                /* Ignore parentheses.  */
1702
1703                if (*magic_ptr == '(' || *magic_ptr == ')') {
1704                        magic_ptr++;
1705                        continue;
1706                }
1707
1708                /* Characters and digits verify against
1709                   the challenge lookup.
1710                */
1711
1712                if (isalpha(*magic_ptr) || isdigit(*magic_ptr)) {
1713                        loc = strchr(challenge_lookup, *magic_ptr);
1714                        if (!loc) {
1715                                /* This isn't good */
1716                                continue;
1717                        }
1718
1719                        /* Get offset into lookup table and lsh 3. */
1720
1721                        magic_work = loc - challenge_lookup;
1722                        magic_work <<= 3;
1723
1724                        magic_ptr++;
1725                        continue;
1726                } else {
1727                        unsigned int local_store;
1728
1729                        loc = strchr(operand_lookup, *magic_ptr);
1730                        if (!loc) {
1731                                /* Also not good. */
1732                                continue;
1733                        }
1734
1735                        local_store = loc - operand_lookup;
1736
1737                        /* Oops; how did this happen? */
1738                        if (magic_cnt >= 64) 
1739                                break;
1740
1741                        magic[magic_cnt++] = magic_work | local_store;
1742                        magic_ptr++;
1743                        continue;
1744                }
1745        }
1746
1747        magic_len = magic_cnt;
1748        magic_cnt = 0;
1749
1750        /* Magic: Phase 2.  Take generated magic value and
1751         * sprinkle fairy dust on the values. */
1752
1753        for (magic_cnt = magic_len-2; magic_cnt >= 0; magic_cnt--) {
1754                unsigned char byte1;
1755                unsigned char byte2;
1756
1757                /* Bad.  Abort.
1758                 */
1759                if (magic_cnt >= magic_len) {
1760                        WARNING(("magic_cnt(%d)  magic_len(%d)", magic_cnt, magic_len))
1761                        break;
1762                }
1763
1764                byte1 = magic[magic_cnt];
1765                byte2 = magic[magic_cnt+1];
1766
1767                byte1 *= 0xcd;
1768                byte1 ^= byte2;
1769
1770                magic[magic_cnt+1] = byte1;
1771        }
1772
1773        /* Magic: Phase 3.  This computes 20 bytes.  The first 4 bytes are used as our magic
1774         * key (and may be changed later); the next 16 bytes are an MD5 sum of the magic key
1775         * plus 3 bytes.  The 3 bytes are found by looping, and they represent the offsets
1776         * into particular functions we'll later call to potentially alter the magic key.
1777         *
1778         * %-)
1779         */ 
1780       
1781        magic_cnt = 1; 
1782        x = 0; 
1783       
1784        do { 
1785                unsigned int     bl = 0; 
1786                unsigned int     cl = magic[magic_cnt++]; 
1787               
1788                if (magic_cnt >= magic_len) 
1789                        break; 
1790               
1791                if (cl > 0x7F) { 
1792                        if (cl < 0xe0) 
1793                                bl = cl = (cl & 0x1f) << 6; 
1794                        else { 
1795                                bl = magic[magic_cnt++]; 
1796                              cl = (cl & 0x0f) << 6; 
1797                              bl = ((bl & 0x3f) + cl) << 6; 
1798                        } 
1799                       
1800                        cl = magic[magic_cnt++]; 
1801                        bl = (cl & 0x3f) + bl; 
1802                } else 
1803                        bl = cl; 
1804               
1805                comparison_src[x++] = (bl & 0xff00) >> 8; 
1806                comparison_src[x++] = bl & 0xff; 
1807        } while (x < 20); 
1808
1809        /* Dump magic key into a char for SHA1 action. */
1810       
1811               
1812        for(x = 0; x < 4; x++) 
1813                magic_key_char[x] = comparison_src[x];
1814
1815        /* Compute values for recursive function table! */
1816        memcpy( chal, magic_key_char, 4 );
1817        x = 1;
1818        for( i = 0; i < 0xFFFF && x; i++ )
1819        {
1820                for( j = 0; j < 5 && x; j++ )
1821                {
1822                        chal[4] = i;
1823                        chal[5] = i >> 8;
1824                        chal[6] = j;
1825                        md5_init( &ctx );
1826                        md5_append( &ctx, chal, 7 );
1827                        md5_finish( &ctx, result );
1828                        if( memcmp( comparison_src + 4, result, 16 ) == 0 )
1829                        {
1830                                depth = i;
1831                                table = j;
1832                                x = 0;
1833                        }
1834                }
1835        }
1836
1837        /* Transform magic_key_char using transform table */
1838        x = magic_key_char[3] << 24  | magic_key_char[2] << 16 
1839                | magic_key_char[1] << 8 | magic_key_char[0];
1840        x = yahoo_xfrm( table, depth, x );
1841        x = yahoo_xfrm( table, depth, x );
1842        magic_key_char[0] = x & 0xFF;
1843        magic_key_char[1] = x >> 8 & 0xFF;
1844        magic_key_char[2] = x >> 16 & 0xFF;
1845        magic_key_char[3] = x >> 24 & 0xFF;
1846
1847        /* Get password and crypt hashes as per usual. */
1848        md5_init(&ctx);
1849        md5_append(&ctx, (md5_byte_t *)yd->password,  strlen(yd->password));
1850        md5_finish(&ctx, result);
1851        to_y64(password_hash, result, 16);
1852
1853        md5_init(&ctx);
1854        crypt_result = yahoo_crypt(yd->password, "$1$_2S43d5f$"); 
1855        md5_append(&ctx, (md5_byte_t *)crypt_result, strlen(crypt_result));
1856        md5_finish(&ctx, result);
1857        to_y64(crypt_hash, result, 16);
1858        free(crypt_result);
1859
1860        /* Our first authentication response is based off
1861         * of the password hash. */
1862
1863        for (x = 0; x < (int)strlen((char *)password_hash); x++) 
1864                pass_hash_xor1[cnt++] = password_hash[x] ^ 0x36;
1865
1866        if (cnt < 64) 
1867                memset(&(pass_hash_xor1[cnt]), 0x36, 64-cnt);
1868
1869        cnt = 0;
1870
1871        for (x = 0; x < (int)strlen((char *)password_hash); x++) 
1872                pass_hash_xor2[cnt++] = password_hash[x] ^ 0x5c;
1873
1874        if (cnt < 64) 
1875                memset(&(pass_hash_xor2[cnt]), 0x5c, 64-cnt);
1876
1877        shaInit(&ctx1);
1878        shaInit(&ctx2);
1879
1880        /* The first context gets the password hash XORed
1881         * with 0x36 plus a magic value
1882         * which we previously extrapolated from our
1883         * challenge. */
1884
1885        shaUpdate(&ctx1, pass_hash_xor1, 64);
1886        if (j >= 3 )
1887                ctx1.sizeLo = 0x1ff;
1888        shaUpdate(&ctx1, magic_key_char, 4);
1889        shaFinal(&ctx1, digest1);
1890
1891         /* The second context gets the password hash XORed
1892          * with 0x5c plus the SHA-1 digest
1893          * of the first context. */
1894
1895        shaUpdate(&ctx2, pass_hash_xor2, 64);
1896        shaUpdate(&ctx2, digest1, 20);
1897        shaFinal(&ctx2, digest2);
1898
1899        /* Now that we have digest2, use it to fetch
1900         * characters from an alphabet to construct
1901         * our first authentication response. */
1902
1903        for (x = 0; x < 20; x += 2) {
1904                unsigned int    val = 0;
1905                unsigned int    lookup = 0;
1906                char            byte[6];
1907
1908                memset(&byte, 0, 6);
1909
1910                /* First two bytes of digest stuffed
1911                 *  together.
1912                 */
1913
1914                val = digest2[x];
1915                val <<= 8;
1916                val += digest2[x+1];
1917
1918                lookup = (val >> 0x0b);
1919                lookup &= 0x1f;
1920                if (lookup >= strlen(alphabet1))
1921                        break;
1922                sprintf(byte, "%c", alphabet1[lookup]);
1923                strcat(resp_6, byte);
1924                strcat(resp_6, "=");
1925
1926                lookup = (val >> 0x06);
1927                lookup &= 0x1f;
1928                if (lookup >= strlen(alphabet2))
1929                        break;
1930                sprintf(byte, "%c", alphabet2[lookup]);
1931                strcat(resp_6, byte);
1932
1933                lookup = (val >> 0x01);
1934                lookup &= 0x1f;
1935                if (lookup >= strlen(alphabet2))
1936                        break;
1937                sprintf(byte, "%c", alphabet2[lookup]);
1938                strcat(resp_6, byte);
1939
1940                lookup = (val & 0x01);
1941                if (lookup >= strlen(delimit_lookup))
1942                        break;
1943                sprintf(byte, "%c", delimit_lookup[lookup]);
1944                strcat(resp_6, byte);
1945        }
1946
1947        /* Our second authentication response is based off
1948         * of the crypto hash. */
1949
1950        cnt = 0;
1951        memset(&digest1, 0, 20);
1952        memset(&digest2, 0, 20);
1953
1954        for (x = 0; x < (int)strlen((char *)crypt_hash); x++) 
1955                crypt_hash_xor1[cnt++] = crypt_hash[x] ^ 0x36;
1956
1957        if (cnt < 64) 
1958                memset(&(crypt_hash_xor1[cnt]), 0x36, 64-cnt);
1959
1960        cnt = 0;
1961
1962        for (x = 0; x < (int)strlen((char *)crypt_hash); x++) 
1963                crypt_hash_xor2[cnt++] = crypt_hash[x] ^ 0x5c;
1964
1965        if (cnt < 64) 
1966                memset(&(crypt_hash_xor2[cnt]), 0x5c, 64-cnt);
1967
1968        shaInit(&ctx1);
1969        shaInit(&ctx2);
1970
1971        /* The first context gets the password hash XORed
1972         * with 0x36 plus a magic value
1973         * which we previously extrapolated from our
1974         * challenge. */
1975
1976        shaUpdate(&ctx1, crypt_hash_xor1, 64);
1977        if (j >= 3 )
1978                ctx1.sizeLo = 0x1ff;
1979        shaUpdate(&ctx1, magic_key_char, 4);
1980        shaFinal(&ctx1, digest1);
1981
1982        /* The second context gets the password hash XORed
1983         * with 0x5c plus the SHA-1 digest
1984         * of the first context. */
1985
1986        shaUpdate(&ctx2, crypt_hash_xor2, 64);
1987        shaUpdate(&ctx2, digest1, 20);
1988        shaFinal(&ctx2, digest2);
1989
1990        /* Now that we have digest2, use it to fetch
1991         * characters from an alphabet to construct
1992         * our first authentication response.  */
1993
1994        for (x = 0; x < 20; x += 2) {
1995                unsigned int val = 0;
1996                unsigned int lookup = 0;
1997
1998                char byte[6];
1999
2000                memset(&byte, 0, 6);
2001
2002                /* First two bytes of digest stuffed
2003                 *  together. */
2004
2005                val = digest2[x];
2006                val <<= 8;
2007                val += digest2[x+1];
2008
2009                lookup = (val >> 0x0b);
2010                lookup &= 0x1f;
2011                if (lookup >= strlen(alphabet1))
2012                        break;
2013                sprintf(byte, "%c", alphabet1[lookup]);
2014                strcat(resp_96, byte);
2015                strcat(resp_96, "=");
2016
2017                lookup = (val >> 0x06);
2018                lookup &= 0x1f;
2019                if (lookup >= strlen(alphabet2))
2020                        break;
2021                sprintf(byte, "%c", alphabet2[lookup]);
2022                strcat(resp_96, byte);
2023
2024                lookup = (val >> 0x01);
2025                lookup &= 0x1f;
2026                if (lookup >= strlen(alphabet2))
2027                        break;
2028                sprintf(byte, "%c", alphabet2[lookup]);
2029                strcat(resp_96, byte);
2030
2031                lookup = (val & 0x01);
2032                if (lookup >= strlen(delimit_lookup))
2033                        break;
2034                sprintf(byte, "%c", delimit_lookup[lookup]);
2035                strcat(resp_96, byte);
2036        }
2037
2038        pack = yahoo_packet_new(YAHOO_SERVICE_AUTHRESP, yd->initial_status, yd->session_id);
2039        yahoo_packet_hash(pack, 0, sn);
2040        yahoo_packet_hash(pack, 6, resp_6);
2041        yahoo_packet_hash(pack, 96, resp_96);
2042        yahoo_packet_hash(pack, 1, sn);
2043        yahoo_send_packet(yid, pack, 0);
2044        yahoo_packet_free(pack);
2045
2046        free(password_hash);
2047        free(crypt_hash);
2048}
2049
2050static void yahoo_process_auth(struct yahoo_input_data *yid, struct yahoo_packet *pkt)
2051{
2052        char *seed = NULL;
2053        char *sn   = NULL;
2054        YList *l = pkt->hash;
2055        int m = 0;
2056
2057        while (l) {
2058                struct yahoo_pair *pair = l->data;
2059                if (pair->key == 94)
2060                        seed = pair->value;
2061                if (pair->key == 1)
2062                        sn = pair->value;
2063                if (pair->key == 13)
2064                        m = atoi(pair->value);
2065                l = l->next;
2066        }
2067
2068        if (!seed) 
2069                return;
2070
2071        switch (m) {
2072                case 0:
2073                        yahoo_process_auth_pre_0x0b(yid, seed, sn);
2074                        break;
2075                case 1:
2076                        yahoo_process_auth_0x0b(yid, seed, sn);
2077                        break;
2078                default:
2079                        /* call error */
2080                        WARNING(("unknown auth type %d", m));
2081                        yahoo_process_auth_0x0b(yid, seed, sn);
2082                        break;
2083        }
2084}
2085
2086static void yahoo_process_auth_resp(struct yahoo_input_data *yid, struct yahoo_packet *pkt)
2087{
2088        struct yahoo_data *yd = yid->yd;
2089        char *login_id;
2090        char *handle;
2091        char *url=NULL;
2092        int  login_status=0;
2093
2094        YList *l;
2095
2096        for (l = pkt->hash; l; l = l->next) {
2097                struct yahoo_pair *pair = l->data;
2098                if (pair->key == 0)
2099                        login_id = pair->value;
2100                else if (pair->key == 1)
2101                        handle = pair->value;
2102                else if (pair->key == 20)
2103                        url = pair->value;
2104                else if (pair->key == 66)
2105                        login_status = atoi(pair->value);
2106        }
2107
2108        if(pkt->status == 0xffffffff) {
2109                YAHOO_CALLBACK(ext_yahoo_login_response)(yd->client_id, login_status, url);
2110        /*      yahoo_logoff(yd->client_id);*/
2111        }
2112}
2113
2114static void yahoo_process_mail(struct yahoo_input_data *yid, struct yahoo_packet *pkt)
2115{
2116        struct yahoo_data *yd = yid->yd;
2117        char *who = NULL;
2118        char *email = NULL;
2119        char *subj = NULL;
2120        int count = 0;
2121        YList *l;
2122
2123        for (l = pkt->hash; l; l = l->next) {
2124                struct yahoo_pair *pair = l->data;
2125                if (pair->key == 9)
2126                        count = strtol(pair->value, NULL, 10);
2127                else if (pair->key == 43)
2128                        who = pair->value;
2129                else if (pair->key == 42)
2130                        email = pair->value;
2131                else if (pair->key == 18)
2132                        subj = pair->value;
2133                else
2134                        LOG(("key: %d => value: %s", pair->key, pair->value));
2135        }
2136
2137        if (who && email && subj) {
2138                char from[1024];
2139                snprintf(from, sizeof(from), "%s (%s)", who, email);
2140                YAHOO_CALLBACK(ext_yahoo_mail_notify)(yd->client_id, from, subj, count);
2141        } else if(count > 0)
2142                YAHOO_CALLBACK(ext_yahoo_mail_notify)(yd->client_id, NULL, NULL, count);
2143}
2144
2145static void yahoo_process_contact(struct yahoo_input_data *yid, struct yahoo_packet *pkt)
2146{
2147        struct yahoo_data *yd = yid->yd;
2148        char *id = NULL;
2149        char *who = NULL;
2150        char *msg = NULL;
2151        char *name = NULL;
2152        long tm = 0L;
2153        int state = YAHOO_STATUS_AVAILABLE;
2154        int online = FALSE;
2155        int away = 0;
2156
2157        YList *l;
2158
2159        for (l = pkt->hash; l; l = l->next) {
2160                struct yahoo_pair *pair = l->data;
2161                if (pair->key == 1)
2162                        id = pair->value;
2163                else if (pair->key == 3)
2164                        who = pair->value;
2165                else if (pair->key == 14)
2166                        msg = pair->value;
2167                else if (pair->key == 7)
2168                        name = pair->value;
2169                else if (pair->key == 10)
2170                        state = strtol(pair->value, NULL, 10);
2171                else if (pair->key == 15)
2172                        tm = strtol(pair->value, NULL, 10);
2173                else if (pair->key == 13)
2174                        online = strtol(pair->value, NULL, 10);
2175                else if (pair->key == 47)
2176                        away = strtol(pair->value, NULL, 10);
2177        }
2178
2179        if (id)
2180                YAHOO_CALLBACK(ext_yahoo_contact_added)(yd->client_id, id, who, msg);
2181        else if (name)
2182                YAHOO_CALLBACK(ext_yahoo_status_changed)(yd->client_id, name, state, msg, away);
2183        else if(pkt->status == 0x07)
2184                YAHOO_CALLBACK(ext_yahoo_rejected)(yd->client_id, who, msg);
2185}
2186
2187static void yahoo_process_buddyadd(struct yahoo_input_data *yid, struct yahoo_packet *pkt)
2188{
2189        struct yahoo_data *yd = yid->yd;
2190        char *who = NULL;
2191        char *where = NULL;
2192        int status = 0;
2193        char *me = NULL;
2194
2195        struct yahoo_buddy *bud=NULL;
2196
2197        YList *l;
2198        for (l = pkt->hash; l; l = l->next) {
2199                struct yahoo_pair *pair = l->data;
2200                if (pair->key == 1)
2201                        me = pair->value;
2202                if (pair->key == 7)
2203                        who = pair->value;
2204                if (pair->key == 65)
2205                        where = pair->value;
2206                if (pair->key == 66)
2207                        status = strtol(pair->value, NULL, 10);
2208        }
2209
2210        yahoo_dump_unhandled(pkt);
2211
2212        if(!who)
2213                return;
2214        if(!where)
2215                where = "Unknown";
2216
2217        bud = y_new0(struct yahoo_buddy, 1);
2218        bud->id = strdup(who);
2219        bud->group = strdup(where);
2220        bud->real_name = NULL;
2221
2222        yd->buddies = y_list_append(yd->buddies, bud);
2223
2224/*      YAHOO_CALLBACK(ext_yahoo_status_changed)(yd->client_id, who, status, NULL, (status==YAHOO_STATUS_AVAILABLE?0:1)); */
2225}
2226
2227static void yahoo_process_buddydel(struct yahoo_input_data *yid, struct yahoo_packet *pkt)
2228{
2229        struct yahoo_data *yd = yid->yd;
2230        char *who = NULL;
2231        char *where = NULL;
2232        int unk_66 = 0;
2233        char *me = NULL;
2234        struct yahoo_buddy *bud;
2235
2236        YList *buddy;
2237
2238        YList *l;
2239        for (l = pkt->hash; l; l = l->next) {
2240                struct yahoo_pair *pair = l->data;
2241                if (pair->key == 1)
2242                        me = pair->value;
2243                else if (pair->key == 7)
2244                        who = pair->value;
2245                else if (pair->key == 65)
2246                        where = pair->value;
2247                else if (pair->key == 66)
2248                        unk_66 = strtol(pair->value, NULL, 10);
2249                else
2250                        DEBUG_MSG(("unknown key: %d = %s", pair->key, pair->value));
2251        }
2252
2253        if(!who || !where)
2254                return;
2255       
2256        bud = y_new0(struct yahoo_buddy, 1);
2257        bud->id = strdup(who);
2258        bud->group = strdup(where);
2259
2260        buddy = y_list_find_custom(yd->buddies, bud, is_same_bud);
2261
2262        FREE(bud->id);
2263        FREE(bud->group);
2264        FREE(bud);
2265
2266        if(buddy) {
2267                bud = buddy->data;
2268                yd->buddies = y_list_remove_link(yd->buddies, buddy);
2269                y_list_free_1(buddy);
2270
2271                FREE(bud->id);
2272                FREE(bud->group);
2273                FREE(bud->real_name);
2274                FREE(bud);
2275
2276                bud=NULL;
2277        }
2278}
2279
2280static void yahoo_process_ignore(struct yahoo_input_data *yid, struct yahoo_packet *pkt)
2281{
2282        char *who = NULL;
2283        int  status = 0;
2284        char *me = NULL;
2285        int  un_ignore = 0;
2286
2287        YList *l;
2288        for (l = pkt->hash; l; l = l->next) {
2289                struct yahoo_pair *pair = l->data;
2290                if (pair->key == 0)
2291                        who = pair->value;
2292                if (pair->key == 1)
2293                        me = pair->value;
2294                if (pair->key == 13) /* 1 == ignore, 2 == unignore */ 
2295                        un_ignore = strtol(pair->value, NULL, 10);
2296                if (pair->key == 66) 
2297                        status = strtol(pair->value, NULL, 10);
2298        }
2299
2300
2301        /*
2302         * status
2303         *      0  - ok
2304         *      2  - already in ignore list, could not add
2305         *      3  - not in ignore list, could not delete
2306         *      12 - is a buddy, could not add
2307         */
2308
2309/*      if(status)
2310                YAHOO_CALLBACK(ext_yahoo_error)(yd->client_id, status, who, 0);
2311*/     
2312}
2313
2314static void yahoo_process_voicechat(struct yahoo_input_data *yid, struct yahoo_packet *pkt)
2315{
2316        char *who = NULL;
2317        char *me = NULL;
2318        char *room = NULL;
2319        char *voice_room = NULL;
2320
2321        YList *l;
2322        for (l = pkt->hash; l; l = l->next) {
2323                struct yahoo_pair *pair = l->data;
2324                if (pair->key == 4)
2325                        who = pair->value;
2326                if (pair->key == 5)
2327                        me = pair->value;
2328                if (pair->key == 13)
2329                        voice_room=pair->value;
2330                if (pair->key == 57) 
2331                        room=pair->value;
2332        }
2333
2334        NOTICE(("got voice chat invite from %s in %s", who, room));
2335        /*
2336         * send: s:0 1:me 5:who 57:room 13:1
2337         * ????  s:4 5:who 10:99 19:-1615114531
2338         * gotr: s:4 5:who 10:99 19:-1615114615
2339         * ????  s:1 5:me 4:who 57:room 13:3room
2340         * got:  s:1 5:me 4:who 57:room 13:1room
2341         * rej:  s:0 1:me 5:who 57:room 13:3
2342         * rejr: s:4 5:who 10:99 19:-1617114599
2343         */
2344}
2345
2346static void _yahoo_webcam_get_server_connected(int fd, int error, void *d)
2347{
2348        struct yahoo_input_data *yid = d;
2349        char *who = yid->wcm->user;
2350        char *data = NULL;
2351        char *packet = NULL;
2352        unsigned char magic_nr[] = {0, 1, 0};
2353        unsigned char header_len = 8;
2354        unsigned int len = 0;
2355        unsigned int pos = 0;
2356
2357        if(error || fd <= 0) {
2358                FREE(who);
2359                FREE(yid);
2360                return;
2361        }
2362
2363        yid->fd = fd;
2364        inputs = y_list_prepend(inputs, yid);
2365       
2366        /* send initial packet */
2367        if (who)
2368                data = strdup("<RVWCFG>");
2369        else
2370                data = strdup("<RUPCFG>");
2371        yahoo_add_to_send_queue(yid, data, strlen(data));
2372        FREE(data);
2373
2374        /* send data */
2375        if (who)
2376        {
2377                data = strdup("g=");
2378                data = y_string_append(data, who);
2379                data = y_string_append(data, "\r\n");
2380        } else {
2381                data = strdup("f=1\r\n");
2382        }
2383        len = strlen(data);
2384        packet = y_new0(char, header_len + len);
2385        packet[pos++] = header_len;
2386        memcpy(packet + pos, magic_nr, sizeof(magic_nr));
2387        pos += sizeof(magic_nr);
2388        pos += yahoo_put32(packet + pos, len);
2389        memcpy(packet + pos, data, len);
2390        pos += len;
2391        yahoo_add_to_send_queue(yid, packet, pos);
2392        FREE(packet);
2393        FREE(data);
2394
2395        yid->read_tag=YAHOO_CALLBACK(ext_yahoo_add_handler)(yid->yd->client_id, fd, YAHOO_INPUT_READ, yid);
2396}
2397
2398static void yahoo_webcam_get_server(struct yahoo_input_data *y, char *who, char *key)
2399{
2400        struct yahoo_input_data *yid = y_new0(struct yahoo_input_data, 1);
2401        struct yahoo_server_settings *yss = y->yd->server_settings;
2402
2403        yid->type = YAHOO_CONNECTION_WEBCAM_MASTER;
2404        yid->yd = y->yd;
2405        yid->wcm = y_new0(struct yahoo_webcam, 1);
2406        yid->wcm->user = who?strdup(who):NULL;
2407        yid->wcm->direction = who?YAHOO_WEBCAM_DOWNLOAD:YAHOO_WEBCAM_UPLOAD;
2408        yid->wcm->key = strdup(key);
2409
2410        YAHOO_CALLBACK(ext_yahoo_connect_async)(yid->yd->client_id, yss->webcam_host, yss->webcam_port, 
2411                        _yahoo_webcam_get_server_connected, yid);
2412
2413}
2414
2415static YList *webcam_queue=NULL;
2416static void yahoo_process_webcam_key(struct yahoo_input_data *yid, struct yahoo_packet *pkt)
2417{
2418        char *me = NULL;
2419        char *key = NULL;
2420        char *who = NULL;
2421
2422        YList *l;
2423        yahoo_dump_unhandled(pkt);
2424        for (l = pkt->hash; l; l = l->next) {
2425                struct yahoo_pair *pair = l->data;
2426                if (pair->key == 5)
2427                        me = pair->value;
2428                if (pair->key == 61) 
2429                        key=pair->value;
2430        }
2431
2432        l = webcam_queue;
2433        if(!l)
2434                return;
2435        who = l->data;
2436        webcam_queue = y_list_remove_link(webcam_queue, webcam_queue);
2437        y_list_free_1(l);
2438        yahoo_webcam_get_server(yid, who, key);
2439        FREE(who);
2440}
2441
2442static void yahoo_packet_process(struct yahoo_input_data *yid, struct yahoo_packet *pkt)
2443{
2444        DEBUG_MSG(("yahoo_packet_process: 0x%02x", pkt->service));
2445        switch (pkt->service)
2446        {
2447        case YAHOO_SERVICE_USERSTAT:
2448        case YAHOO_SERVICE_LOGON:
2449        case YAHOO_SERVICE_LOGOFF:
2450        case YAHOO_SERVICE_ISAWAY:
2451        case YAHOO_SERVICE_ISBACK:
2452        case YAHOO_SERVICE_GAMELOGON:
2453        case YAHOO_SERVICE_GAMELOGOFF:
2454        case YAHOO_SERVICE_IDACT:
2455        case YAHOO_SERVICE_IDDEACT:
2456                yahoo_process_status(yid, pkt);
2457                break;
2458        case YAHOO_SERVICE_NOTIFY:
2459                yahoo_process_notify(yid, pkt);
2460                break;
2461        case YAHOO_SERVICE_MESSAGE:
2462        case YAHOO_SERVICE_GAMEMSG:
2463        case YAHOO_SERVICE_SYSMESSAGE:
2464                yahoo_process_message(yid, pkt);
2465                break;
2466        case YAHOO_SERVICE_NEWMAIL:
2467                yahoo_process_mail(yid, pkt);
2468                break;
2469        case YAHOO_SERVICE_NEWCONTACT:
2470                yahoo_process_contact(yid, pkt);
2471                break;
2472        case YAHOO_SERVICE_LIST:
2473                yahoo_process_list(yid, pkt);
2474                break;
2475        case YAHOO_SERVICE_VERIFY:
2476                yahoo_process_verify(yid, pkt);
2477                break;
2478        case YAHOO_SERVICE_AUTH:
2479                yahoo_process_auth(yid, pkt);
2480                break;
2481        case YAHOO_SERVICE_AUTHRESP:
2482                yahoo_process_auth_resp(yid, pkt);
2483                break;
2484        case YAHOO_SERVICE_CONFINVITE:
2485        case YAHOO_SERVICE_CONFADDINVITE:
2486        case YAHOO_SERVICE_CONFDECLINE:
2487        case YAHOO_SERVICE_CONFLOGON:
2488        case YAHOO_SERVICE_CONFLOGOFF:
2489        case YAHOO_SERVICE_CONFMSG:
2490                yahoo_process_conference(yid, pkt);
2491                break;
2492        case YAHOO_SERVICE_CHATONLINE:
2493        case YAHOO_SERVICE_CHATGOTO:
2494        case YAHOO_SERVICE_CHATJOIN:
2495        case YAHOO_SERVICE_CHATLEAVE:
2496        case YAHOO_SERVICE_CHATEXIT:
2497        case YAHOO_SERVICE_CHATLOGOUT:
2498        case YAHOO_SERVICE_CHATPING:
2499        case YAHOO_SERVICE_COMMENT:
2500                yahoo_process_chat(yid, pkt);
2501                break;
2502        case YAHOO_SERVICE_P2PFILEXFER:
2503        case YAHOO_SERVICE_FILETRANSFER:
2504                yahoo_process_filetransfer(yid, pkt);
2505                break;
2506        case YAHOO_SERVICE_ADDBUDDY:
2507                yahoo_process_buddyadd(yid, pkt);
2508                break;
2509        case YAHOO_SERVICE_REMBUDDY:
2510                yahoo_process_buddydel(yid, pkt);
2511                break;
2512        case YAHOO_SERVICE_IGNORECONTACT:
2513                yahoo_process_ignore(yid, pkt);
2514                break;
2515        case YAHOO_SERVICE_VOICECHAT:
2516                yahoo_process_voicechat(yid, pkt);
2517                break;
2518        case YAHOO_SERVICE_WEBCAM:
2519                yahoo_process_webcam_key(yid, pkt);
2520                break;
2521        case YAHOO_SERVICE_IDLE:
2522        case YAHOO_SERVICE_MAILSTAT:
2523        case YAHOO_SERVICE_CHATINVITE:
2524        case YAHOO_SERVICE_CALENDAR:
2525        case YAHOO_SERVICE_NEWPERSONALMAIL:
2526        case YAHOO_SERVICE_ADDIDENT:
2527        case YAHOO_SERVICE_ADDIGNORE:
2528        case YAHOO_SERVICE_PING:
2529        case YAHOO_SERVICE_GOTGROUPRENAME:
2530        case YAHOO_SERVICE_GROUPRENAME:
2531        case YAHOO_SERVICE_PASSTHROUGH2:
2532        case YAHOO_SERVICE_CHATLOGON:
2533        case YAHOO_SERVICE_CHATLOGOFF:
2534        case YAHOO_SERVICE_CHATMSG:
2535        case YAHOO_SERVICE_REJECTCONTACT:
2536        case YAHOO_SERVICE_PEERTOPEER:
2537                WARNING(("unhandled service 0x%02x", pkt->service));
2538                yahoo_dump_unhandled(pkt);
2539                break;
2540        default:
2541                WARNING(("unknown service 0x%02x", pkt->service));
2542                yahoo_dump_unhandled(pkt);
2543                break;
2544        }
2545}
2546
2547static struct yahoo_packet * yahoo_getdata(struct yahoo_input_data * yid)
2548{
2549        struct yahoo_packet *pkt;
2550        struct yahoo_data *yd = yid->yd;
2551        int pos = 0;
2552        int pktlen;
2553
2554        if(!yd)
2555                return NULL;
2556
2557        DEBUG_MSG(("rxlen is %d", yid->rxlen));
2558        if (yid->rxlen < YAHOO_PACKET_HDRLEN) {
2559                DEBUG_MSG(("len < YAHOO_PACKET_HDRLEN"));
2560                return NULL;
2561        }
2562
2563        pos += 4; /* YMSG */
2564        pos += 2;
2565        pos += 2;
2566
2567        pktlen = yahoo_get16(yid->rxqueue + pos); pos += 2;
2568        DEBUG_MSG(("%d bytes to read, rxlen is %d", 
2569                        pktlen, yid->rxlen));
2570
2571        if (yid->rxlen < (YAHOO_PACKET_HDRLEN + pktlen)) {
2572                DEBUG_MSG(("len < YAHOO_PACKET_HDRLEN + pktlen"));
2573                return NULL;
2574        }
2575
2576        LOG(("reading packet"));
2577        yahoo_packet_dump(yid->rxqueue, YAHOO_PACKET_HDRLEN + pktlen);
2578
2579        pkt = yahoo_packet_new(0, 0, 0);
2580
2581        pkt->service = yahoo_get16(yid->rxqueue + pos); pos += 2;
2582        pkt->status = yahoo_get32(yid->rxqueue + pos); pos += 4;
2583        DEBUG_MSG(("Yahoo Service: 0x%02x Status: %d", pkt->service,
2584                                pkt->status));
2585        pkt->id = yahoo_get32(yid->rxqueue + pos); pos += 4;
2586
2587        yd->session_id = pkt->id;
2588
2589        yahoo_packet_read(pkt, yid->rxqueue + pos, pktlen);
2590
2591        yid->rxlen -= YAHOO_PACKET_HDRLEN + pktlen;
2592        DEBUG_MSG(("rxlen == %d, rxqueue == %p", yid->rxlen, yid->rxqueue));
2593        if (yid->rxlen>0) {
2594                unsigned char *tmp = y_memdup(yid->rxqueue + YAHOO_PACKET_HDRLEN
2595                                + pktlen, yid->rxlen);
2596                FREE(yid->rxqueue);
2597                yid->rxqueue = tmp;
2598                DEBUG_MSG(("new rxlen == %d, rxqueue == %p", yid->rxlen, yid->rxqueue));
2599        } else {
2600                DEBUG_MSG(("freed rxqueue == %p", yid->rxqueue));
2601                FREE(yid->rxqueue);
2602        }
2603
2604        return pkt;
2605}
2606
2607static void yahoo_yab_read(struct yab *yab, unsigned char *d, int len)
2608{
2609        char *st, *en;
2610        char *data = (char *)d;
2611        data[len]='\0';
2612
2613        DEBUG_MSG(("Got yab: %s", data));
2614        st = en = strstr(data, "userid=\"");
2615        if(st) {
2616                st += strlen("userid=\"");
2617                en = strchr(st, '"'); *en++ = '\0';
2618                yab->id = yahoo_xmldecode(st);
2619        }
2620
2621        st = strstr(en, "fname=\"");
2622        if(st) {
2623                st += strlen("fname=\"");
2624                en = strchr(st, '"'); *en++ = '\0';
2625                yab->fname = yahoo_xmldecode(st);
2626        }
2627
2628        st = strstr(en, "lname=\"");
2629        if(st) {
2630                st += strlen("lname=\"");
2631                en = strchr(st, '"'); *en++ = '\0';
2632                yab->lname = yahoo_xmldecode(st);
2633        }
2634
2635        st = strstr(en, "nname=\"");
2636        if(st) {
2637                st += strlen("nname=\"");
2638                en = strchr(st, '"'); *en++ = '\0';
2639                yab->nname = yahoo_xmldecode(st);
2640        }
2641
2642        st = strstr(en, "email=\"");
2643        if(st) {
2644                st += strlen("email=\"");
2645                en = strchr(st, '"'); *en++ = '\0';
2646                yab->email = yahoo_xmldecode(st);
2647        }
2648
2649        st = strstr(en, "hphone=\"");
2650        if(st) {
2651                st += strlen("hphone=\"");
2652                en = strchr(st, '"'); *en++ = '\0';
2653                yab->hphone = yahoo_xmldecode(st);
2654        }
2655
2656        st = strstr(en, "wphone=\"");
2657        if(st) {
2658                st += strlen("wphone=\"");
2659                en = strchr(st, '"'); *en++ = '\0';
2660                yab->wphone = yahoo_xmldecode(st);
2661        }
2662
2663        st = strstr(en, "mphone=\"");
2664        if(st) {
2665                st += strlen("mphone=\"");
2666                en = strchr(st, '"'); *en++ = '\0';
2667                yab->mphone = yahoo_xmldecode(st);
2668        }
2669
2670        st = strstr(en, "dbid=\"");
2671        if(st) {
2672                st += strlen("dbid=\"");
2673                en = strchr(st, '"'); *en++ = '\0';
2674                yab->dbid = atoi(st);
2675        }
2676}
2677
2678static struct yab * yahoo_getyab(struct yahoo_input_data *yid)
2679{
2680        struct yab *yab = NULL;
2681        int pos = 0, end=0;
2682        struct yahoo_data *yd = yid->yd;
2683
2684        if(!yd)
2685                return NULL;
2686
2687        DEBUG_MSG(("rxlen is %d", yid->rxlen));
2688
2689        if(yid->rxlen <= strlen("<record"))
2690                return NULL;
2691
2692        /* start with <record */
2693        while(pos < yid->rxlen-strlen("<record")+1 
2694                        && memcmp(yid->rxqueue + pos, "<record", strlen("<record")))
2695                pos++;
2696
2697        if(pos >= yid->rxlen-1)
2698                return NULL;
2699
2700        end = pos+2;
2701        /* end with /> */
2702        while(end < yid->rxlen-strlen("/>")+1 && memcmp(yid->rxqueue + end, "/>", strlen("/>")))
2703                end++;
2704
2705        if(end >= yid->rxlen-1)
2706                return NULL;
2707
2708        yab = y_new0(struct yab, 1);
2709        yahoo_yab_read(yab, yid->rxqueue + pos, end+2-pos);
2710       
2711
2712        yid->rxlen -= end+1;
2713        DEBUG_MSG(("rxlen == %d, rxqueue == %p", yid->rxlen, yid->rxqueue));
2714        if (yid->rxlen>0) {
2715                unsigned char *tmp = y_memdup(yid->rxqueue + end + 1, yid->rxlen);
2716                FREE(yid->rxqueue);
2717                yid->rxqueue = tmp;
2718                DEBUG_MSG(("new rxlen == %d, rxqueue == %p", yid->rxlen, yid->rxqueue));
2719        } else {
2720                DEBUG_MSG(("freed rxqueue == %p", yid->rxqueue));
2721                FREE(yid->rxqueue);
2722        }
2723
2724
2725        return yab;
2726}
2727
2728static char * yahoo_getwebcam_master(struct yahoo_input_data *yid)
2729{
2730        unsigned int pos=0;
2731        unsigned int len=0;
2732        unsigned int status=0;
2733        char *server=NULL;
2734        struct yahoo_data *yd = yid->yd;
2735
2736        if(!yid || !yd)
2737                return NULL;
2738
2739        DEBUG_MSG(("rxlen is %d", yid->rxlen));
2740
2741        len = yid->rxqueue[pos++];
2742        if (yid->rxlen < len)
2743                return NULL;
2744
2745        /* extract status (0 = ok, 6 = webcam not online) */
2746        status = yid->rxqueue[pos++];
2747
2748        if (status == 0)
2749        {
2750                pos += 2; /* skip next 2 bytes */
2751                server =  y_memdup(yid->rxqueue+pos, 16);
2752                pos += 16;
2753        }
2754        else if (status == 6)
2755        {
2756                YAHOO_CALLBACK(ext_yahoo_webcam_closed)
2757                        (yd->client_id, yid->wcm->user, 4);
2758        }
2759
2760        /* skip rest of the data */
2761
2762        yid->rxlen -= len;
2763        DEBUG_MSG(("rxlen == %d, rxqueue == %p", yid->rxlen, yid->rxqueue));
2764        if (yid->rxlen>0) {
2765                unsigned char *tmp = y_memdup(yid->rxqueue + pos, yid->rxlen);
2766                FREE(yid->rxqueue);
2767                yid->rxqueue = tmp;
2768                DEBUG_MSG(("new rxlen == %d, rxqueue == %p", yid->rxlen, yid->rxqueue));
2769        } else {
2770                DEBUG_MSG(("freed rxqueue == %p", yid->rxqueue));
2771                FREE(yid->rxqueue);
2772        }
2773
2774        return server;
2775}
2776
2777static int yahoo_get_webcam_data(struct yahoo_input_data *yid)
2778{
2779        unsigned char reason=0;
2780        unsigned int pos=0;
2781        unsigned int begin=0;
2782        unsigned int end=0;
2783        unsigned int closed=0;
2784        unsigned char header_len=0;
2785        char *who;
2786        int connect=0;
2787        struct yahoo_data *yd = yid->yd;
2788
2789        if(!yd)
2790                return -1;
2791
2792        if(!yid->wcm || !yid->wcd || !yid->rxlen)
2793                return -1;
2794
2795        DEBUG_MSG(("rxlen is %d", yid->rxlen));
2796
2797        /* if we are not reading part of image then read header */
2798        if (!yid->wcd->to_read)
2799        {
2800                header_len=yid->rxqueue[pos++];
2801                yid->wcd->packet_type=0;
2802
2803                if (yid->rxlen < header_len)
2804                        return 0;
2805
2806                if (header_len >= 8)
2807                {
2808                        reason = yid->rxqueue[pos++];
2809                        /* next 2 bytes should always be 05 00 */
2810                        pos += 2;
2811                        yid->wcd->data_size = yahoo_get32(yid->rxqueue + pos);
2812                        pos += 4;
2813                        yid->wcd->to_read = yid->wcd->data_size;
2814                }
2815                if (header_len >= 13)
2816                {
2817                        yid->wcd->packet_type = yid->rxqueue[pos++];
2818                        yid->wcd->timestamp = yahoo_get32(yid->rxqueue + pos);
2819                        pos += 4;
2820                }
2821
2822                /* skip rest of header */
2823                pos = header_len;
2824        }
2825
2826        begin = pos;
2827        pos += yid->wcd->to_read;
2828        if (pos > yid->rxlen) pos = yid->rxlen;
2829
2830        /* if it is not an image then make sure we have the whole packet */
2831        if (yid->wcd->packet_type != 0x02) {
2832                if ((pos - begin) != yid->wcd->data_size) {
2833                        yid->wcd->to_read = 0;
2834                        return 0;
2835                } else {
2836                        yahoo_packet_dump(yid->rxqueue + begin, pos - begin);
2837                }
2838        }
2839
2840        DEBUG_MSG(("packet type %.2X, data length %d", yid->wcd->packet_type,
2841                yid->wcd->data_size));
2842
2843        /* find out what kind of packet we got */
2844        switch (yid->wcd->packet_type)
2845        {
2846                case 0x00:
2847                        /* user requests to view webcam (uploading) */
2848                        if (yid->wcd->data_size &&
2849                                yid->wcm->direction == YAHOO_WEBCAM_UPLOAD) {
2850                                end = begin;
2851                                while (end <= yid->rxlen &&
2852                                        yid->rxqueue[end++] != 13);
2853                                if (end > begin)
2854                                {
2855                                        who = y_memdup(yid->rxqueue + begin, end - begin);
2856                                        who[end - begin - 1] = 0;
2857                                        YAHOO_CALLBACK(ext_yahoo_webcam_viewer)(yd->client_id, who + 2, 2);
2858                                        FREE(who);
2859                                }
2860                        }
2861
2862                        if (yid->wcm->direction == YAHOO_WEBCAM_DOWNLOAD) {
2863                                /* timestamp/status field */
2864                                /* 0 = declined viewing permission */
2865                                /* 1 = accepted viewing permission */
2866                                if (yid->wcd->timestamp == 0) {
2867                                        YAHOO_CALLBACK(ext_yahoo_webcam_closed)(yd->client_id, yid->wcm->user, 3);
2868                                }
2869                        }
2870                        break;
2871                case 0x01: /* status packets?? */
2872                        /* timestamp contains status info */
2873                        /* 00 00 00 01 = we have data?? */
2874                        break;
2875                case 0x02: /* image data */
2876                        YAHOO_CALLBACK(ext_yahoo_got_webcam_image)(yd->client_id, 
2877                                        yid->wcm->user, yid->rxqueue + begin,
2878                                        yid->wcd->data_size, pos - begin,
2879                                        yid->wcd->timestamp);
2880                        break;
2881                case 0x05: /* response packets when uploading */
2882                        if (!yid->wcd->data_size) {
2883                                YAHOO_CALLBACK(ext_yahoo_webcam_data_request)(yd->client_id, yid->wcd->timestamp);
2884                        }
2885                        break;
2886                case 0x07: /* connection is closing */
2887                        switch(reason)
2888                        {
2889                                case 0x01: /* user closed connection */
2890                                        closed = 1;
2891                                        break;
2892                                case 0x0F: /* user cancelled permission */
2893                                        closed = 2;
2894                                        break;
2895                        }
2896                        YAHOO_CALLBACK(ext_yahoo_webcam_closed)(yd->client_id, yid->wcm->user, closed);
2897                        break;
2898                case 0x0C: /* user connected */
2899                case 0x0D: /* user disconnected */
2900                        if (yid->wcd->data_size) {
2901                                who = y_memdup(yid->rxqueue + begin, pos - begin + 1);
2902                                who[pos - begin] = 0;
2903                                if (yid->wcd->packet_type == 0x0C)
2904                                        connect=1;
2905                                else
2906                                        connect=0;
2907                                YAHOO_CALLBACK(ext_yahoo_webcam_viewer)(yd->client_id, who, connect);
2908                                FREE(who);
2909                        }
2910                        break;
2911                case 0x13: /* user data */
2912                        /* i=user_ip (ip of the user we are viewing) */
2913                        /* j=user_ext_ip (external ip of the user we */
2914                        /*                are viewing) */
2915                        break;
2916                case 0x17: /* ?? */
2917                        break;
2918        }
2919        yid->wcd->to_read -= pos - begin;
2920
2921        yid->rxlen -= pos;
2922        DEBUG_MSG(("rxlen == %d, rxqueue == %p", yid->rxlen, yid->rxqueue));
2923        if (yid->rxlen>0) {
2924                unsigned char *tmp = y_memdup(yid->rxqueue + pos, yid->rxlen);
2925                FREE(yid->rxqueue);
2926                yid->rxqueue = tmp;
2927                DEBUG_MSG(("new rxlen == %d, rxqueue == %p", yid->rxlen, yid->rxqueue));
2928        } else {
2929                DEBUG_MSG(("freed rxqueue == %p", yid->rxqueue));
2930                FREE(yid->rxqueue);
2931        }
2932
2933        /* If we read a complete packet return success */
2934        if (!yid->wcd->to_read)
2935                return 1;
2936
2937        return 0;
2938}
2939
2940int yahoo_write_ready(int id, int fd, void *data)
2941{
2942        struct yahoo_input_data *yid = data;
2943        int len;
2944        struct data_queue *tx;
2945
2946        LOG(("write callback: id=%d fd=%d data=%p", id, fd, data));
2947        if(!yid || !yid->txqueues)
2948                return -2;
2949       
2950        tx = yid->txqueues->data;
2951        LOG(("writing %d bytes", tx->len));
2952        len = yahoo_send_data(fd, tx->queue, MIN(1024, tx->len));
2953
2954        if(len == -1 && errno == EAGAIN)
2955                return 1;
2956
2957        if(len <= 0) {
2958                int e = errno;
2959                DEBUG_MSG(("len == %d (<= 0)", len));
2960                while(yid->txqueues) {
2961                        YList *l=yid->txqueues;
2962                        tx = l->data;
2963                        free(tx->queue);
2964                        free(tx);
2965                        yid->txqueues = y_list_remove_link(yid->txqueues, yid->txqueues);
2966                        y_list_free_1(l);
2967                }
2968                LOG(("yahoo_write_ready(%d, %d) len < 0", id, fd));
2969                YAHOO_CALLBACK(ext_yahoo_remove_handler)(id, yid->write_tag);
2970                yid->write_tag = 0;
2971                errno=e;
2972                return 0;
2973        }
2974
2975
2976        tx->len -= len;
2977        if(tx->len > 0) {
2978                unsigned char *tmp = y_memdup(tx->queue + len, tx->len);
2979                FREE(tx->queue);
2980                tx->queue = tmp;
2981        } else {
2982                YList *l=yid->txqueues;
2983                free(tx->queue);
2984                free(tx);
2985                yid->txqueues = y_list_remove_link(yid->txqueues, yid->txqueues);
2986                y_list_free_1(l);
2987                /*
2988                if(!yid->txqueues)
2989                        LOG(("yahoo_write_ready(%d, %d) !yxqueues", id, fd));
2990                */
2991                if(!yid->txqueues) {
2992                        LOG(("yahoo_write_ready(%d, %d) !yxqueues", id, fd));
2993                        YAHOO_CALLBACK(ext_yahoo_remove_handler)(id, yid->write_tag);
2994                        yid->write_tag = 0;
2995                }
2996        }
2997
2998        return 1;
2999}
3000
3001static void yahoo_process_pager_connection(struct yahoo_input_data *yid, int over)
3002{
3003        struct yahoo_packet *pkt;
3004        struct yahoo_data *yd = yid->yd;
3005        int id = yd->client_id;
3006
3007        if(over)
3008                return;
3009
3010        while (find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER) 
3011                        && (pkt = yahoo_getdata(yid)) != NULL) {
3012
3013                yahoo_packet_process(yid, pkt);
3014
3015                yahoo_packet_free(pkt);
3016        }
3017}
3018
3019static void yahoo_process_ft_connection(struct yahoo_input_data *yid, int over)
3020{
3021}
3022
3023static void yahoo_process_chatcat_connection(struct yahoo_input_data *yid, int over)
3024{
3025        if(over)
3026                return;
3027
3028        if (strstr((char*)yid->rxqueue+(yid->rxlen-20), "</content>")) {
3029                YAHOO_CALLBACK(ext_yahoo_chat_cat_xml)(yid->yd->client_id, (char*)yid->rxqueue);
3030        }
3031}
3032
3033static void yahoo_process_yab_connection(struct yahoo_input_data *yid, int over)
3034{
3035        struct yahoo_data *yd = yid->yd;
3036        struct yab *yab;
3037        YList *buds;
3038        int changed=0;
3039        int id = yd->client_id;
3040
3041        if(over)
3042                return;
3043
3044        while(find_input_by_id_and_type(id, YAHOO_CONNECTION_YAB) 
3045                        && (yab = yahoo_getyab(yid)) != NULL) {
3046                if(!yab->id)
3047                        continue;
3048                changed=1;
3049                for(buds = yd->buddies; buds; buds=buds->next) {
3050                        struct yahoo_buddy * bud = buds->data;
3051                        if(!strcmp(bud->id, yab->id)) {
3052                                bud->yab_entry = yab;
3053                                if(yab->nname) {
3054                                        bud->real_name = strdup(yab->nname);
3055                                } else if(yab->fname && yab->lname) {
3056                                        bud->real_name = y_new0(char, 
3057                                                        strlen(yab->fname)+
3058                                                        strlen(yab->lname)+2
3059                                                        );
3060                                        sprintf(bud->real_name, "%s %s",
3061                                                        yab->fname, yab->lname);
3062                                } else if(yab->fname) {
3063                                        bud->real_name = strdup(yab->fname);
3064                                }
3065                                break; /* for */
3066                        }
3067                }
3068        }
3069
3070        if(changed)
3071                YAHOO_CALLBACK(ext_yahoo_got_buddies)(yd->client_id, yd->buddies);
3072}
3073
3074static void yahoo_process_search_connection(struct yahoo_input_data *yid, int over)
3075{
3076        struct yahoo_found_contact *yct=NULL;
3077        char *p = (char *)yid->rxqueue, *np, *cp;
3078        int k, n;
3079        int start=0, found=0, total=0;
3080        YList *contacts=NULL;
3081        struct yahoo_input_data *pyid = find_input_by_id_and_type(yid->yd->client_id, YAHOO_CONNECTION_PAGER);
3082
3083        if(!over || !pyid)
3084                return;
3085
3086        if(p && (p=strstr(p, "\r\n\r\n"))) {
3087                p += 4;
3088
3089                for(k = 0; (p = strchr(p, 4)) && (k < 4); k++) {
3090                        p++;
3091                        n = atoi(p);
3092                        switch(k) {
3093                                case 0: found = pyid->ys->lsearch_nfound = n; break;
3094                                case 2: start = pyid->ys->lsearch_nstart = n; break;
3095                                case 3: total = pyid->ys->lsearch_ntotal = n; break;
3096                        }
3097                }
3098
3099                if(p)
3100                        p++;
3101
3102                k=0;
3103                while(p && *p) {
3104                        cp = p;
3105                        np = strchr(p, 4);
3106
3107                        if(!np)
3108                                break;
3109                        *np = 0;
3110                        p = np+1;
3111
3112                        switch(k++) {
3113                                case 1:
3114                                        if(strlen(cp) > 2 && y_list_length(contacts) < total) {
3115                                                yct = y_new0(struct yahoo_found_contact, 1);
3116                                                contacts = y_list_append(contacts, yct);
3117                                                yct->id = cp+2;
3118                                        } else {
3119                                                *p = 0;
3120                                        }
3121                                        break;
3122                                case 2: 
3123                                        yct->online = !strcmp(cp, "2") ? 1 : 0;
3124                                        break;
3125                                case 3: 
3126                                        yct->gender = cp;
3127                                        break;
3128                                case 4: 
3129                                        yct->age = atoi(cp);
3130                                        break;
3131                                case 5: 
3132                                        if(cp != "\005")
3133                                                yct->location = cp;
3134                                        k = 0;
3135                                        break;
3136                        }
3137                }
3138        }
3139
3140        YAHOO_CALLBACK(ext_yahoo_got_search_result)(yid->yd->client_id, found, start, total, contacts);
3141
3142        while(contacts) {
3143                YList *node = contacts;
3144                contacts = y_list_remove_link(contacts, node);
3145                free(node->data);
3146                y_list_free_1(node);
3147        }
3148}
3149
3150static void _yahoo_webcam_connected(int fd, int error, void *d)
3151{
3152        struct yahoo_input_data *yid = d;
3153        struct yahoo_webcam *wcm = yid->wcm;
3154        struct yahoo_data *yd = yid->yd;
3155        char conn_type[100];
3156        char *data=NULL;
3157        char *packet=NULL;
3158        unsigned char magic_nr[] = {1, 0, 0, 0, 1};
3159        unsigned header_len=0;
3160        unsigned int len=0;
3161        unsigned int pos=0;
3162
3163        if(error || fd <= 0) {
3164                FREE(yid);
3165                return;
3166        }
3167
3168        yid->fd = fd;
3169        inputs = y_list_prepend(inputs, yid);
3170
3171        LOG(("Connected"));
3172        /* send initial packet */
3173        switch (wcm->direction)
3174        {
3175                case YAHOO_WEBCAM_DOWNLOAD:
3176                        data = strdup("<REQIMG>");
3177                        break;
3178                case YAHOO_WEBCAM_UPLOAD:       
3179                        data = strdup("<SNDIMG>");
3180                        break;
3181                default:
3182                        return;
3183        }
3184        yahoo_add_to_send_queue(yid, data, strlen(data));
3185        FREE(data);
3186
3187        /* send data */
3188        switch (wcm->direction)
3189        {
3190                case YAHOO_WEBCAM_DOWNLOAD:
3191                        header_len = 8;
3192                        data = strdup("a=2\r\nc=us\r\ne=21\r\nu=");
3193                        data = y_string_append(data, yd->user);
3194                        data = y_string_append(data, "\r\nt=");
3195                        data = y_string_append(data, wcm->key);
3196                        data = y_string_append(data, "\r\ni=");
3197                        data = y_string_append(data, wcm->my_ip);
3198                        data = y_string_append(data, "\r\ng=");
3199                        data = y_string_append(data, wcm->user);
3200                        data = y_string_append(data, "\r\no=w-2-5-1\r\np=");
3201                        snprintf(conn_type, sizeof(conn_type), "%d", wcm->conn_type);
3202                        data = y_string_append(data, conn_type);
3203                        data = y_string_append(data, "\r\n");
3204                        break;
3205                case YAHOO_WEBCAM_UPLOAD:
3206                        header_len = 13;
3207                        data = strdup("a=2\r\nc=us\r\nu=");
3208                        data = y_string_append(data, yd->user);
3209                        data = y_string_append(data, "\r\nt=");
3210                        data = y_string_append(data, wcm->key);
3211                        data = y_string_append(data, "\r\ni=");
3212                        data = y_string_append(data, wcm->my_ip);
3213                        data = y_string_append(data, "\r\no=w-2-5-1\r\np=");
3214                        snprintf(conn_type, sizeof(conn_type), "%d", wcm->conn_type);
3215                        data = y_string_append(data, conn_type);
3216                        data = y_string_append(data, "\r\nb=");
3217                        data = y_string_append(data, wcm->description);
3218                        data = y_string_append(data, "\r\n");
3219                        break;
3220        }
3221
3222        len = strlen(data);
3223        packet = y_new0(char, header_len + len);
3224        packet[pos++] = header_len;
3225        packet[pos++] = 0;
3226        switch (wcm->direction)
3227        {
3228                case YAHOO_WEBCAM_DOWNLOAD:
3229                        packet[pos++] = 1;
3230                        packet[pos++] = 0;
3231                        break;
3232                case YAHOO_WEBCAM_UPLOAD:
3233                        packet[pos++] = 5;
3234                        packet[pos++] = 0;
3235                        break;
3236        }
3237
3238        pos += yahoo_put32(packet + pos, len);
3239        if (wcm->direction == YAHOO_WEBCAM_UPLOAD)
3240        {
3241                memcpy(packet + pos, magic_nr, sizeof(magic_nr));
3242                pos += sizeof(magic_nr);
3243        }
3244        memcpy(packet + pos, data, len);
3245        yahoo_add_to_send_queue(yid, packet, header_len + len);
3246        FREE(packet);
3247        FREE(data);
3248
3249        yid->read_tag=YAHOO_CALLBACK(ext_yahoo_add_handler)(yid->yd->client_id, yid->fd, YAHOO_INPUT_READ, yid);
3250}
3251
3252static void yahoo_webcam_connect(struct yahoo_input_data *y)
3253{
3254        struct yahoo_webcam *wcm = y->wcm;
3255        struct yahoo_input_data *yid;
3256        struct yahoo_server_settings *yss;
3257
3258        if (!wcm || !wcm->server || !wcm->key)
3259                return;
3260
3261        yid = y_new0(struct yahoo_input_data, 1);
3262        yid->type = YAHOO_CONNECTION_WEBCAM;
3263        yid->yd = y->yd;
3264
3265        /* copy webcam data to new connection */
3266        yid->wcm = y->wcm;
3267        y->wcm = NULL;
3268
3269        yss = y->yd->server_settings;
3270
3271        yid->wcd = y_new0(struct yahoo_webcam_data, 1);
3272
3273        LOG(("Connecting to: %s:%d", wcm->server, wcm->port));
3274        YAHOO_CALLBACK(ext_yahoo_connect_async)(y->yd->client_id, wcm->server, wcm->port,
3275                        _yahoo_webcam_connected, yid);
3276
3277}
3278
3279static void yahoo_process_webcam_master_connection(struct yahoo_input_data *yid, int over)
3280{
3281        char* server;
3282        struct yahoo_server_settings *yss;
3283
3284        if(over)
3285                return;
3286
3287        server = yahoo_getwebcam_master(yid);
3288
3289        if (server)
3290        {
3291                yss = yid->yd->server_settings;
3292                yid->wcm->server = strdup(server);
3293                yid->wcm->port = yss->webcam_port;
3294                yid->wcm->conn_type = yss->conn_type;
3295                yid->wcm->my_ip = strdup(yss->local_host);
3296                if (yid->wcm->direction == YAHOO_WEBCAM_UPLOAD)
3297                        yid->wcm->description = strdup(yss->webcam_description);
3298                yahoo_webcam_connect(yid);
3299                FREE(server);
3300        }
3301}
3302
3303static void yahoo_process_webcam_connection(struct yahoo_input_data *yid, int over)
3304{
3305        int id = yid->yd->client_id;
3306        int fd = yid->fd;
3307
3308        if(over)
3309                return;
3310
3311        /* as long as we still have packets available keep processing them */
3312        while (find_input_by_id_and_fd(id, fd) 
3313                        && yahoo_get_webcam_data(yid) == 1);
3314}
3315
3316static void (*yahoo_process_connection[])(struct yahoo_input_data *, int over) = {
3317        yahoo_process_pager_connection,
3318        yahoo_process_ft_connection,
3319        yahoo_process_yab_connection,
3320        yahoo_process_webcam_master_connection,
3321        yahoo_process_webcam_connection,
3322        yahoo_process_chatcat_connection,
3323        yahoo_process_search_connection
3324};
3325
3326int yahoo_read_ready(int id, int fd, void *data)
3327{
3328        struct yahoo_input_data *yid = data;
3329        char buf[1024];
3330        int len;
3331
3332        LOG(("read callback: id=%d fd=%d data=%p", id, fd, data));
3333        if(!yid)
3334                return -2;
3335
3336       
3337        do {
3338                len = read(fd, buf, sizeof(buf));
3339        } while(len == -1 && errno == EINTR);
3340
3341        if(len == -1 && errno == EAGAIN)        /* we'll try again later */
3342                return 1;
3343
3344        if (len <= 0) {
3345                int e = errno;
3346                DEBUG_MSG(("len == %d (<= 0)", len));
3347
3348                if(yid->type == YAHOO_CONNECTION_PAGER) {
3349                        YAHOO_CALLBACK(ext_yahoo_login_response)(yid->yd->client_id, YAHOO_LOGIN_SOCK, NULL);
3350                }
3351
3352                yahoo_process_connection[yid->type](yid, 1);
3353                yahoo_input_close(yid);
3354
3355                /* no need to return an error, because we've already fixed it */
3356                if(len == 0)
3357                        return 1;
3358
3359                errno=e;
3360                LOG(("read error: %s", strerror(errno)));
3361                return -1;
3362        }
3363
3364        yid->rxqueue = y_renew(unsigned char, yid->rxqueue, len + yid->rxlen);
3365        memcpy(yid->rxqueue + yid->rxlen, buf, len);
3366        yid->rxlen += len;
3367
3368        yahoo_process_connection[yid->type](yid, 0);
3369
3370        return len;
3371}
3372
3373int yahoo_init_with_attributes(const char *username, const char *password, ...)
3374{
3375        va_list ap;
3376        struct yahoo_data *yd;
3377
3378        yd = y_new0(struct yahoo_data, 1);
3379
3380        if(!yd)
3381                return 0;
3382
3383        yd->user = strdup(username);
3384        yd->password = strdup(password);
3385
3386        yd->initial_status = -1;
3387        yd->current_status = -1;
3388
3389        yd->client_id = ++last_id;
3390
3391        add_to_list(yd);
3392
3393        va_start(ap, password);
3394        yd->server_settings = _yahoo_assign_server_settings(ap);
3395        va_end(ap);
3396
3397        return yd->client_id;
3398}
3399
3400int yahoo_init(const char *username, const char *password)
3401{
3402        return yahoo_init_with_attributes(username, password, NULL);
3403}
3404
3405struct connect_callback_data {
3406        struct yahoo_data *yd;
3407        int tag;
3408        int i;
3409};
3410
3411static void yahoo_connected(int fd, int error, void *data)
3412{
3413        struct connect_callback_data *ccd = data;
3414        struct yahoo_data *yd = ccd->yd;
3415        struct yahoo_packet *pkt;
3416        struct yahoo_input_data *yid;
3417        struct yahoo_server_settings *yss = yd->server_settings;
3418
3419        if(error) {
3420                if(fallback_ports[ccd->i]) {
3421                        int tag;
3422                        yss->pager_port = fallback_ports[ccd->i++];
3423                        tag = YAHOO_CALLBACK(ext_yahoo_connect_async)(yd->client_id, yss->pager_host,
3424                                        yss->pager_port, yahoo_connected, ccd);
3425
3426                        if(tag > 0)
3427                                ccd->tag=tag;
3428                } else {
3429                        FREE(ccd);
3430                        YAHOO_CALLBACK(ext_yahoo_login_response)(yd->client_id, YAHOO_LOGIN_SOCK, NULL);
3431                }
3432                return;
3433        }
3434
3435        FREE(ccd);
3436
3437        /* fd < 0 && error == 0 means connect was cancelled */
3438        if(fd < 0)
3439                return;
3440
3441        pkt = yahoo_packet_new(YAHOO_SERVICE_AUTH, YAHOO_STATUS_AVAILABLE, yd->session_id);
3442        NOTICE(("Sending initial packet"));
3443
3444        yahoo_packet_hash(pkt, 1, yd->user);
3445
3446        yid = y_new0(struct yahoo_input_data, 1);
3447        yid->yd = yd;
3448        yid->fd = fd;
3449        inputs = y_list_prepend(inputs, yid);
3450
3451        yahoo_send_packet(yid, pkt, 0);
3452
3453        yahoo_packet_free(pkt);
3454
3455        yid->read_tag=YAHOO_CALLBACK(ext_yahoo_add_handler)(yid->yd->client_id, yid->fd, YAHOO_INPUT_READ, yid);
3456}
3457
3458void yahoo_login(int id, int initial)
3459{
3460        struct yahoo_data *yd = find_conn_by_id(id);
3461        struct connect_callback_data *ccd;
3462        struct yahoo_server_settings *yss;
3463        int tag;
3464
3465        if(!yd)
3466                return;
3467
3468        yss = yd->server_settings;
3469
3470        yd->initial_status = initial;
3471
3472        ccd = y_new0(struct connect_callback_data, 1);
3473        ccd->yd = yd;
3474        tag = YAHOO_CALLBACK(ext_yahoo_connect_async)(yd->client_id, yss->pager_host, yss->pager_port, 
3475                        yahoo_connected, ccd);
3476
3477        /*
3478         * if tag <= 0, then callback has already been called
3479         * so ccd will have been freed
3480         */
3481        if(tag > 0)
3482                ccd->tag = tag;
3483        else if(tag < 0)
3484                YAHOO_CALLBACK(ext_yahoo_login_response)(yd->client_id, YAHOO_LOGIN_SOCK, NULL);
3485}
3486
3487
3488int yahoo_get_fd(int id)
3489{
3490        struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
3491        if(!yid)
3492                return 0;
3493        else
3494                return yid->fd;
3495}
3496
3497void yahoo_send_im(int id, const char *from, const char *who, const char *what, int utf8)
3498{
3499        struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
3500        struct yahoo_packet *pkt = NULL;
3501        struct yahoo_data *yd;
3502
3503        if(!yid)
3504                return;
3505
3506        yd = yid->yd;
3507
3508        pkt = yahoo_packet_new(YAHOO_SERVICE_MESSAGE, YAHOO_STATUS_OFFLINE, yd->session_id);
3509
3510        if(from && strcmp(from, yd->user))
3511                yahoo_packet_hash(pkt, 0, yd->user);
3512        yahoo_packet_hash(pkt, 1, from?from:yd->user);
3513        yahoo_packet_hash(pkt, 5, who);
3514        yahoo_packet_hash(pkt, 14, what);
3515
3516        if(utf8)
3517                yahoo_packet_hash(pkt, 97, "1");
3518
3519        yahoo_packet_hash(pkt, 63, ";0");       /* imvironment name; or ;0 */
3520        yahoo_packet_hash(pkt, 64, "0");
3521
3522
3523        yahoo_send_packet(yid, pkt, 0);
3524
3525        yahoo_packet_free(pkt);
3526}
3527
3528void yahoo_send_typing(int id, const char *from, const char *who, int typ)
3529{
3530        struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
3531        struct yahoo_data *yd;
3532        struct yahoo_packet *pkt = NULL;
3533        if(!yid)
3534                return;
3535
3536        yd = yid->yd;
3537        pkt = yahoo_packet_new(YAHOO_SERVICE_NOTIFY, YAHOO_STATUS_NOTIFY, yd->session_id);
3538
3539        yahoo_packet_hash(pkt, 5, who);
3540        yahoo_packet_hash(pkt, 4, from?from:yd->user);
3541        yahoo_packet_hash(pkt, 14, " ");
3542        yahoo_packet_hash(pkt, 13, typ ? "1" : "0");
3543        yahoo_packet_hash(pkt, 49, "TYPING");
3544
3545        yahoo_send_packet(yid, pkt, 0);
3546
3547        yahoo_packet_free(pkt);
3548}
3549
3550void yahoo_set_away(int id, enum yahoo_status state, const char *msg, int away)
3551{
3552        struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
3553        struct yahoo_data *yd;
3554        struct yahoo_packet *pkt = NULL;
3555        int service;
3556        char s[4];
3557
3558        if(!yid)
3559                return;
3560
3561        yd = yid->yd;
3562
3563        if (msg) {
3564                yd->current_status = YAHOO_STATUS_CUSTOM;
3565        } else {
3566                yd->current_status = state;
3567        }
3568
3569        if (yd->current_status == YAHOO_STATUS_AVAILABLE)
3570                service = YAHOO_SERVICE_ISBACK;
3571        else
3572                service = YAHOO_SERVICE_ISAWAY;
3573        pkt = yahoo_packet_new(service, yd->current_status, yd->session_id);
3574        snprintf(s, sizeof(s), "%d", yd->current_status);
3575        yahoo_packet_hash(pkt, 10, s);
3576        if (yd->current_status == YAHOO_STATUS_CUSTOM) {
3577                yahoo_packet_hash(pkt, 19, msg);
3578                yahoo_packet_hash(pkt, 47, away?"1":"0");
3579        }
3580
3581        yahoo_send_packet(yid, pkt, 0);
3582        yahoo_packet_free(pkt);
3583}
3584
3585void yahoo_logoff(int id)
3586{
3587        struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
3588        struct yahoo_data *yd;
3589        struct yahoo_packet *pkt = NULL;
3590
3591        if(!yid)
3592                return;
3593        yd = yid->yd;
3594
3595        LOG(("yahoo_logoff: current status: %d", yd->current_status));
3596
3597        if(yd->current_status != -1) {
3598                pkt = yahoo_packet_new(YAHOO_SERVICE_LOGOFF, YAHOO_STATUS_AVAILABLE, yd->session_id);
3599                yd->current_status = -1;
3600
3601                if (pkt) {
3602                        yahoo_send_packet(yid, pkt, 0);
3603                        yahoo_packet_free(pkt);
3604                }
3605        }
3606
3607       
3608/*      do {
3609                yahoo_input_close(yid);
3610        } while((yid = find_input_by_id(id)));*/
3611       
3612}
3613
3614void yahoo_get_list(int id)
3615{
3616        struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
3617        struct yahoo_data *yd;
3618        struct yahoo_packet *pkt = NULL;
3619
3620        if(!yid)
3621                return;
3622        yd = yid->yd;
3623
3624        pkt = yahoo_packet_new(YAHOO_SERVICE_LIST, YAHOO_STATUS_AVAILABLE, yd->session_id);
3625        yahoo_packet_hash(pkt, 1, yd->user);
3626        if (pkt) {
3627                yahoo_send_packet(yid, pkt, 0);
3628                yahoo_packet_free(pkt);
3629        }
3630}
3631
3632static void _yahoo_http_connected(int id, int fd, int error, void *data)
3633{
3634        struct yahoo_input_data *yid = data;
3635        if(fd <= 0) {
3636                inputs = y_list_remove(inputs, yid);
3637                FREE(yid);
3638                return;
3639        }
3640
3641        yid->fd = fd;
3642        yid->read_tag=YAHOO_CALLBACK(ext_yahoo_add_handler)(yid->yd->client_id, fd, YAHOO_INPUT_READ, yid);
3643}
3644
3645void yahoo_get_yab(int id)
3646{
3647        struct yahoo_data *yd = find_conn_by_id(id);
3648        struct yahoo_input_data *yid;
3649        char url[1024];
3650        char buff[1024];
3651
3652        if(!yd)
3653                return;
3654
3655        yid = y_new0(struct yahoo_input_data, 1);
3656        yid->yd = yd;
3657        yid->type = YAHOO_CONNECTION_YAB;
3658
3659        snprintf(url, 1024, "http://insider.msg.yahoo.com/ycontent/?ab2=0");
3660
3661        snprintf(buff, sizeof(buff), "Y=%s; T=%s",
3662                        yd->cookie_y, yd->cookie_t);
3663
3664        inputs = y_list_prepend(inputs, yid);
3665
3666        yahoo_http_get(yid->yd->client_id, url, buff, 
3667                        _yahoo_http_connected, yid);
3668}
3669
3670void yahoo_set_yab(int id, struct yab * yab)
3671{
3672        struct yahoo_data *yd = find_conn_by_id(id);
3673        struct yahoo_input_data *yid;
3674        char url[1024];
3675        char buff[1024];
3676        char *temp;
3677        int size = sizeof(url)-1;
3678
3679        if(!yd)
3680                return;
3681
3682        yid = y_new0(struct yahoo_input_data, 1);
3683        yid->type = YAHOO_CONNECTION_YAB;
3684        yid->yd = yd;
3685
3686        strncpy(url, "http://insider.msg.yahoo.com/ycontent/?addab2=0", size);
3687
3688        if(yab->dbid) {
3689                /* change existing yab */
3690                char tmp[32];
3691                strncat(url, "&ee=1&ow=1&id=", size - strlen(url));
3692                snprintf(tmp, sizeof(tmp), "%d", yab->dbid);
3693                strncat(url, tmp, size - strlen(url));
3694        }
3695
3696        if(yab->fname) {
3697                strncat(url, "&fn=", size - strlen(url));
3698                temp = yahoo_urlencode(yab->fname);
3699                strncat(url, temp, size - strlen(url));
3700                free(temp);
3701        }
3702        if(yab->lname) {
3703                strncat(url, "&ln=", size - strlen(url));
3704                temp = yahoo_urlencode(yab->lname);
3705                strncat(url, temp, size - strlen(url));
3706                free(temp);
3707        }
3708        strncat(url, "&yid=", size - strlen(url));
3709        temp = yahoo_urlencode(yab->id);
3710        strncat(url, temp, size - strlen(url));
3711        free(temp);
3712        if(yab->nname) {
3713                strncat(url, "&nn=", size - strlen(url));
3714                temp = yahoo_urlencode(yab->nname);
3715                strncat(url, temp, size - strlen(url));
3716                free(temp);
3717        }
3718        if(yab->email) {
3719                strncat(url, "&e=", size - strlen(url));
3720                temp = yahoo_urlencode(yab->email);
3721                strncat(url, temp, size - strlen(url));
3722                free(temp);
3723        }
3724        if(yab->hphone) {
3725                strncat(url, "&hp=", size - strlen(url));
3726                temp = yahoo_urlencode(yab->hphone);
3727                strncat(url, temp, size - strlen(url));
3728                free(temp);
3729        }
3730        if(yab->wphone) {
3731                strncat(url, "&wp=", size - strlen(url));
3732                temp = yahoo_urlencode(yab->wphone);
3733                strncat(url, temp, size - strlen(url));
3734                free(temp);
3735        }
3736        if(yab->mphone) {
3737                strncat(url, "&mp=", size - strlen(url));
3738                temp = yahoo_urlencode(yab->mphone);
3739                strncat(url, temp, size - strlen(url));
3740                free(temp);
3741        }
3742        strncat(url, "&pp=0", size - strlen(url));
3743
3744        snprintf(buff, sizeof(buff), "Y=%s; T=%s",
3745                        yd->cookie_y, yd->cookie_t);
3746
3747        inputs = y_list_prepend(inputs, yid);
3748
3749        yahoo_http_get(yid->yd->client_id, url, buff, 
3750                        _yahoo_http_connected, yid);
3751}
3752
3753void yahoo_set_identity_status(int id, const char * identity, int active)
3754{
3755        struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
3756        struct yahoo_data *yd;
3757        struct yahoo_packet *pkt = NULL;
3758
3759        if(!yid)
3760                return;
3761        yd = yid->yd;
3762
3763        pkt = yahoo_packet_new(active?YAHOO_SERVICE_IDACT:YAHOO_SERVICE_IDDEACT,
3764                        YAHOO_STATUS_AVAILABLE, yd->session_id);
3765        yahoo_packet_hash(pkt, 3, identity);
3766        if (pkt) {
3767                yahoo_send_packet(yid, pkt, 0);
3768                yahoo_packet_free(pkt);
3769        }
3770}
3771
3772void yahoo_refresh(int id)
3773{
3774        struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
3775        struct yahoo_data *yd;
3776        struct yahoo_packet *pkt = NULL;
3777
3778        if(!yid)
3779                return;
3780        yd = yid->yd;
3781
3782        pkt = yahoo_packet_new(YAHOO_SERVICE_USERSTAT, YAHOO_STATUS_AVAILABLE, yd->session_id);
3783        if (pkt) {
3784                yahoo_send_packet(yid, pkt, 0);
3785                yahoo_packet_free(pkt);
3786        }
3787}
3788
3789void yahoo_keepalive(int id)
3790{
3791        struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
3792        struct yahoo_data *yd;
3793        struct yahoo_packet *pkt=NULL;
3794        if(!yid)
3795                return;
3796        yd = yid->yd;
3797
3798        pkt = yahoo_packet_new(YAHOO_SERVICE_PING, YAHOO_STATUS_AVAILABLE, yd->session_id);
3799        yahoo_send_packet(yid, pkt, 0);
3800        yahoo_packet_free(pkt);
3801}
3802
3803void yahoo_chat_keepalive (int id)
3804{
3805        struct yahoo_input_data *yid = find_input_by_id_and_type (id, YAHOO_CONNECTION_PAGER);
3806        struct yahoo_data *yd;
3807        struct yahoo_packet *pkt = NULL;
3808
3809        if (!yid)
3810            return;
3811
3812        yd = yid->yd;
3813
3814        pkt = yahoo_packet_new (YAHOO_SERVICE_CHATPING, YAHOO_STATUS_AVAILABLE, yd->session_id);
3815        yahoo_send_packet (yid, pkt, 0);
3816        yahoo_packet_free (pkt);
3817}
3818
3819void yahoo_add_buddy(int id, const char *who, const char *group)
3820{
3821        struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
3822        struct yahoo_data *yd;
3823        struct yahoo_packet *pkt;
3824
3825        if(!yid)
3826                return;
3827        yd = yid->yd;
3828
3829        if (!yd->logged_in)
3830                return;
3831
3832        pkt = yahoo_packet_new(YAHOO_SERVICE_ADDBUDDY, YAHOO_STATUS_AVAILABLE, yd->session_id);
3833        yahoo_packet_hash(pkt, 1, yd->user);
3834        yahoo_packet_hash(pkt, 7, who);
3835        yahoo_packet_hash(pkt, 65, group);
3836        yahoo_send_packet(yid, pkt, 0);
3837        yahoo_packet_free(pkt);
3838}
3839
3840void yahoo_remove_buddy(int id, const char *who, const char *group)
3841{
3842        struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
3843        struct yahoo_data *yd;
3844        struct yahoo_packet *pkt = NULL;
3845
3846        if(!yid)
3847                return;
3848        yd = yid->yd;
3849
3850        pkt = yahoo_packet_new(YAHOO_SERVICE_REMBUDDY, YAHOO_STATUS_AVAILABLE, yd->session_id);
3851
3852        yahoo_packet_hash(pkt, 1, yd->user);
3853        yahoo_packet_hash(pkt, 7, who);
3854        yahoo_packet_hash(pkt, 65, group);
3855        yahoo_send_packet(yid, pkt, 0);
3856        yahoo_packet_free(pkt);
3857}
3858
3859void yahoo_reject_buddy(int id, const char *who, const char *msg)
3860{
3861        struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
3862        struct yahoo_data *yd;
3863        struct yahoo_packet *pkt;
3864
3865        if(!yid)
3866                return;
3867        yd = yid->yd;
3868
3869        if (!yd->logged_in)
3870                return;
3871
3872        pkt = yahoo_packet_new(YAHOO_SERVICE_REJECTCONTACT, YAHOO_STATUS_AVAILABLE, yd->session_id);
3873        yahoo_packet_hash(pkt, 1, yd->user);
3874        yahoo_packet_hash(pkt, 7, who);
3875        yahoo_packet_hash(pkt, 14, msg);
3876        yahoo_send_packet(yid, pkt, 0);
3877        yahoo_packet_free(pkt);
3878}
3879
3880void yahoo_ignore_buddy(int id, const char *who, int unignore)
3881{
3882        struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
3883        struct yahoo_data *yd;
3884        struct yahoo_packet *pkt;
3885
3886        if(!yid)
3887                return;
3888        yd = yid->yd;
3889
3890        if (!yd->logged_in)
3891                return;
3892
3893        pkt = yahoo_packet_new(YAHOO_SERVICE_IGNORECONTACT, YAHOO_STATUS_AVAILABLE, yd->session_id);
3894        yahoo_packet_hash(pkt, 1, yd->user);
3895        yahoo_packet_hash(pkt, 7, who);
3896        yahoo_packet_hash(pkt, 13, unignore?"2":"1");
3897        yahoo_send_packet(yid, pkt, 0);
3898        yahoo_packet_free(pkt);
3899}
3900
3901void yahoo_change_buddy_group(int id, const char *who, const char *old_group, const char *new_group)
3902{
3903        struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
3904        struct yahoo_data *yd;
3905        struct yahoo_packet *pkt = NULL;
3906
3907        if(!yid)
3908                return;
3909        yd = yid->yd;
3910
3911        pkt = yahoo_packet_new(YAHOO_SERVICE_ADDBUDDY, YAHOO_STATUS_AVAILABLE, yd->session_id);
3912        yahoo_packet_hash(pkt, 1, yd->user);
3913        yahoo_packet_hash(pkt, 7, who);
3914        yahoo_packet_hash(pkt, 65, new_group);
3915        yahoo_packet_hash(pkt, 14, " ");
3916
3917        yahoo_send_packet(yid, pkt, 0);
3918        yahoo_packet_free(pkt);
3919
3920        pkt = yahoo_packet_new(YAHOO_SERVICE_REMBUDDY, YAHOO_STATUS_AVAILABLE, yd->session_id);
3921        yahoo_packet_hash(pkt, 1, yd->user);
3922        yahoo_packet_hash(pkt, 7, who);
3923        yahoo_packet_hash(pkt, 65, old_group);
3924        yahoo_send_packet(yid, pkt, 0);
3925        yahoo_packet_free(pkt);
3926}
3927
3928void yahoo_group_rename(int id, const char *old_group, const char *new_group)
3929{
3930        struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
3931        struct yahoo_data *yd;
3932        struct yahoo_packet *pkt = NULL;
3933
3934        if(!yid)
3935                return;
3936        yd = yid->yd;
3937
3938        pkt = yahoo_packet_new(YAHOO_SERVICE_GROUPRENAME, YAHOO_STATUS_AVAILABLE, yd->session_id);
3939        yahoo_packet_hash(pkt, 1, yd->user);
3940        yahoo_packet_hash(pkt, 65, old_group);
3941        yahoo_packet_hash(pkt, 67, new_group);
3942
3943        yahoo_send_packet(yid, pkt, 0);
3944        yahoo_packet_free(pkt);
3945}
3946
3947void yahoo_conference_addinvite(int id, const char * from, const char *who, const char *room, const YList * members, const char *msg)
3948{
3949        struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
3950        struct yahoo_data *yd;
3951        struct yahoo_packet *pkt;
3952               
3953        if(!yid)
3954                return;
3955        yd = yid->yd;
3956
3957        pkt = yahoo_packet_new(YAHOO_SERVICE_CONFADDINVITE, YAHOO_STATUS_AVAILABLE, yd->session_id);
3958
3959        yahoo_packet_hash(pkt, 1, (from?from:yd->user));
3960        yahoo_packet_hash(pkt, 51, who);
3961        yahoo_packet_hash(pkt, 57, room);
3962        yahoo_packet_hash(pkt, 58, msg);
3963        yahoo_packet_hash(pkt, 13, "0");
3964        for(; members; members = members->next) {
3965                yahoo_packet_hash(pkt, 52, (char *)members->data);
3966                yahoo_packet_hash(pkt, 53, (char *)members->data);
3967        }
3968        /* 52, 53 -> other members? */
3969
3970        yahoo_send_packet(yid, pkt, 0);
3971
3972        yahoo_packet_free(pkt);
3973}
3974
3975void yahoo_conference_invite(int id, const char * from, YList *who, const char *room, const char *msg)
3976{
3977        struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
3978        struct yahoo_data *yd;
3979        struct yahoo_packet *pkt;
3980               
3981        if(!yid)
3982                return;
3983        yd = yid->yd;
3984
3985        pkt = yahoo_packet_new(YAHOO_SERVICE_CONFINVITE, YAHOO_STATUS_AVAILABLE, yd->session_id);
3986
3987        yahoo_packet_hash(pkt, 1, (from?from:yd->user));
3988        yahoo_packet_hash(pkt, 50, yd->user);
3989        for(; who; who = who->next) {
3990                yahoo_packet_hash(pkt, 52, (char *)who->data);
3991        }
3992        yahoo_packet_hash(pkt, 57, room);
3993        yahoo_packet_hash(pkt, 58, msg);
3994        yahoo_packet_hash(pkt, 13, "0");
3995
3996        yahoo_send_packet(yid, pkt, 0);
3997
3998        yahoo_packet_free(pkt);
3999}
4000
4001void yahoo_conference_logon(int id, const char *from, YList *who, const char *room)
4002{
4003        struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
4004        struct yahoo_data *yd;
4005        struct yahoo_packet *pkt;
4006               
4007        if(!yid)
4008                return;
4009        yd = yid->yd;
4010
4011        pkt = yahoo_packet_new(YAHOO_SERVICE_CONFLOGON, YAHOO_STATUS_AVAILABLE, yd->session_id);
4012
4013        yahoo_packet_hash(pkt, 1, (from?from:yd->user));
4014        for(; who; who = who->next) {
4015                yahoo_packet_hash(pkt, 3, (char *)who->data);
4016        }
4017        yahoo_packet_hash(pkt, 57, room);
4018
4019        yahoo_send_packet(yid, pkt, 0);
4020
4021        yahoo_packet_free(pkt);
4022}
4023
4024void yahoo_conference_decline(int id, const char * from, YList *who, const char *room, const char *msg)
4025{
4026        struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
4027        struct yahoo_data *yd;
4028        struct yahoo_packet *pkt;
4029               
4030        if(!yid)
4031                return;
4032        yd = yid->yd;
4033
4034        pkt = yahoo_packet_new(YAHOO_SERVICE_CONFDECLINE, YAHOO_STATUS_AVAILABLE, yd->session_id);
4035
4036        yahoo_packet_hash(pkt, 1, (from?from:yd->user));
4037        for(; who; who = who->next) {
4038                yahoo_packet_hash(pkt, 3, (char *)who->data);
4039        }
4040        yahoo_packet_hash(pkt, 57, room);
4041        yahoo_packet_hash(pkt, 14, msg);
4042
4043        yahoo_send_packet(yid, pkt, 0);
4044
4045        yahoo_packet_free(pkt);
4046}
4047
4048void yahoo_conference_logoff(int id, const char * from, YList *who, const char *room)
4049{
4050        struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
4051        struct yahoo_data *yd;
4052        struct yahoo_packet *pkt;
4053               
4054        if(!yid)
4055                return;
4056        yd = yid->yd;
4057
4058        pkt = yahoo_packet_new(YAHOO_SERVICE_CONFLOGOFF, YAHOO_STATUS_AVAILABLE, yd->session_id);
4059
4060        yahoo_packet_hash(pkt, 1, (from?from:yd->user));
4061        for(; who; who = who->next) {
4062                yahoo_packet_hash(pkt, 3, (char *)who->data);
4063        }
4064        yahoo_packet_hash(pkt, 57, room);
4065
4066        yahoo_send_packet(yid, pkt, 0);
4067
4068        yahoo_packet_free(pkt);
4069}
4070
4071void yahoo_conference_message(int id, const char * from, YList *who, const char *room, const char *msg, int utf8)
4072{
4073        struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
4074        struct yahoo_data *yd;
4075        struct yahoo_packet *pkt;
4076               
4077        if(!yid)
4078                return;
4079        yd = yid->yd;
4080
4081        pkt = yahoo_packet_new(YAHOO_SERVICE_CONFMSG, YAHOO_STATUS_AVAILABLE, yd->session_id);
4082
4083        yahoo_packet_hash(pkt, 1, (from?from:yd->user));
4084        for(; who; who = who->next) {
4085                yahoo_packet_hash(pkt, 53, (char *)who->data);
4086        }
4087        yahoo_packet_hash(pkt, 57, room);
4088        yahoo_packet_hash(pkt, 14, msg);
4089
4090        if(utf8)
4091                yahoo_packet_hash(pkt, 97, "1");
4092
4093        yahoo_send_packet(yid, pkt, 0);
4094
4095        yahoo_packet_free(pkt);
4096}
4097
4098void yahoo_get_chatrooms(int id, int chatroomid)
4099{
4100        struct yahoo_data *yd = find_conn_by_id(id);
4101        struct yahoo_input_data *yid;
4102        char url[1024];
4103        char buff[1024];
4104
4105        if(!yd)
4106                return;
4107
4108        yid = y_new0(struct yahoo_input_data, 1);
4109        yid->yd = yd;
4110        yid->type = YAHOO_CONNECTION_CHATCAT;
4111
4112        if (chatroomid == 0) {
4113                snprintf(url, 1024, "http://insider.msg.yahoo.com/ycontent/?chatcat=0");
4114        } else {
4115                snprintf(url, 1024, "http://insider.msg.yahoo.com/ycontent/?chatroom_%d=0",chatroomid);
4116        }
4117
4118        snprintf(buff, sizeof(buff), "Y=%s; T=%s", yd->cookie_y, yd->cookie_t);
4119
4120        inputs = y_list_prepend(inputs, yid);
4121
4122        yahoo_http_get(yid->yd->client_id, url, buff, _yahoo_http_connected, yid);
4123}
4124
4125void yahoo_chat_logon(int id, const char *from, const char *room, const char *roomid)
4126{
4127        struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
4128        struct yahoo_data *yd;
4129        struct yahoo_packet *pkt;
4130               
4131        if(!yid)
4132                return;
4133
4134        yd = yid->yd;
4135
4136        pkt = yahoo_packet_new(YAHOO_SERVICE_CHATONLINE, YAHOO_STATUS_AVAILABLE, yd->session_id);
4137
4138        yahoo_packet_hash(pkt, 1, (from?from:yd->user));
4139        yahoo_packet_hash(pkt, 109, yd->user);
4140        yahoo_packet_hash(pkt, 6, "abcde");
4141
4142        yahoo_send_packet(yid, pkt, 0);
4143
4144        yahoo_packet_free(pkt);
4145
4146        pkt = yahoo_packet_new(YAHOO_SERVICE_CHATJOIN, YAHOO_STATUS_AVAILABLE, yd->session_id);
4147
4148        yahoo_packet_hash(pkt, 1, (from?from:yd->user));
4149        yahoo_packet_hash(pkt, 104, room);
4150        yahoo_packet_hash(pkt, 129, roomid);
4151        yahoo_packet_hash(pkt, 62, "2"); /* ??? */
4152
4153        yahoo_send_packet(yid, pkt, 0);
4154
4155        yahoo_packet_free(pkt);
4156}
4157
4158
4159void  yahoo_chat_message(int id, const char *from, const char *room, const char *msg, const int msgtype, const int utf8)
4160{
4161        struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
4162        struct yahoo_data *yd;
4163        struct yahoo_packet *pkt;
4164        char buf[2];
4165               
4166        if(!yid)
4167                return;
4168
4169        yd = yid->yd;
4170
4171        pkt = yahoo_packet_new(YAHOO_SERVICE_COMMENT, YAHOO_STATUS_AVAILABLE, yd->session_id);
4172
4173        yahoo_packet_hash(pkt, 1, (from?from:yd->user));
4174        yahoo_packet_hash(pkt, 104, room);
4175        yahoo_packet_hash(pkt, 117, msg);
4176       
4177        snprintf(buf, sizeof(buf), "%d", msgtype);
4178        yahoo_packet_hash(pkt, 124, buf);
4179
4180        if(utf8)
4181                yahoo_packet_hash(pkt, 97, "1");
4182
4183        yahoo_send_packet(yid, pkt, 0);
4184
4185        yahoo_packet_free(pkt);
4186}
4187
4188
4189void yahoo_chat_logoff(int id, const char *from)
4190{
4191        struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
4192        struct yahoo_data *yd;
4193        struct yahoo_packet *pkt;
4194               
4195        if(!yid)
4196                return;
4197
4198        yd = yid->yd;
4199
4200        pkt = yahoo_packet_new(YAHOO_SERVICE_CHATLOGOUT, YAHOO_STATUS_AVAILABLE, yd->session_id);
4201
4202        yahoo_packet_hash(pkt, 1, (from?from:yd->user));
4203
4204        yahoo_send_packet(yid, pkt, 0);
4205
4206        yahoo_packet_free(pkt);
4207}
4208
4209void yahoo_webcam_close_feed(int id, const char *who)
4210{
4211        struct yahoo_input_data *yid = find_input_by_id_and_webcam_user(id, who);
4212
4213        if(yid)
4214                yahoo_input_close(yid);
4215}
4216
4217void yahoo_webcam_get_feed(int id, const char *who)
4218{
4219        struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
4220        struct yahoo_data *yd;
4221        struct yahoo_packet *pkt;
4222               
4223        if(!yid)
4224                return;
4225
4226        /*
4227         * add the user to the queue.  this is a dirty hack, since
4228         * the yahoo server doesn't tell us who's key it's returning,
4229         * we have to just hope that it sends back keys in the same
4230         * order that we request them.
4231         * The queue is popped in yahoo_process_webcam_key
4232         */
4233        webcam_queue = y_list_append(webcam_queue, who?strdup(who):NULL);
4234
4235        yd = yid->yd;
4236
4237        pkt = yahoo_packet_new(YAHOO_SERVICE_WEBCAM, YAHOO_STATUS_AVAILABLE, yd->session_id);
4238
4239        yahoo_packet_hash(pkt, 1, yd->user);
4240        if (who != NULL)
4241                yahoo_packet_hash(pkt, 5, who);
4242        yahoo_send_packet(yid, pkt, 0);
4243
4244        yahoo_packet_free(pkt);
4245}
4246
4247void yahoo_webcam_send_image(int id, unsigned char *image, unsigned int length, unsigned int timestamp)
4248{
4249        struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_WEBCAM);
4250        unsigned char *packet;
4251        unsigned char header_len = 13;
4252        unsigned int pos = 0;
4253
4254        if (!yid)
4255                return;
4256
4257        packet = y_new0(unsigned char, header_len);
4258
4259        packet[pos++] = header_len;
4260        packet[pos++] = 0;
4261        packet[pos++] = 5; /* version byte?? */
4262        packet[pos++] = 0;
4263        pos += yahoo_put32(packet + pos, length);
4264        packet[pos++] = 2; /* packet type, image */
4265        pos += yahoo_put32(packet + pos, timestamp);
4266        yahoo_add_to_send_queue(yid, packet, header_len);
4267        FREE(packet);
4268
4269        if (length)
4270                yahoo_add_to_send_queue(yid, image, length);
4271}
4272
4273void yahoo_webcam_accept_viewer(int id, const char* who, int accept)
4274{
4275        struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_WEBCAM);
4276        char *packet = NULL;
4277        char *data = NULL;
4278        unsigned char header_len = 13;
4279        unsigned int pos = 0;
4280        unsigned int len = 0;
4281
4282        if (!yid)
4283                return;
4284
4285        data = strdup("u=");
4286        data = y_string_append(data, (char*)who);
4287        data = y_string_append(data, "\r\n");
4288        len = strlen(data);
4289
4290        packet = y_new0(char, header_len + len);
4291        packet[pos++] = header_len;
4292        packet[pos++] = 0;
4293        packet[pos++] = 5; /* version byte?? */
4294        packet[pos++] = 0;
4295        pos += yahoo_put32(packet + pos, len);
4296        packet[pos++] = 0; /* packet type */
4297        pos += yahoo_put32(packet + pos, accept);
4298        memcpy(packet + pos, data, len);
4299        FREE(data);
4300        yahoo_add_to_send_queue(yid, packet, header_len + len);
4301        FREE(packet);
4302}
4303
4304void yahoo_webcam_invite(int id, const char *who)
4305{
4306        struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
4307        struct yahoo_packet *pkt;
4308               
4309        if(!yid)
4310                return;
4311
4312        pkt = yahoo_packet_new(YAHOO_SERVICE_NOTIFY, YAHOO_STATUS_NOTIFY, yid->yd->session_id);
4313
4314        yahoo_packet_hash(pkt, 49, "WEBCAMINVITE");
4315        yahoo_packet_hash(pkt, 14, " ");
4316        yahoo_packet_hash(pkt, 13, "0");
4317        yahoo_packet_hash(pkt, 1, yid->yd->user);
4318        yahoo_packet_hash(pkt, 5, who);
4319        yahoo_send_packet(yid, pkt, 0);
4320
4321        yahoo_packet_free(pkt);
4322}
4323
4324static void yahoo_search_internal(int id, int t, const char *text, int g, int ar, int photo, int yahoo_only, int startpos, int total)
4325{
4326        struct yahoo_data *yd = find_conn_by_id(id);
4327        struct yahoo_input_data *yid;
4328        char url[1024];
4329        char buff[1024];
4330        char *ctext, *p;
4331
4332        if(!yd)
4333                return;
4334
4335        yid = y_new0(struct yahoo_input_data, 1);
4336        yid->yd = yd;
4337        yid->type = YAHOO_CONNECTION_SEARCH;
4338
4339        /*
4340        age range
4341        .ar=1 - 13-18, 2 - 18-25, 3 - 25-35, 4 - 35-50, 5 - 50-70, 6 - 70+
4342        */
4343
4344        snprintf(buff, sizeof(buff), "&.sq=%%20&.tt=%d&.ss=%d", total, startpos);
4345
4346        ctext = strdup(text);
4347        while((p = strchr(ctext, ' ')))
4348                *p = '+';
4349
4350        snprintf(url, 1024, "http://members.yahoo.com/interests?.oc=m&.kw=%s&.sb=%d&.g=%d&.ar=0%s%s%s",
4351                        ctext, t, g, photo ? "&.p=y" : "", yahoo_only ? "&.pg=y" : "",
4352                        startpos ? buff : "");
4353
4354        FREE(ctext);
4355
4356        snprintf(buff, sizeof(buff), "Y=%s; T=%s", yd->cookie_y, yd->cookie_t);
4357
4358        inputs = y_list_prepend(inputs, yid);
4359        yahoo_http_get(yid->yd->client_id, url, buff, _yahoo_http_connected, yid);
4360}
4361
4362void yahoo_search(int id, enum yahoo_search_type t, const char *text, enum yahoo_search_gender g, enum yahoo_search_agerange ar, 
4363                int photo, int yahoo_only)
4364{
4365        struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
4366        struct yahoo_search_state *yss;
4367
4368        if(!yid)
4369                return;
4370
4371        if(!yid->ys)
4372                yid->ys = y_new0(struct yahoo_search_state, 1);
4373
4374        yss = yid->ys;
4375
4376        FREE(yss->lsearch_text);
4377        yss->lsearch_type = t;
4378        yss->lsearch_text = strdup(text);
4379        yss->lsearch_gender = g;
4380        yss->lsearch_agerange = ar;
4381        yss->lsearch_photo = photo;
4382        yss->lsearch_yahoo_only = yahoo_only;
4383
4384        yahoo_search_internal(id, t, text, g, ar, photo, yahoo_only, 0, 0);
4385}
4386
4387void yahoo_search_again(int id, int start)
4388{
4389        struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
4390        struct yahoo_search_state *yss;
4391
4392        if(!yid || !yid->ys)
4393                return;
4394
4395        yss = yid->ys;
4396
4397        if(start == -1)
4398                start = yss->lsearch_nstart + yss->lsearch_nfound;
4399
4400        yahoo_search_internal(id, yss->lsearch_type, yss->lsearch_text, 
4401                        yss->lsearch_gender, yss->lsearch_agerange, 
4402                        yss->lsearch_photo, yss->lsearch_yahoo_only, 
4403                        start, yss->lsearch_ntotal);
4404}
4405
4406struct send_file_data {
4407        struct yahoo_packet *pkt;
4408        yahoo_get_fd_callback callback;
4409        void *user_data;
4410};
4411
4412static void _yahoo_send_file_connected(int id, int fd, int error, void *data)
4413{
4414        struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_FT);
4415        struct send_file_data *sfd = data;
4416        struct yahoo_packet *pkt = sfd->pkt;
4417        unsigned char buff[1024];
4418
4419        if(fd <= 0) {
4420                sfd->callback(id, fd, error, sfd->user_data);
4421                FREE(sfd);
4422                yahoo_packet_free(pkt);
4423                inputs = y_list_remove(inputs, yid);
4424                FREE(yid);
4425                return;
4426        }
4427
4428        yid->fd = fd;
4429        yahoo_send_packet(yid, pkt, 8);
4430        yahoo_packet_free(pkt);
4431
4432        snprintf((char *)buff, sizeof(buff), "29");
4433        buff[2] = 0xc0;
4434        buff[3] = 0x80;
4435       
4436        write(yid->fd, buff, 4);
4437
4438/*      YAHOO_CALLBACK(ext_yahoo_add_handler)(nyd->fd, YAHOO_INPUT_READ); */
4439
4440        sfd->callback(id, fd, error, sfd->user_data);
4441        FREE(sfd);
4442        inputs = y_list_remove(inputs, yid);
4443        /*
4444        while(yahoo_tcp_readline(buff, sizeof(buff), nyd->fd) > 0) {
4445                if(!strcmp(buff, ""))
4446                        break;
4447        }
4448
4449        */
4450        yahoo_input_close(yid);
4451}
4452
4453void yahoo_send_file(int id, const char *who, const char *msg, 
4454                const char *name, unsigned long size, 
4455                yahoo_get_fd_callback callback, void *data)
4456{
4457        struct yahoo_data *yd = find_conn_by_id(id);
4458        struct yahoo_input_data *yid;
4459        struct yahoo_server_settings *yss;
4460        struct yahoo_packet *pkt = NULL;
4461        char size_str[10];
4462        long content_length=0;
4463        unsigned char buff[1024];
4464        char url[255];
4465        struct send_file_data *sfd;
4466
4467        if(!yd)
4468                return;
4469
4470        yss = yd->server_settings;
4471
4472        yid = y_new0(struct yahoo_input_data, 1);
4473        yid->yd = yd;
4474        yid->type = YAHOO_CONNECTION_FT;
4475
4476        pkt = yahoo_packet_new(YAHOO_SERVICE_FILETRANSFER, YAHOO_STATUS_AVAILABLE, yd->session_id);
4477
4478        snprintf(size_str, sizeof(size_str), "%ld", size);
4479
4480        yahoo_packet_hash(pkt, 0, yd->user);
4481        yahoo_packet_hash(pkt, 5, who);
4482        yahoo_packet_hash(pkt, 14, msg);
4483        yahoo_packet_hash(pkt, 27, name);
4484        yahoo_packet_hash(pkt, 28, size_str);
4485
4486        content_length = YAHOO_PACKET_HDRLEN + yahoo_packet_length(pkt);
4487
4488        snprintf(url, sizeof(url), "http://%s:%d/notifyft", 
4489                        yss->filetransfer_host, yss->filetransfer_port);
4490        snprintf((char *)buff, sizeof(buff), "Y=%s; T=%s",
4491                        yd->cookie_y, yd->cookie_t);
4492        inputs = y_list_prepend(inputs, yid);
4493
4494        sfd = y_new0(struct send_file_data, 1);
4495        sfd->pkt = pkt;
4496        sfd->callback = callback;
4497        sfd->user_data = data;
4498        yahoo_http_post(yid->yd->client_id, url, (char *)buff, content_length+4+size,
4499                        _yahoo_send_file_connected, sfd);
4500}
4501
4502
4503enum yahoo_status yahoo_current_status(int id)
4504{
4505        struct yahoo_data *yd = find_conn_by_id(id);
4506        if(!yd)
4507                return YAHOO_STATUS_OFFLINE;
4508        return yd->current_status;
4509}
4510
4511const YList * yahoo_get_buddylist(int id)
4512{
4513        struct yahoo_data *yd = find_conn_by_id(id);
4514        if(!yd)
4515                return NULL;
4516        return yd->buddies;
4517}
4518
4519const YList * yahoo_get_ignorelist(int id)
4520{
4521        struct yahoo_data *yd = find_conn_by_id(id);
4522        if(!yd)
4523                return NULL;
4524        return yd->ignore;
4525}
4526
4527const YList * yahoo_get_identities(int id)
4528{
4529        struct yahoo_data *yd = find_conn_by_id(id);
4530        if(!yd)
4531                return NULL;
4532        return yd->identities;
4533}
4534
4535const char * yahoo_get_cookie(int id, const char *which)
4536{
4537        struct yahoo_data *yd = find_conn_by_id(id);
4538        if(!yd)
4539                return NULL;
4540        if(!strncasecmp(which, "y", 1))
4541                return yd->cookie_y;
4542        if(!strncasecmp(which, "t", 1))
4543                return yd->cookie_t;
4544        if(!strncasecmp(which, "c", 1))
4545                return yd->cookie_c;
4546        if(!strncasecmp(which, "login", 5))
4547                return yd->login_cookie;
4548        return NULL;
4549}
4550
4551void yahoo_get_url_handle(int id, const char *url, 
4552                yahoo_get_url_handle_callback callback, void *data)
4553{
4554        struct yahoo_data *yd = find_conn_by_id(id);
4555        if(!yd)
4556                return;
4557
4558        yahoo_get_url_fd(id, url, yd, callback, data);
4559}
4560
4561const char * yahoo_get_profile_url( void )
4562{
4563        return profile_url;
4564}
4565
Note: See TracBrowser for help on using the repository browser.