comparison src/protocols/yahoo/yahoo.c @ 2681:37d80035e77f

[gaim-migrate @ 2694] don't ask. committer: Tailor Script <tailor@pidgin.im>
author Eric Warmenhoven <eric@warmenhoven.org>
date Tue, 06 Nov 2001 23:58:24 +0000
parents
children db2b0b733732
comparison
equal deleted inserted replaced
2680:ab2ca2770d2e 2681:37d80035e77f
1 /*
2 * gaim
3 *
4 * Some code copyright (C) 1998-1999, Mark Spencer <markster@marko.net>
5 * libfaim code copyright 1998, 1999 Adam Fritzler <afritz@auk.cx>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 */
22
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26
27
28 #include <netdb.h>
29 #include <unistd.h>
30 #include <errno.h>
31 #include <netinet/in.h>
32 #include <arpa/inet.h>
33 #include <string.h>
34 #include <stdlib.h>
35 #include <stdio.h>
36 #include <time.h>
37 #include <sys/socket.h>
38 #include <sys/stat.h>
39 #include <ctype.h>
40 #include <crypt.h>
41 #include "multi.h"
42 #include "prpl.h"
43 #include "gaim.h"
44 #include "proxy.h"
45
46 #include "pixmaps/status-away.xpm"
47 #include "pixmaps/status-here.xpm"
48 #include "pixmaps/status-idle.xpm"
49
50 #define YAHOO_DEBUG
51
52 #define USEROPT_MAIL 0
53
54 #define USEROPT_PAGERHOST 3
55 #define YAHOO_PAGER_HOST "scs.yahoo.com"
56 #define USEROPT_PAGERPORT 4
57 #define YAHOO_PAGER_PORT 5050
58
59 enum yahoo_service {
60 YAHOO_SERVICE_LOGON = 1,
61 YAHOO_SERVICE_LOGOFF,
62 YAHOO_SERVICE_ISAWAY,
63 YAHOO_SERVICE_ISBACK,
64 YAHOO_SERVICE_IDLE,
65 YAHOO_SERVICE_MESSAGE,
66 YAHOO_SERVICE_IDACT,
67 YAHOO_SERVICE_IDDEACT,
68 YAHOO_SERVICE_MAILSTAT,
69 YAHOO_SERVICE_USERSTAT,
70 YAHOO_SERVICE_NEWMAIL,
71 YAHOO_SERVICE_CHATINVITE,
72 YAHOO_SERVICE_CALENDAR,
73 YAHOO_SERVICE_NEWPERSONALMAIL,
74 YAHOO_SERVICE_NEWCONTACT,
75 YAHOO_SERVICE_ADDIDENT,
76 YAHOO_SERVICE_ADDIGNORE,
77 YAHOO_SERVICE_PING,
78 YAHOO_SERVICE_GROUPRENAME,
79 YAHOO_SERVICE_SYSMESSAGE = 20,
80 YAHOO_SERVICE_PASSTHROUGH2 = 22,
81 YAHOO_SERVICE_CONFINVITE = 24,
82 YAHOO_SERVICE_CONFLOGON,
83 YAHOO_SERVICE_CONFDECLINE,
84 YAHOO_SERVICE_CONFLOGOFF,
85 YAHOO_SERVICE_CONFADDINVITE,
86 YAHOO_SERVICE_CONFMSG,
87 YAHOO_SERVICE_CHATLOGON,
88 YAHOO_SERVICE_CHATLOGOFF,
89 YAHOO_SERVICE_CHATMSG = 32,
90 YAHOO_SERVICE_GAMELOGON = 40,
91 YAHOO_SERVICE_GAMELOGOFF = 41,
92 YAHOO_SERVICE_FILETRANSFER = 70,
93 YAHOO_SERVICE_LIST = 85,
94 YAHOO_SERVICE_ADDBUDDY = 131,
95 YAHOO_SERVICE_REMBUDDY = 132
96 };
97
98 enum yahoo_status {
99 YAHOO_STATUS_AVAILABLE,
100 YAHOO_STATUS_BRB,
101 YAHOO_STATUS_BUSY,
102 YAHOO_STATUS_NOTATHOME,
103 YAHOO_STATUS_NOTATDESK,
104 YAHOO_STATUS_NOTINOFFICE,
105 YAHOO_STATUS_ONPHONE,
106 YAHOO_STATUS_ONVACATION,
107 YAHOO_STATUS_OUTTOLUNCH,
108 YAHOO_STATUS_STEPPEDOUT,
109 YAHOO_STATUS_INVISIBLE = 12,
110 YAHOO_STATUS_CUSTOM = 99,
111 YAHOO_STATUS_IDLE = 999,
112 YAHOO_STATUS_OFFLINE = 0x5a55aa56
113 };
114
115 struct yahoo_data {
116 int fd;
117 guchar *rxqueue;
118 int rxlen;
119 GHashTable *hash;
120 GSList *login;
121 int current_status;
122 gboolean logged_in;
123 };
124
125 struct yahoo_pair {
126 int key;
127 char *value;
128 };
129
130 struct yahoo_packet {
131 guint16 service;
132 guint32 status;
133 guint32 id;
134 GSList *hash;
135 };
136
137 struct yahoo_buddy {
138 char *name;
139 int state;
140 char *msg;
141 };
142
143 static char *yahoo_name() {
144 return "Yahoo";
145 }
146
147 #define YAHOO_PACKET_HDRLEN (4 + 2 + 2 + 2 + 2 + 4 + 4)
148
149 static struct yahoo_packet *yahoo_packet_new(enum yahoo_service service, enum yahoo_status status, int id)
150 {
151 struct yahoo_packet *pkt = g_new0(struct yahoo_packet, 1);
152
153 pkt->service = service;
154 pkt->status = status;
155 pkt->id = id;
156
157 return pkt;
158 }
159
160 static void yahoo_packet_hash(struct yahoo_packet *pkt, int key, char *value)
161 {
162 struct yahoo_pair *pair = g_new0(struct yahoo_pair, 1);
163 pair->key = key;
164 pair->value = g_strdup(value);
165 pkt->hash = g_slist_append(pkt->hash, pair);
166 }
167
168 static int yahoo_packet_length(struct yahoo_packet *pkt)
169 {
170 GSList *l;
171
172 int len = 0;
173
174 l = pkt->hash;
175 while (l) {
176 struct yahoo_pair *pair = l->data;
177 int tmp = pair->key;
178 do {
179 tmp /= 10;
180 len++;
181 } while (tmp);
182 len += 2;
183 len += strlen(pair->value);
184 len += 2;
185 l = l->next;
186 }
187
188 return len;
189 }
190
191 /* sometimes i wish prpls could #include things from other prpls. then i could just
192 * use the routines from libfaim and not have to admit to knowing how they work. */
193 #define yahoo_put16(buf, data) ( \
194 (*(buf) = (u_char)((data)>>8)&0xff), \
195 (*((buf)+1) = (u_char)(data)&0xff), \
196 2)
197 #define yahoo_get16(buf) ((((*(buf))<<8)&0xff00) + ((*((buf)+1)) & 0xff))
198 #define yahoo_put32(buf, data) ( \
199 (*((buf)) = (u_char)((data)>>24)&0xff), \
200 (*((buf)+1) = (u_char)((data)>>16)&0xff), \
201 (*((buf)+2) = (u_char)((data)>>8)&0xff), \
202 (*((buf)+3) = (u_char)(data)&0xff), \
203 4)
204 #define yahoo_get32(buf) ((((*(buf))<<24)&0xff000000) + \
205 (((*((buf)+1))<<16)&0x00ff0000) + \
206 (((*((buf)+2))<< 8)&0x0000ff00) + \
207 (((*((buf)+3) )&0x000000ff)))
208
209 static void yahoo_packet_read(struct yahoo_packet *pkt, guchar *data, int len)
210 {
211 int pos = 0;
212
213 while (pos + 1 < len) {
214 char key[64], *value;
215 int x;
216
217 struct yahoo_pair *pair = g_new0(struct yahoo_pair, 1);
218 pkt->hash = g_slist_append(pkt->hash, pair);
219
220 x = 0;
221 while (pos + 1 < len) {
222 if (data[pos] == 0xc0 && data[pos + 1] == 0x80)
223 break;
224 key[x++] = data[pos++];
225 }
226 key[x] = 0;
227 pos += 2;
228 pair->key = strtol(key, NULL, 10);
229
230 value = g_malloc(len - pos);
231 x = 0;
232 while (pos + 1 < len) {
233 if (data[pos] == 0xc0 && data[pos + 1] == 0x80)
234 break;
235 value[x++] = data[pos++];
236 }
237 value[x] = 0;
238 pos += 2;
239 pair->value = g_strdup(value);
240 g_free(value);
241 debug_printf("Key: %d \tValue: %s\n", pair->key, pair->value);
242 }
243 }
244
245 static void yahoo_packet_write(struct yahoo_packet *pkt, guchar *data)
246 {
247 GSList *l = pkt->hash;
248 int pos = 0;
249
250 while (l) {
251 struct yahoo_pair *pair = l->data;
252 guchar buf[100];
253
254 g_snprintf(buf, sizeof(buf), "%d", pair->key);
255 strcpy(data + pos, buf);
256 pos += strlen(buf);
257 data[pos++] = 0xc0;
258 data[pos++] = 0x80;
259
260 strcpy(data + pos, pair->value);
261 pos += strlen(pair->value);
262 data[pos++] = 0xc0;
263 data[pos++] = 0x80;
264
265 l = l->next;
266 }
267 }
268
269 static void yahoo_packet_dump(guchar *data, int len)
270 {
271 #ifdef YAHOO_DEBUG
272 int i;
273 for (i = 0; i + 1 < len; i += 2) {
274 if ((i % 16 == 0) && i)
275 debug_printf("\n");
276 debug_printf("%02x", data[i]);
277 debug_printf("%02x ", data[i+1]);
278 }
279 if (i < len)
280 debug_printf("%02x", data[i]);
281 debug_printf("\n");
282 for (i = 0; i < len; i++) {
283 if ((i % 16 == 0) && i)
284 debug_printf("\n");
285 if (isprint(data[i]))
286 debug_printf("%c ", data[i]);
287 else
288 debug_printf(". ");
289 }
290 debug_printf("\n");
291 #endif
292 }
293
294 static int yahoo_send_packet(struct yahoo_data *yd, struct yahoo_packet *pkt)
295 {
296 int pktlen = yahoo_packet_length(pkt);
297 int len = YAHOO_PACKET_HDRLEN + pktlen;
298 int ret;
299
300 guchar *data;
301 int pos = 0;
302
303 if (yd->fd < 0)
304 return -1;
305
306 data = g_malloc0(len + 1);
307
308 memcpy(data + pos, "YMSG", 4); pos += 4;
309 pos += yahoo_put16(data + pos, 0x0600);
310 pos += yahoo_put16(data + pos, 0x0000);
311 pos += yahoo_put16(data + pos, pktlen);
312 pos += yahoo_put16(data + pos, pkt->service);
313 pos += yahoo_put32(data + pos, pkt->status);
314 pos += yahoo_put32(data + pos, pkt->id);
315
316 yahoo_packet_write(pkt, data + pos);
317
318 yahoo_packet_dump(data, len);
319 ret = write(yd->fd, data, len);
320
321 g_free(data);
322
323 return ret;
324 }
325
326 static void yahoo_packet_free(struct yahoo_packet *pkt)
327 {
328 while (pkt->hash) {
329 struct yahoo_pair *pair = pkt->hash->data;
330 g_free(pair->value);
331 g_free(pair);
332 pkt->hash = g_slist_remove(pkt->hash, pair);
333 }
334 g_free(pkt);
335 }
336
337 static void yahoo_process_logon(struct gaim_connection *gc, struct yahoo_packet *pkt)
338 {
339 struct yahoo_data *yd = gc->proto_data;
340 GSList *l = pkt->hash;
341 struct yahoo_buddy *buddy = NULL;
342 struct yahoo_packet *newpkt;
343 char *name = NULL;
344 int state = 0;
345 char *msg = NULL;
346
347 while (l) {
348 struct yahoo_pair *pair = l->data;
349
350 switch (pair->key) {
351 case 0: /* we won't actually do anything with this */
352 break;
353 case 1: /* we don't get the full buddy list here. */
354 account_online(gc);
355 serv_finish_login(gc);
356 g_snprintf(gc->displayname, sizeof(gc->displayname), "%s", pair->value);
357 do_import(gc, NULL);
358 yd->logged_in = TRUE;
359
360 /* this requests the list. i have a feeling that this is very evil */
361 newpkt = yahoo_packet_new(YAHOO_SERVICE_LIST, YAHOO_STATUS_OFFLINE, 0);
362 yahoo_send_packet(yd, newpkt);
363 yahoo_packet_free(newpkt);
364
365 break;
366 case 8: /* how many online buddies we have */
367 break;
368 case 7: /* the current buddy */
369 name = pair->value;
370 break;
371 case 10: /* state */
372 state = strtol(pair->value, NULL, 10);
373 break;
374 case 19: /* custom message */
375 msg = pair->value;
376 break;
377 case 11: /* i didn't know what this was in the old protocol either */
378 break;
379 case 17: /* in chat? */
380 break;
381 case 13: /* in pager, i think this should always be 1 */
382 /* we don't actually give notification here. we wait until after we've
383 * gotten the list, so that they get added to the right group */
384 buddy = g_new0(struct yahoo_buddy, 1);
385 buddy->name = g_strdup(name);
386 buddy->state = state;
387 buddy->msg = msg ? g_strdup(msg) : NULL;
388 yd->login = g_slist_append(yd->login, buddy);
389 break;
390 default:
391 debug_printf("unknown login key %d\n", pair->key);
392 break;
393 }
394
395 l = l->next;
396 }
397 }
398
399 static void yahoo_process_list(struct gaim_connection *gc, struct yahoo_packet *pkt)
400 {
401 struct yahoo_data *yd = gc->proto_data;
402 GSList *l = pkt->hash;
403 gboolean export = FALSE;
404
405 while (l) {
406 char **lines;
407 char **split;
408 char **buddies;
409 char **tmp, **bud;
410
411 struct yahoo_pair *pair = l->data;
412 l = l->next;
413
414 if (pair->key != 87)
415 continue;
416
417 lines = g_strsplit(pair->value, "\n", -1);
418 for (tmp = lines; *tmp; tmp++) {
419 split = g_strsplit(*tmp, ":", 2);
420 buddies = g_strsplit(split[1], ",", -1);
421 for (bud = buddies; *bud; bud++)
422 if (!find_buddy(gc, *bud)) {
423 add_buddy(gc, split[0], *bud, *bud);
424 export = TRUE;
425 }
426 g_strfreev(buddies);
427 g_strfreev(split);
428 }
429 g_strfreev(lines);
430 }
431
432 if (export)
433 do_export(gc);
434
435 while (yd->login) {
436 struct yahoo_buddy *buddy = yd->login->data;
437 int status = buddy->state;
438 yd->login = g_slist_remove(yd->login, buddy);
439 if (status == YAHOO_STATUS_AVAILABLE)
440 serv_got_update(gc, buddy->name, 1, 0, 0, 0, 0, 0);
441 else if (status == YAHOO_STATUS_IDLE)
442 serv_got_update(gc, buddy->name, 1, 0, 0, time(NULL) - 600, (status << 1), 0);
443 else
444 serv_got_update(gc, buddy->name, 1, 0, 0, 0, (status << 1) | UC_UNAVAILABLE, 0);
445 if (status == YAHOO_STATUS_CUSTOM) {
446 gpointer val = g_hash_table_lookup(yd->hash, buddy->name);
447 if (val) {
448 g_free(val);
449 g_hash_table_insert(yd->hash, buddy->name, g_strdup(buddy->msg));
450 } else
451 g_hash_table_insert(yd->hash, g_strdup(buddy->name), g_strdup(buddy->msg));
452 }
453 g_free(buddy->msg);
454 g_free(buddy->name);
455 g_free(buddy);
456 }
457 }
458
459 static void yahoo_process_message(struct gaim_connection *gc, struct yahoo_packet *pkt)
460 {
461 char *msg = NULL;
462 char *from = NULL;
463 time_t tm = time(NULL);
464 GSList *l = pkt->hash;
465
466 while (l) {
467 struct yahoo_pair *pair = l->data;
468 if (pair->key == 4)
469 from = pair->value;
470 if (pair->key == 14)
471 msg = pair->value;
472 if (pair->key == 15)
473 tm = strtol(pair->value, NULL, 10);
474 l = l->next;
475 }
476
477 if (pkt->status == 1) {
478 strip_linefeed(msg);
479 serv_got_im(gc, from, msg, 0, tm);
480 } else if (pkt->status == 2) {
481 do_error_dialog(_("Your message did not get sent."), _("Gaim - Error"));
482 }
483 }
484
485 static void yahoo_process_status(struct gaim_connection *gc, struct yahoo_packet *pkt)
486 {
487 struct yahoo_data *yd = gc->proto_data;
488 GSList *l = pkt->hash;
489 char *name = NULL;
490 int state = 0;
491 char *msg = NULL;
492
493 while (l) {
494 struct yahoo_pair *pair = l->data;
495
496 switch (pair->key) {
497 case 7:
498 name = pair->value;
499 break;
500 case 10:
501 state = strtol(pair->value, NULL, 10);
502 break;
503 case 19:
504 msg = pair->value;
505 break;
506 case 11: /* i didn't know what this was in the old protocol either */
507 break;
508 case 17: /* in chat? */
509 break;
510 case 13:
511 if (strtol(pair->value, NULL, 10) != 1) {
512 serv_got_update(gc, name, 0, 0, 0, 0, 0, 0);
513 break;
514 }
515 if (state == YAHOO_STATUS_AVAILABLE)
516 serv_got_update(gc, name, 1, 0, 0, 0, 0, 0);
517 else if (state == YAHOO_STATUS_IDLE)
518 serv_got_update(gc, name, 1, 0, 0, time(NULL) - 600, (state << 1), 0);
519 else
520 serv_got_update(gc, name, 1, 0, 0, 0, (state << 1) | UC_UNAVAILABLE, 0);
521 if (state == YAHOO_STATUS_CUSTOM) {
522 gpointer val = g_hash_table_lookup(yd->hash, name);
523 if (val) {
524 g_free(val);
525 g_hash_table_insert(yd->hash, name, g_strdup(msg));
526 } else
527 g_hash_table_insert(yd->hash, g_strdup(name), g_strdup(msg));
528 }
529 break;
530 }
531
532 l = l->next;
533 }
534 }
535
536 static void yahoo_process_contact(struct gaim_connection *gc, struct yahoo_packet *pkt)
537 {
538 char *id = NULL;
539 char *who = NULL;
540 char *msg = NULL;
541 GSList *l = pkt->hash;
542
543 while (l) {
544 struct yahoo_pair *pair = l->data;
545 if (pair->key == 1)
546 id = pair->value;
547 else if (pair->key == 3)
548 who = pair->value;
549 else if (pair->key == 14)
550 msg = pair->value;
551 l = l->next;
552 }
553
554 show_got_added(gc, id, who, NULL, msg);
555 }
556
557 static void yahoo_process_mail(struct gaim_connection *gc, struct yahoo_packet *pkt)
558 {
559 char *who = NULL;
560 char *email = NULL;
561 char *subj = NULL;
562 int count = 0;
563 GSList *l = pkt->hash;
564
565 while (l) {
566 struct yahoo_pair *pair = l->data;
567 if (pair->key == 9)
568 count = strtol(pair->value, NULL, 10);
569 else if (pair->key == 43)
570 who = pair->value;
571 else if (pair->key == 42)
572 email = pair->value;
573 else if (pair->key == 18)
574 subj = pair->value;
575 l = l->next;
576 }
577
578 connection_has_mail(gc, count, NULL, NULL, "http://mail.yahoo.com/");
579 }
580
581 static void yahoo_packet_process(struct gaim_connection *gc, struct yahoo_packet *pkt)
582 {
583 switch (pkt->service)
584 {
585 case YAHOO_SERVICE_LOGON:
586 yahoo_process_logon(gc, pkt);
587 break;
588 case YAHOO_SERVICE_ISAWAY:
589 yahoo_process_status(gc, pkt);
590 break;
591 case YAHOO_SERVICE_MESSAGE:
592 yahoo_process_message(gc, pkt);
593 break;
594 case YAHOO_SERVICE_NEWMAIL:
595 yahoo_process_mail(gc, pkt);
596 break;
597 case YAHOO_SERVICE_NEWCONTACT:
598 yahoo_process_contact(gc, pkt);
599 break;
600 case YAHOO_SERVICE_LIST:
601 yahoo_process_list(gc, pkt);
602 break;
603 default:
604 debug_printf("unhandled service %d\n", pkt->service);
605 break;
606 }
607 }
608
609 static void yahoo_pending(gpointer data, gint source, GaimInputCondition cond)
610 {
611 struct gaim_connection *gc = data;
612 struct yahoo_data *yd = gc->proto_data;
613 char buf[1024];
614 int len;
615
616 len = read(yd->fd, buf, sizeof(buf));
617
618 if (len <= 0) {
619 hide_login_progress(gc, "Unable to read");
620 signoff(gc);
621 return;
622 }
623
624 yd->rxqueue = g_realloc(yd->rxqueue, len + yd->rxlen);
625 memcpy(yd->rxqueue + yd->rxlen, buf, len);
626 yd->rxlen += len;
627
628 while (1) {
629 struct yahoo_packet *pkt;
630 int pos = 0;
631 int pktlen;
632
633 if (yd->rxlen < YAHOO_PACKET_HDRLEN)
634 return;
635
636 pos += 4; /* YMSG */
637 pos += 2;
638 pos += 2;
639
640 pktlen = yahoo_get16(yd->rxqueue + pos); pos += 2;
641 debug_printf("%d bytes to read, rxlen is %d\n", pktlen, yd->rxlen);
642
643 if (yd->rxlen < (YAHOO_PACKET_HDRLEN + pktlen))
644 return;
645
646 yahoo_packet_dump(yd->rxqueue, YAHOO_PACKET_HDRLEN + pktlen);
647
648 pkt = yahoo_packet_new(0, 0, 0);
649
650 pkt->service = yahoo_get16(yd->rxqueue + pos); pos += 2;
651 debug_printf("Yahoo Service: %d Status: %d\n", pkt->service, pkt->status);
652 pkt->status = yahoo_get32(yd->rxqueue + pos); pos += 4;
653 pkt->id = yahoo_get32(yd->rxqueue + pos); pos += 4;
654
655 yahoo_packet_read(pkt, yd->rxqueue + pos, pktlen);
656
657 yd->rxlen -= YAHOO_PACKET_HDRLEN + pktlen;
658 if (yd->rxlen) {
659 char *tmp = g_memdup(yd->rxqueue + YAHOO_PACKET_HDRLEN + pktlen, yd->rxlen);
660 g_free(yd->rxqueue);
661 yd->rxqueue = tmp;
662 } else {
663 g_free(yd->rxqueue);
664 yd->rxqueue = NULL;
665 }
666
667 yahoo_packet_process(gc, pkt);
668
669 yahoo_packet_free(pkt);
670 }
671 }
672
673 static void yahoo_got_connected(gpointer data, gint source, GaimInputCondition cond)
674 {
675 struct gaim_connection *gc = data;
676 struct yahoo_data *yd;
677 struct yahoo_packet *pkt;
678
679 if (!g_slist_find(connections, gc)) {
680 close(source);
681 return;
682 }
683
684 if (source < 0) {
685 hide_login_progress(gc, "Unable to connect");
686 signoff(gc);
687 return;
688 }
689
690 yd = gc->proto_data;
691 yd->fd = source;
692
693 pkt = yahoo_packet_new(YAHOO_SERVICE_LOGON, YAHOO_STATUS_AVAILABLE, 0);
694
695 yahoo_packet_hash(pkt, 0, gc->username);
696 yahoo_packet_hash(pkt, 1, gc->username);
697 yahoo_packet_hash(pkt, 6, crypt(gc->password, "$1$_2S43d5f$"));
698
699 yahoo_send_packet(yd, pkt);
700
701 yahoo_packet_free(pkt);
702
703 gc->inpa = gaim_input_add(yd->fd, GAIM_INPUT_READ, yahoo_pending, gc);
704 }
705
706 static void yahoo_login(struct aim_user *user) {
707 struct gaim_connection *gc = new_gaim_conn(user);
708 struct yahoo_data *yd = gc->proto_data = g_new0(struct yahoo_data, 1);
709
710 set_login_progress(gc, 1, "Connecting");
711
712 yd->fd = -1;
713 yd->hash = g_hash_table_new(g_str_hash, g_str_equal);
714
715 if (!proxy_connect(user->proto_opt[USEROPT_PAGERHOST][0] ?
716 user->proto_opt[USEROPT_PAGERHOST] : YAHOO_PAGER_HOST,
717 user->proto_opt[USEROPT_PAGERPORT][0] ?
718 atoi(user->proto_opt[USEROPT_PAGERPORT]) : YAHOO_PAGER_PORT,
719 yahoo_got_connected, gc)) {
720 hide_login_progress(gc, "Connection problem");
721 signoff(gc);
722 return;
723 }
724
725 }
726
727 static gboolean yahoo_destroy_hash(gpointer key, gpointer val, gpointer data)
728 {
729 g_free(key);
730 g_free(val);
731 return TRUE;
732 }
733
734 static void yahoo_close(struct gaim_connection *gc) {
735 struct yahoo_data *yd = (struct yahoo_data *)gc->proto_data;
736 g_hash_table_foreach_remove(yd->hash, yahoo_destroy_hash, NULL);
737 g_hash_table_destroy(yd->hash);
738 if (yd->fd >= 0)
739 close(yd->fd);
740 if (yd->rxqueue)
741 g_free(yd->rxqueue);
742 while (yd->login) {
743 struct yahoo_buddy *buddy = yd->login->data;
744 yd->login = g_slist_remove(yd->login, buddy);
745 g_free(buddy->msg);
746 g_free(buddy->name);
747 g_free(buddy);
748 }
749 if (gc->inpa)
750 gaim_input_remove(gc->inpa);
751 g_free(yd);
752 }
753
754 static char **yahoo_list_icon(int uc)
755 {
756 if ((uc >> 1) == YAHOO_STATUS_IDLE)
757 return status_idle_xpm;
758 else if (uc == 0)
759 return status_here_xpm;
760 return status_away_xpm;
761 }
762
763 static char *yahoo_get_status_string(enum yahoo_status a)
764 {
765 switch (a) {
766 case YAHOO_STATUS_BRB:
767 return "Be Right Back";
768 case YAHOO_STATUS_BUSY:
769 return "Busy";
770 case YAHOO_STATUS_NOTATHOME:
771 return "Not At Home";
772 case YAHOO_STATUS_NOTATDESK:
773 return "Not At Desk";
774 case YAHOO_STATUS_NOTINOFFICE:
775 return "Not In Office";
776 case YAHOO_STATUS_ONPHONE:
777 return "On Phone";
778 case YAHOO_STATUS_ONVACATION:
779 return "On Vacation";
780 case YAHOO_STATUS_OUTTOLUNCH:
781 return "Out To Lunch";
782 case YAHOO_STATUS_STEPPEDOUT:
783 return "Stepped Out";
784 default:
785 return NULL;
786 }
787 }
788
789 static GList *yahoo_buddy_menu(struct gaim_connection *gc, char *who)
790 {
791 GList *m = NULL;
792 struct proto_buddy_menu *pbm;
793 struct yahoo_data *yd = (struct yahoo_data *)gc->proto_data;
794 struct buddy *b = find_buddy(gc, who); /* this should never be null. if it is,
795 segfault and get the bug report. */
796 static char buf[1024];
797
798 if (!(b->uc & UC_UNAVAILABLE))
799 return NULL;
800
801 pbm = g_new0(struct proto_buddy_menu, 1);
802 if ((b->uc >> 1) != YAHOO_STATUS_CUSTOM)
803 g_snprintf(buf, sizeof buf, "Status: %s", yahoo_get_status_string(b->uc >> 1));
804 else
805 g_snprintf(buf, sizeof buf, "Custom Status: %s",
806 (char *)g_hash_table_lookup(yd->hash, b->name));
807 pbm->label = buf;
808 pbm->callback = NULL;
809 pbm->gc = gc;
810 m = g_list_append(m, pbm);
811
812 return m;
813 }
814
815 static GList *yahoo_user_opts()
816 {
817 GList *m = NULL;
818 struct proto_user_opt *puo;
819
820 puo = g_new0(struct proto_user_opt, 1);
821 puo->label = "Pager Host:";
822 puo->def = YAHOO_PAGER_HOST;
823 puo->pos = USEROPT_PAGERHOST;
824 m = g_list_append(m, puo);
825
826 puo = g_new0(struct proto_user_opt, 1);
827 puo->label = "Pager Port:";
828 puo->def = "5050";
829 puo->pos = USEROPT_PAGERPORT;
830 m = g_list_append(m, puo);
831
832 return m;
833 }
834
835 static void yahoo_act_id(gpointer data, char *entry)
836 {
837 struct gaim_connection *gc = data;
838 struct yahoo_data *yd = gc->proto_data;
839
840 struct yahoo_packet *pkt = yahoo_packet_new(YAHOO_SERVICE_IDACT, YAHOO_STATUS_AVAILABLE, 0);
841 yahoo_packet_hash(pkt, 3, entry);
842 yahoo_send_packet(yd, pkt);
843 yahoo_packet_free(pkt);
844
845 g_snprintf(gc->displayname, sizeof(gc->displayname), "%s", entry);
846 }
847
848 static void yahoo_do_action(struct gaim_connection *gc, char *act)
849 {
850 if (!strcmp(act, "Activate ID")) {
851 do_prompt_dialog("Activate which ID:", gc->displayname, gc, yahoo_act_id, NULL);
852 }
853 }
854
855 static GList *yahoo_actions() {
856 GList *m = NULL;
857
858 m = g_list_append(m, "Activate ID");
859
860 return m;
861 }
862
863 static int yahoo_send_im(struct gaim_connection *gc, char *who, char *what, int flags)
864 {
865 struct yahoo_data *yd = gc->proto_data;
866 struct yahoo_packet *pkt = yahoo_packet_new(YAHOO_SERVICE_MESSAGE, YAHOO_STATUS_OFFLINE, 0);
867
868 yahoo_packet_hash(pkt, 1, gc->displayname);
869 yahoo_packet_hash(pkt, 5, who);
870 yahoo_packet_hash(pkt, 14, what);
871
872 yahoo_send_packet(yd, pkt);
873
874 yahoo_packet_free(pkt);
875
876 return 1;
877 }
878
879 static void yahoo_set_away(struct gaim_connection *gc, char *state, char *msg)
880 {
881 struct yahoo_data *yd = (struct yahoo_data *)gc->proto_data;
882 struct yahoo_packet *pkt;
883 char s[4];
884
885 gc->away = NULL;
886
887 if (msg) {
888 yd->current_status = YAHOO_STATUS_CUSTOM;
889 gc->away = "";
890 } else if (state) {
891 gc->away = "";
892 if (!strcmp(state, "Available")) {
893 yd->current_status = YAHOO_STATUS_AVAILABLE;
894 gc->away = NULL;
895 } else if (!strcmp(state, "Be Right Back")) {
896 yd->current_status = YAHOO_STATUS_BRB;
897 } else if (!strcmp(state, "Busy")) {
898 yd->current_status = YAHOO_STATUS_BUSY;
899 } else if (!strcmp(state, "Not At Home")) {
900 yd->current_status = YAHOO_STATUS_NOTATHOME;
901 } else if (!strcmp(state, "Not At Desk")) {
902 yd->current_status = YAHOO_STATUS_NOTATDESK;
903 } else if (!strcmp(state, "Not In Office")) {
904 yd->current_status = YAHOO_STATUS_NOTINOFFICE;
905 } else if (!strcmp(state, "On Phone")) {
906 yd->current_status = YAHOO_STATUS_ONPHONE;
907 } else if (!strcmp(state, "On Vacation")) {
908 yd->current_status = YAHOO_STATUS_ONVACATION;
909 } else if (!strcmp(state, "Out To Lunch")) {
910 yd->current_status = YAHOO_STATUS_OUTTOLUNCH;
911 } else if (!strcmp(state, "Stepped Out")) {
912 yd->current_status = YAHOO_STATUS_STEPPEDOUT;
913 } else if (!strcmp(state, "Invisible")) {
914 yd->current_status = YAHOO_STATUS_INVISIBLE;
915 } else if (!strcmp(state, GAIM_AWAY_CUSTOM)) {
916 if (gc->is_idle) {
917 yd->current_status = YAHOO_STATUS_IDLE;
918 } else {
919 yd->current_status = YAHOO_STATUS_AVAILABLE;
920 }
921 gc->away = NULL;
922 }
923 } else if (gc->is_idle) {
924 yd->current_status = YAHOO_STATUS_IDLE;
925 } else {
926 yd->current_status = YAHOO_STATUS_AVAILABLE;
927 }
928
929 pkt = yahoo_packet_new(YAHOO_SERVICE_ISAWAY, yd->current_status, 0);
930 g_snprintf(s, sizeof(s), "%d", yd->current_status);
931 yahoo_packet_hash(pkt, 10, s);
932 if (yd->current_status == YAHOO_STATUS_CUSTOM)
933 yahoo_packet_hash(pkt, 19, msg);
934
935 yahoo_send_packet(yd, pkt);
936 yahoo_packet_free(pkt);
937 }
938
939 static void yahoo_set_idle(struct gaim_connection *gc, int idle)
940 {
941 struct yahoo_data *yd = gc->proto_data;
942 struct yahoo_packet *pkt = NULL;
943
944 if (idle && yd->current_status == YAHOO_STATUS_AVAILABLE) {
945 pkt = yahoo_packet_new(YAHOO_SERVICE_ISAWAY, YAHOO_STATUS_IDLE, 0);
946 yd->current_status = YAHOO_STATUS_IDLE;
947 } else if (!idle && yd->current_status == YAHOO_STATUS_IDLE) {
948 pkt = yahoo_packet_new(YAHOO_SERVICE_ISAWAY, YAHOO_STATUS_AVAILABLE, 0);
949 yd->current_status = YAHOO_STATUS_AVAILABLE;
950 }
951
952 if (pkt) {
953 char buf[4];
954 g_snprintf(buf, sizeof(buf), "%d", yd->current_status);
955 yahoo_packet_hash(pkt, 10, buf);
956 yahoo_send_packet(yd, pkt);
957 yahoo_packet_free(pkt);
958 }
959 }
960
961 static GList *yahoo_away_states(struct gaim_connection *gc)
962 {
963 GList *m = NULL;
964
965 m = g_list_append(m, "Available");
966 m = g_list_append(m, "Be Right Back");
967 m = g_list_append(m, "Busy");
968 m = g_list_append(m, "Not At Home");
969 m = g_list_append(m, "Not At Desk");
970 m = g_list_append(m, "Not In Office");
971 m = g_list_append(m, "On Phone");
972 m = g_list_append(m, "On Vacation");
973 m = g_list_append(m, "Out To Lunch");
974 m = g_list_append(m, "Stepped Out");
975 m = g_list_append(m, "Invisible");
976 m = g_list_append(m, GAIM_AWAY_CUSTOM);
977
978 return m;
979 }
980
981 static void yahoo_keepalive(struct gaim_connection *gc)
982 {
983 struct yahoo_data *yd = gc->proto_data;
984 struct yahoo_packet *pkt = yahoo_packet_new(YAHOO_SERVICE_PING, YAHOO_STATUS_AVAILABLE, 0);
985 yahoo_send_packet(yd, pkt);
986 yahoo_packet_free(pkt);
987 }
988
989 static void yahoo_add_buddy(struct gaim_connection *gc, char *who)
990 {
991 struct yahoo_data *yd = (struct yahoo_data *)gc->proto_data;
992 struct yahoo_packet *pkt;
993 struct group *g;
994 char *group = NULL;
995
996 if (!yd->logged_in)
997 return;
998
999 g = find_group_by_buddy(gc, who);
1000 if (g)
1001 group = g->name;
1002 else
1003 group = "Buddies";
1004
1005 pkt = yahoo_packet_new(YAHOO_SERVICE_ADDBUDDY, YAHOO_STATUS_AVAILABLE, 0);
1006 yahoo_packet_hash(pkt, 1, gc->displayname);
1007 yahoo_packet_hash(pkt, 7, who);
1008 yahoo_packet_hash(pkt, 65, group);
1009 yahoo_send_packet(yd, pkt);
1010 yahoo_packet_free(pkt);
1011 }
1012
1013 static void yahoo_remove_buddy(struct gaim_connection *gc, char *who, char *group)
1014 {
1015 struct yahoo_data *yd = (struct yahoo_data *)gc->proto_data;
1016
1017 struct yahoo_packet *pkt = yahoo_packet_new(YAHOO_SERVICE_REMBUDDY, YAHOO_STATUS_AVAILABLE, 0);
1018 yahoo_packet_hash(pkt, 1, gc->displayname);
1019 yahoo_packet_hash(pkt, 7, who);
1020 yahoo_packet_hash(pkt, 65, group);
1021 yahoo_send_packet(yd, pkt);
1022 yahoo_packet_free(pkt);
1023 }
1024
1025 static struct prpl *my_protocol = NULL;
1026
1027 void yahoo_init(struct prpl *ret) {
1028 ret->protocol = PROTO_YAHOO;
1029 ret->options = OPT_PROTO_MAIL_CHECK;
1030 ret->name = yahoo_name;
1031 ret->user_opts = yahoo_user_opts;
1032 ret->login = yahoo_login;
1033 ret->close = yahoo_close;
1034 ret->buddy_menu = yahoo_buddy_menu;
1035 ret->list_icon = yahoo_list_icon;
1036 ret->actions = yahoo_actions;
1037 ret->do_action = yahoo_do_action;
1038 ret->send_im = yahoo_send_im;
1039 ret->away_states = yahoo_away_states;
1040 ret->set_away = yahoo_set_away;
1041 ret->set_idle = yahoo_set_idle;
1042 ret->keepalive = yahoo_keepalive;
1043 ret->add_buddy = yahoo_add_buddy;
1044 ret->remove_buddy = yahoo_remove_buddy;
1045
1046 my_protocol = ret;
1047 }
1048
1049 #ifndef STATIC
1050
1051 char *gaim_plugin_init(GModule *handle)
1052 {
1053 load_protocol(yahoo_init, sizeof(struct prpl));
1054 return NULL;
1055 }
1056
1057 void gaim_plugin_remove()
1058 {
1059 struct prpl *p = find_prpl(PROTO_YAHOO);
1060 if (p == my_protocol)
1061 unload_protocol(p);
1062 }
1063
1064 char *name()
1065 {
1066 return "Yahoo";
1067 }
1068
1069 char *description()
1070 {
1071 return PRPL_DESC("Yahoo");
1072 }
1073
1074 #endif