comparison libpurple/protocols/yahoo/libymsg.c @ 27394:16ef6a9e7acd

Start of splitting yahoo into two separate prpls. Not sure if this even comes close to building yet.
author John Bailey <rekkanoryo@rekkanoryo.org>
date Sun, 05 Jul 2009 04:37:33 +0000
parents
children ef5f0cde8d74
comparison
equal deleted inserted replaced
27393:319b0f3dd2d0 27394:16ef6a9e7acd
1 /*
2 * purple
3 *
4 * Purple is the legal property of its developers, whose names are too numerous
5 * to list here. Please refer to the COPYRIGHT file distributed with this
6 * source distribution.
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
21 *
22 */
23
24 #include "internal.h"
25
26 #include "account.h"
27 #include "accountopt.h"
28 #include "blist.h"
29 #include "cipher.h"
30 #include "cmds.h"
31 #include "core.h"
32 #include "debug.h"
33 #include "network.h"
34 #include "notify.h"
35 #include "privacy.h"
36 #include "prpl.h"
37 #include "proxy.h"
38 #include "request.h"
39 #include "server.h"
40 #include "util.h"
41 #include "version.h"
42 #include "xmlnode.h"
43
44 #include "yahoo.h"
45 #include "yahoochat.h"
46 #include "yahoo_aliases.h"
47 #include "yahoo_doodle.h"
48 #include "yahoo_filexfer.h"
49 #include "yahoo_friend.h"
50 #include "yahoo_packet.h"
51 #include "yahoo_picture.h"
52 #include "ycht.h"
53
54 /* #define YAHOO_DEBUG */
55
56 /* #define TRY_WEBMESSENGER_LOGIN 0 */
57
58 /* One hour */
59 #define PING_TIMEOUT 3600
60
61 /* One minute */
62 #define KEEPALIVE_TIMEOUT 60
63
64 static void yahoo_add_buddy(PurpleConnection *gc, PurpleBuddy *, PurpleGroup *);
65 #ifdef TRY_WEBMESSENGER_LOGIN
66 static void yahoo_login_page_cb(PurpleUtilFetchUrlData *url_data, gpointer user_data, const gchar *url_text, size_t len, const gchar *error_message);
67 #endif /* TRY_WEBMESSENGER_LOGIN */
68 static void yahoo_set_status(PurpleAccount *account, PurpleStatus *status);
69
70 static void yahoo_update_status(PurpleConnection *gc, const char *name, YahooFriend *f)
71 {
72 char *status = NULL;
73
74 if (!gc || !name || !f || !purple_find_buddy(purple_connection_get_account(gc), name))
75 return;
76
77 switch (f->status) {
78 case YAHOO_STATUS_OFFLINE:
79 status = YAHOO_STATUS_TYPE_OFFLINE;
80 break;
81 case YAHOO_STATUS_AVAILABLE:
82 status = YAHOO_STATUS_TYPE_AVAILABLE;
83 break;
84 case YAHOO_STATUS_BRB:
85 status = YAHOO_STATUS_TYPE_BRB;
86 break;
87 case YAHOO_STATUS_BUSY:
88 status = YAHOO_STATUS_TYPE_BUSY;
89 break;
90 case YAHOO_STATUS_NOTATHOME:
91 status = YAHOO_STATUS_TYPE_NOTATHOME;
92 break;
93 case YAHOO_STATUS_NOTATDESK:
94 status = YAHOO_STATUS_TYPE_NOTATDESK;
95 break;
96 case YAHOO_STATUS_NOTINOFFICE:
97 status = YAHOO_STATUS_TYPE_NOTINOFFICE;
98 break;
99 case YAHOO_STATUS_ONPHONE:
100 status = YAHOO_STATUS_TYPE_ONPHONE;
101 break;
102 case YAHOO_STATUS_ONVACATION:
103 status = YAHOO_STATUS_TYPE_ONVACATION;
104 break;
105 case YAHOO_STATUS_OUTTOLUNCH:
106 status = YAHOO_STATUS_TYPE_OUTTOLUNCH;
107 break;
108 case YAHOO_STATUS_STEPPEDOUT:
109 status = YAHOO_STATUS_TYPE_STEPPEDOUT;
110 break;
111 case YAHOO_STATUS_INVISIBLE: /* this should never happen? */
112 status = YAHOO_STATUS_TYPE_INVISIBLE;
113 break;
114 case YAHOO_STATUS_CUSTOM:
115 case YAHOO_STATUS_IDLE:
116 if (!f->away)
117 status = YAHOO_STATUS_TYPE_AVAILABLE;
118 else
119 status = YAHOO_STATUS_TYPE_AWAY;
120 break;
121 default:
122 purple_debug_warning("yahoo", "Warning, unknown status %d\n", f->status);
123 break;
124 }
125
126 if (status) {
127 if (f->status == YAHOO_STATUS_CUSTOM)
128 purple_prpl_got_user_status(purple_connection_get_account(gc), name, status, "message",
129 yahoo_friend_get_status_message(f), NULL);
130 else
131 purple_prpl_got_user_status(purple_connection_get_account(gc), name, status, NULL);
132 }
133
134 if (f->idle != 0)
135 purple_prpl_got_user_idle(purple_connection_get_account(gc), name, TRUE, f->idle);
136 else
137 purple_prpl_got_user_idle(purple_connection_get_account(gc), name, FALSE, 0);
138
139 if (f->sms)
140 purple_prpl_got_user_status(purple_connection_get_account(gc), name, YAHOO_STATUS_TYPE_MOBILE, NULL);
141 else
142 purple_prpl_got_user_status_deactive(purple_connection_get_account(gc), name, YAHOO_STATUS_TYPE_MOBILE);
143 }
144
145 static void yahoo_process_status(PurpleConnection *gc, struct yahoo_packet *pkt)
146 {
147 PurpleAccount *account = purple_connection_get_account(gc);
148 GSList *l = pkt->hash;
149 YahooFriend *f = NULL;
150 char *name = NULL;
151 gboolean unicode = FALSE;
152 char *message = NULL;
153 char *msn_name = NULL;
154
155 if (pkt->service == YAHOO_SERVICE_LOGOFF && pkt->status == -1) {
156 if (!purple_account_get_remember_password(account))
157 purple_account_set_password(account, NULL);
158 purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NAME_IN_USE,
159 _("You have signed on from another location."));
160 return;
161 }
162
163 while (l) {
164 struct yahoo_pair *pair = l->data;
165
166 switch (pair->key) {
167 case 0: /* we won't actually do anything with this */
168 case 1: /* we won't actually do anything with this */
169 break;
170 case 8: /* how many online buddies we have */
171 break;
172 case 7: /* the current buddy */
173 /* update the previous buddy before changing the variables */
174 if (f) {
175 if (message)
176 yahoo_friend_set_status_message(f, yahoo_string_decode(gc, message, unicode));
177 if (name)
178 yahoo_update_status(gc, name, f);
179 }
180 name = message = NULL;
181 f = NULL;
182 if (pair->value && g_utf8_validate(pair->value, -1, NULL)) {
183 GSList *tmplist;
184 int protocol = 0;
185
186 name = pair->value;
187
188 /* Look ahead to see if we have the protocol info about the buddy */
189 for (tmplist = l->next; tmplist; tmplist = tmplist->next) {
190 struct yahoo_pair *p = tmplist->data;
191 if (p->key == 7)
192 break;
193 if (p->key == 241) {
194 if(strtol(p->value, NULL, 10) == 2) {
195 g_free(msn_name);
196 msn_name = g_strconcat("msn/", name, NULL);
197 name = msn_name;
198 protocol = 2;
199 }
200 break;
201 }
202 }
203 f = yahoo_friend_find_or_new(gc, name);
204 f->protocol = protocol;
205 }
206 break;
207 case 10: /* state */
208 if (!f)
209 break;
210
211 f->status = strtol(pair->value, NULL, 10);
212 if ((f->status >= YAHOO_STATUS_BRB) && (f->status <= YAHOO_STATUS_STEPPEDOUT))
213 f->away = 1;
214 else
215 f->away = 0;
216
217 if (f->status == YAHOO_STATUS_IDLE) {
218 /* Idle may have already been set in a more precise way in case 137 */
219 if (f->idle == 0)
220 f->idle = time(NULL);
221 } else
222 f->idle = 0;
223
224 if (f->status != YAHOO_STATUS_CUSTOM)
225 yahoo_friend_set_status_message(f, NULL);
226
227 f->sms = 0;
228 break;
229 case 19: /* custom message */
230 if (f)
231 message = pair->value;
232 break;
233 case 11: /* this is the buddy's session id */
234 if (f)
235 f->session_id = strtol(pair->value, NULL, 10);
236 break;
237 case 17: /* in chat? */
238 break;
239 case 47: /* is custom status away or not? 2=idle*/
240 if (!f)
241 break;
242
243 /* I have no idea what it means when this is
244 * set when someone's available, but it doesn't
245 * mean idle. */
246 if (f->status == YAHOO_STATUS_AVAILABLE)
247 break;
248
249 f->away = strtol(pair->value, NULL, 10);
250 if (f->away == 2) {
251 /* Idle may have already been set in a more precise way in case 137 */
252 if (f->idle == 0)
253 f->idle = time(NULL);
254 }
255
256 break;
257 case 138: /* either we're not idle, or we are but won't say how long */
258 if (!f)
259 break;
260
261 if (f->idle)
262 f->idle = -1;
263 break;
264 case 137: /* usually idle time in seconds, sometimes login time */
265 if (!f)
266 break;
267
268 if (f->status != YAHOO_STATUS_AVAILABLE)
269 f->idle = time(NULL) - strtol(pair->value, NULL, 10);
270 break;
271 case 13: /* bitmask, bit 0 = pager, bit 1 = chat, bit 2 = game */
272 if (strtol(pair->value, NULL, 10) == 0) {
273 if (f)
274 f->status = YAHOO_STATUS_OFFLINE;
275 if (name) {
276 purple_prpl_got_user_status(account, name, "offline", NULL);
277 purple_prpl_got_user_status_deactive(account, name, YAHOO_STATUS_TYPE_MOBILE);
278 }
279 break;
280 }
281 break;
282 case 60: /* SMS */
283 if (f) {
284 f->sms = strtol(pair->value, NULL, 10);
285 yahoo_update_status(gc, name, f);
286 }
287 break;
288 case 197: /* Avatars */
289 {
290 guchar *decoded;
291 char *tmp;
292 gsize len;
293
294 if (pair->value) {
295 decoded = purple_base64_decode(pair->value, &len);
296 if (len) {
297 tmp = purple_str_binary_to_ascii(decoded, len);
298 purple_debug_info("yahoo", "Got key 197, value = %s\n", tmp);
299 g_free(tmp);
300 }
301 g_free(decoded);
302 }
303 break;
304 }
305 case 192: /* Pictures, aka Buddy Icons, checksum */
306 {
307 /* FIXME: Please, if you know this protocol,
308 * FIXME: fix up the strtol() stuff if possible. */
309 int cksum = strtol(pair->value, NULL, 10);
310 const char *locksum = NULL;
311 PurpleBuddy *b;
312
313 if (!name)
314 break;
315
316 b = purple_find_buddy(gc->account, name);
317
318 if (!cksum || (cksum == -1)) {
319 if (f)
320 yahoo_friend_set_buddy_icon_need_request(f, TRUE);
321 purple_buddy_icons_set_for_user(gc->account, name, NULL, 0, NULL);
322 break;
323 }
324
325 if (!f)
326 break;
327
328 yahoo_friend_set_buddy_icon_need_request(f, FALSE);
329 if (b) {
330 locksum = purple_buddy_icons_get_checksum_for_user(b);
331 if (!locksum || (cksum != strtol(locksum, NULL, 10)))
332 yahoo_send_picture_request(gc, name);
333 }
334
335 break;
336 }
337 case 16: /* Custom error message */
338 {
339 char *tmp = yahoo_string_decode(gc, pair->value, TRUE);
340 purple_notify_error(gc, NULL, tmp, NULL);
341 g_free(tmp);
342 }
343 break;
344 case 97: /* Unicode status message */
345 unicode = !strcmp(pair->value, "1");
346 break;
347 case 244: /* client version number. Yahoo Client Detection */
348 if(f && strtol(pair->value, NULL, 10))
349 f->version_id = strtol(pair->value, NULL, 10);
350 break;
351 case 241: /* protocol buddy belongs to */
352 break; /* We process this when get '7' */
353 default:
354 purple_debug_warning("yahoo",
355 "Unknown status key %d\n", pair->key);
356 break;
357 }
358
359 l = l->next;
360 }
361
362 if (f) {
363 if (pkt->service == YAHOO_SERVICE_LOGOFF)
364 f->status = YAHOO_STATUS_OFFLINE;
365 if (message)
366 yahoo_friend_set_status_message(f, yahoo_string_decode(gc, message, unicode));
367
368 if (name) /* update the last buddy */
369 yahoo_update_status(gc, name, f);
370 }
371 g_free(msn_name);
372 }
373
374 static void yahoo_do_group_check(PurpleAccount *account, GHashTable *ht, const char *name, const char *group)
375 {
376 PurpleBuddy *b;
377 PurpleGroup *g;
378 GSList *list, *i;
379 gboolean onlist = 0;
380 char *oname = NULL;
381 char **oname_p = &oname;
382 GSList **list_p = &list;
383
384 if (!g_hash_table_lookup_extended(ht, purple_normalize(account, name), (gpointer *) oname_p, (gpointer *) list_p))
385 list = purple_find_buddies(account, name);
386 else
387 g_hash_table_steal(ht, name);
388
389 for (i = list; i; i = i->next) {
390 b = i->data;
391 g = purple_buddy_get_group(b);
392 if (!purple_utf8_strcasecmp(group, purple_group_get_name(g))) {
393 purple_debug_misc("yahoo",
394 "Oh good, %s is in the right group (%s).\n", name, group);
395 list = g_slist_delete_link(list, i);
396 onlist = 1;
397 break;
398 }
399 }
400
401 if (!onlist) {
402 purple_debug_misc("yahoo",
403 "Uhoh, %s isn't on the list (or not in this group), adding him to group %s.\n", name, group);
404 if (!(g = purple_find_group(group))) {
405 g = purple_group_new(group);
406 purple_blist_add_group(g, NULL);
407 }
408 b = purple_buddy_new(account, name, NULL);
409 purple_blist_add_buddy(b, NULL, g, NULL);
410 }
411
412 if (list) {
413 if (!oname)
414 oname = g_strdup(purple_normalize(account, name));
415 g_hash_table_insert(ht, oname, list);
416 } else if (oname)
417 g_free(oname);
418 }
419
420 static void yahoo_do_group_cleanup(gpointer key, gpointer value, gpointer user_data)
421 {
422 char *name = key;
423 GSList *list = value, *i;
424 PurpleBuddy *b;
425 PurpleGroup *g;
426
427 for (i = list; i; i = i->next) {
428 b = i->data;
429 g = purple_buddy_get_group(b);
430 purple_debug_misc("yahoo", "Deleting Buddy %s from group %s.\n", name,
431 purple_group_get_name(g));
432 purple_blist_remove_buddy(b);
433 }
434 }
435
436 static char *_getcookie(char *rawcookie)
437 {
438 char *cookie = NULL;
439 char *tmpcookie;
440 char *cookieend;
441
442 if (strlen(rawcookie) < 2)
443 return NULL;
444 tmpcookie = g_strdup(rawcookie+2);
445 cookieend = strchr(tmpcookie, ';');
446
447 if (cookieend)
448 *cookieend = '\0';
449
450 cookie = g_strdup(tmpcookie);
451 g_free(tmpcookie);
452
453 return cookie;
454 }
455
456 static void yahoo_process_cookie(struct yahoo_data *yd, char *c)
457 {
458 if (c[0] == 'Y') {
459 if (yd->cookie_y)
460 g_free(yd->cookie_y);
461 yd->cookie_y = _getcookie(c);
462 } else if (c[0] == 'T') {
463 if (yd->cookie_t)
464 g_free(yd->cookie_t);
465 yd->cookie_t = _getcookie(c);
466 } else
467 purple_debug_info("yahoo", "Unrecognized cookie '%c'\n", c[0]);
468 yd->cookies = g_slist_prepend(yd->cookies, g_strdup(c));
469 }
470
471 static void yahoo_process_list_15(PurpleConnection *gc, struct yahoo_packet *pkt)
472 {
473 GSList *l = pkt->hash;
474
475 PurpleAccount *account = purple_connection_get_account(gc);
476 struct yahoo_data *yd = gc->proto_data;
477 GHashTable *ht;
478 char *norm_bud = NULL;
479 char *temp = NULL;
480 YahooFriend *f = NULL; /* It's your friends. They're going to want you to share your StarBursts. */
481 /* But what if you had no friends? */
482 PurpleBuddy *b;
483 PurpleGroup *g;
484 int protocol = 0;
485 int stealth = 0;
486
487
488 ht = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_slist_free);
489
490 while (l) {
491 struct yahoo_pair *pair = l->data;
492 l = l->next;
493
494 switch (pair->key) {
495 case 302:
496 /* This is always 318 before a group, 319 before the first s/n in a group, 320 before any ignored s/n.
497 * It is not sent for s/n's in a group after the first.
498 * All ignored s/n's are listed last, so when we see a 320 we clear the group and begin marking the
499 * s/n's as ignored. It is always followed by an identical 300 key.
500 */
501 if (pair->value && !strcmp(pair->value, "320")) {
502 /* No longer in any group; this indicates the start of the ignore list. */
503 g_free(yd->current_list15_grp);
504 yd->current_list15_grp = NULL;
505 }
506
507 break;
508 case 301: /* This is 319 before all s/n's in a group after the first. It is followed by an identical 300. */
509 if(temp != NULL) {
510 if(protocol == 2)
511 norm_bud = g_strconcat("msn/", temp, NULL);
512 else
513 norm_bud = g_strdup(temp);
514
515 if (yd->current_list15_grp) {
516 /* This buddy is in a group */
517 f = yahoo_friend_find_or_new(gc, norm_bud);
518 if (!(b = purple_find_buddy(account, norm_bud))) {
519 if (!(g = purple_find_group(yd->current_list15_grp))) {
520 g = purple_group_new(yd->current_list15_grp);
521 purple_blist_add_group(g, NULL);
522 }
523 b = purple_buddy_new(account, norm_bud, NULL);
524 purple_blist_add_buddy(b, NULL, g, NULL);
525 }
526 yahoo_do_group_check(account, ht, norm_bud, yd->current_list15_grp);
527 if(protocol != 0) {
528 f->protocol = protocol;
529 purple_debug_info("yahoo", "Setting protocol to %d\n", f->protocol);
530 }
531 if(stealth == 2)
532 f->presence = YAHOO_PRESENCE_PERM_OFFLINE;
533
534 /* set p2p status not connected and no p2p packet sent */
535 if(protocol == 0) {
536 yahoo_friend_set_p2p_status(f, YAHOO_P2PSTATUS_NOT_CONNECTED);
537 f->p2p_packet_sent = 0;
538 } else
539 yahoo_friend_set_p2p_status(f, YAHOO_P2PSTATUS_DO_NOT_CONNECT);
540 } else {
541 /* This buddy is on the ignore list (and therefore in no group) */
542 purple_debug_info("yahoo", "%s adding %s to the deny list because of the ignore list / no group was found\n",account->username, norm_bud);
543 purple_privacy_deny_add(account, norm_bud, 1);
544 }
545
546 protocol = 0;
547 stealth = 0;
548 norm_bud = NULL;
549 temp = NULL;
550 }
551 break;
552 case 300: /* This is 318 before a group, 319 before any s/n in a group, and 320 before any ignored s/n. */
553 break;
554 case 65: /* This is the group */
555 g_free(yd->current_list15_grp);
556 yd->current_list15_grp = yahoo_string_decode(gc, pair->value, FALSE);
557 break;
558 case 7: /* buddy's s/n */
559 temp = g_strdup(purple_normalize(account, pair->value));
560 break;
561 case 241: /* another protocol user */
562 protocol = strtol(pair->value, NULL, 10);
563 break;
564 case 59: /* somebody told cookies come here too, but im not sure */
565 yahoo_process_cookie(yd, pair->value);
566 break;
567 case 317: /* Stealth Setting */
568 stealth = strtol(pair->value, NULL, 10);
569 break;
570 /* case 242: */ /* this seems related to 241 */
571 /* break; */
572 }
573 }
574
575 g_hash_table_foreach(ht, yahoo_do_group_cleanup, NULL);
576
577 /* Now that we have processed the buddy list, we can say yahoo has connected */
578 purple_connection_set_display_name(gc, purple_normalize(account, purple_account_get_username(account)));
579 purple_connection_set_state(gc, PURPLE_CONNECTED);
580 yd->logged_in = TRUE;
581 if (yd->picture_upload_todo) {
582 yahoo_buddy_icon_upload(gc, yd->picture_upload_todo);
583 yd->picture_upload_todo = NULL;
584 }
585 yahoo_set_status(account, purple_account_get_active_status(account));
586 purple_debug_info("yahoo","Authentication: Connection established\n");
587
588 g_hash_table_destroy(ht);
589 g_free(norm_bud);
590 g_free(temp);
591 }
592
593 static void yahoo_process_list(PurpleConnection *gc, struct yahoo_packet *pkt)
594 {
595 GSList *l = pkt->hash;
596 gboolean export = FALSE;
597 gboolean got_serv_list = FALSE;
598 PurpleBuddy *b;
599 PurpleGroup *g;
600 YahooFriend *f = NULL;
601 PurpleAccount *account = purple_connection_get_account(gc);
602 struct yahoo_data *yd = gc->proto_data;
603 GHashTable *ht;
604
605 char **lines;
606 char **split;
607 char **buddies;
608 char **tmp, **bud, *norm_bud;
609 char *grp = NULL;
610
611 if (pkt->id)
612 yd->session_id = pkt->id;
613
614 while (l) {
615 struct yahoo_pair *pair = l->data;
616 l = l->next;
617
618 switch (pair->key) {
619 case 87:
620 if (!yd->tmp_serv_blist)
621 yd->tmp_serv_blist = g_string_new(pair->value);
622 else
623 g_string_append(yd->tmp_serv_blist, pair->value);
624 break;
625 case 88:
626 if (!yd->tmp_serv_ilist)
627 yd->tmp_serv_ilist = g_string_new(pair->value);
628 else
629 g_string_append(yd->tmp_serv_ilist, pair->value);
630 break;
631 case 89:
632 yd->profiles = g_strsplit(pair->value, ",", -1);
633 break;
634 case 59: /* cookies, yum */
635 yahoo_process_cookie(yd, pair->value);
636 break;
637 case YAHOO_SERVICE_PRESENCE_PERM:
638 if (!yd->tmp_serv_plist)
639 yd->tmp_serv_plist = g_string_new(pair->value);
640 else
641 g_string_append(yd->tmp_serv_plist, pair->value);
642 break;
643 }
644 }
645
646 if (pkt->status != 0)
647 return;
648
649 if (yd->tmp_serv_blist) {
650 ht = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_slist_free);
651
652 lines = g_strsplit(yd->tmp_serv_blist->str, "\n", -1);
653 for (tmp = lines; *tmp; tmp++) {
654 split = g_strsplit(*tmp, ":", 2);
655 if (!split)
656 continue;
657 if (!split[0] || !split[1]) {
658 g_strfreev(split);
659 continue;
660 }
661 grp = yahoo_string_decode(gc, split[0], FALSE);
662 buddies = g_strsplit(split[1], ",", -1);
663 for (bud = buddies; bud && *bud; bud++) {
664 norm_bud = g_strdup(purple_normalize(account, *bud));
665 f = yahoo_friend_find_or_new(gc, norm_bud);
666
667 if (!(b = purple_find_buddy(account, norm_bud))) {
668 if (!(g = purple_find_group(grp))) {
669 g = purple_group_new(grp);
670 purple_blist_add_group(g, NULL);
671 }
672 b = purple_buddy_new(account, norm_bud, NULL);
673 purple_blist_add_buddy(b, NULL, g, NULL);
674 export = TRUE;
675 }
676
677 yahoo_do_group_check(account, ht, norm_bud, grp);
678 /* set p2p status not connected and no p2p packet sent */
679 yahoo_friend_set_p2p_status(f, YAHOO_P2PSTATUS_NOT_CONNECTED);
680 f->p2p_packet_sent = 0;
681
682 g_free(norm_bud);
683 }
684 g_strfreev(buddies);
685 g_strfreev(split);
686 g_free(grp);
687 }
688 g_strfreev(lines);
689
690 g_string_free(yd->tmp_serv_blist, TRUE);
691 yd->tmp_serv_blist = NULL;
692 g_hash_table_foreach(ht, yahoo_do_group_cleanup, NULL);
693 g_hash_table_destroy(ht);
694 }
695
696 if (yd->tmp_serv_ilist) {
697 buddies = g_strsplit(yd->tmp_serv_ilist->str, ",", -1);
698 for (bud = buddies; bud && *bud; bud++) {
699 /* The server is already ignoring the user */
700 got_serv_list = TRUE;
701 purple_privacy_deny_add(account, *bud, 1);
702 }
703 g_strfreev(buddies);
704
705 g_string_free(yd->tmp_serv_ilist, TRUE);
706 yd->tmp_serv_ilist = NULL;
707 }
708
709 if (got_serv_list &&
710 ((account->perm_deny != PURPLE_PRIVACY_ALLOW_BUDDYLIST) &&
711 (account->perm_deny != PURPLE_PRIVACY_DENY_ALL) &&
712 (account->perm_deny != PURPLE_PRIVACY_ALLOW_USERS)))
713 {
714 account->perm_deny = PURPLE_PRIVACY_DENY_USERS;
715 purple_debug_info("yahoo", "%s privacy defaulting to PURPLE_PRIVACY_DENY_USERS.\n",
716 account->username);
717 }
718
719 if (yd->tmp_serv_plist) {
720 buddies = g_strsplit(yd->tmp_serv_plist->str, ",", -1);
721 for (bud = buddies; bud && *bud; bud++) {
722 f = yahoo_friend_find(gc, *bud);
723 if (f) {
724 purple_debug_info("yahoo", "%s setting presence for %s to PERM_OFFLINE\n",
725 account->username, *bud);
726 f->presence = YAHOO_PRESENCE_PERM_OFFLINE;
727 }
728 }
729 g_strfreev(buddies);
730 g_string_free(yd->tmp_serv_plist, TRUE);
731 yd->tmp_serv_plist = NULL;
732
733 }
734 /* Now that we've got the list, request aliases */
735 yahoo_fetch_aliases(gc);
736 }
737
738 /* pkt_type is YAHOO_PKT_TYPE_SERVER if pkt arrives from yahoo server, YAHOO_PKT_TYPE_P2P if pkt arrives through p2p */
739 static void yahoo_process_notify(PurpleConnection *gc, struct yahoo_packet *pkt, yahoo_pkt_type pkt_type)
740 {
741 PurpleAccount *account;
742 char *msg = NULL;
743 char *from = NULL;
744 char *stat = NULL;
745 char *game = NULL;
746 YahooFriend *f = NULL;
747 GSList *l = pkt->hash;
748 gint val_11 = 0;
749 struct yahoo_data *yd = gc->proto_data;
750 gboolean msn = FALSE;
751
752 account = purple_connection_get_account(gc);
753
754 while (l) {
755 struct yahoo_pair *pair = l->data;
756 if (pair->key == 4 || pair->key == 1)
757 from = pair->value;
758 if (pair->key == 49)
759 msg = pair->value;
760 if (pair->key == 13)
761 stat = pair->value;
762 if (pair->key == 14)
763 game = pair->value;
764 if (pair->key == 11)
765 val_11 = strtol(pair->value, NULL, 10);
766 if (pair->key == 241)
767 if(strtol(pair->value, NULL, 10) == 2)
768 msn = TRUE;
769 l = l->next;
770 }
771
772 if (!from || !msg)
773 return;
774
775 /* disconnect the peer if connected through p2p and sends wrong value for session id */
776 if( (pkt_type == YAHOO_PKT_TYPE_P2P) && (val_11 != yd->session_id) ) {
777 purple_debug_warning("yahoo","p2p: %s sent us notify with wrong session id. Disconnecting p2p connection to peer\n", from);
778 /* remove from p2p connection lists, also calls yahoo_p2p_disconnect_destroy_data */
779 g_hash_table_remove(yd->peers, from);
780 return;
781 }
782
783 if (!g_ascii_strncasecmp(msg, "TYPING", strlen("TYPING"))
784 && (purple_privacy_check(account, from)))
785 {
786 if(msn) {
787 char *msn_from = g_strconcat("msn/", from, NULL);
788 if (*stat == '1')
789 serv_got_typing(gc, msn_from, 0, PURPLE_TYPING);
790 else
791 serv_got_typing_stopped(gc, msn_from);
792 g_free(msn_from);
793 }
794 else {
795 if (*stat == '1')
796 serv_got_typing(gc, from, 0, PURPLE_TYPING);
797 else
798 serv_got_typing_stopped(gc, from);
799 }
800 } else if (!g_ascii_strncasecmp(msg, "GAME", strlen("GAME"))) {
801 PurpleBuddy *bud = purple_find_buddy(account, from);
802
803 if (!bud) {
804 purple_debug_warning("yahoo",
805 "%s is playing a game, and doesn't want you to know.\n", from);
806 }
807
808 f = yahoo_friend_find(gc, from);
809 if (!f)
810 return; /* if they're not on the list, don't bother */
811
812 yahoo_friend_set_game(f, NULL);
813
814 if (*stat == '1') {
815 yahoo_friend_set_game(f, game);
816 if (bud)
817 yahoo_update_status(gc, from, f);
818 }
819 } else if (!g_ascii_strncasecmp(msg, "WEBCAMINVITE", strlen("WEBCAMINVITE"))) {
820 PurpleConversation *conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, from, account);
821 char *buf = g_strdup_printf(_("%s has sent you a webcam invite, which is not yet supported."), from);
822 purple_conversation_write(conv, NULL, buf, PURPLE_MESSAGE_SYSTEM|PURPLE_MESSAGE_NOTIFY, time(NULL));
823 g_free(buf);
824 }
825 }
826
827
828 struct _yahoo_im {
829 char *from;
830 char *active_id;
831 int time;
832 int utf8;
833 int buddy_icon;
834 char *id;
835 char *msg;
836 gboolean msn;
837 };
838
839 static void yahoo_process_sms_message(PurpleConnection *gc, struct yahoo_packet *pkt)
840 {
841 PurpleAccount *account;
842 GSList *l = pkt->hash;
843 struct _yahoo_im *sms = NULL;
844 struct yahoo_data *yd;
845 char *server_msg = NULL;
846 char *m;
847
848 yd = gc->proto_data;
849 account = purple_connection_get_account(gc);
850
851 while (l != NULL) {
852 struct yahoo_pair *pair = l->data;
853 if (pair->key == 4) {
854 sms = g_new0(struct _yahoo_im, 1);
855 sms->from = g_strdup_printf("+%s", pair->value);
856 sms->time = time(NULL);
857 sms->utf8 = TRUE;
858 }
859 if (pair->key == 14) {
860 if (sms)
861 sms->msg = pair->value;
862 }
863 if (pair->key == 68)
864 if(sms)
865 g_hash_table_insert(yd->sms_carrier, g_strdup(sms->from), g_strdup(pair->value));
866 if (pair->key == 16)
867 server_msg = pair->value;
868 l = l->next;
869 }
870
871 if( (pkt->status == -1) || (pkt->status == YAHOO_STATUS_DISCONNECTED) ) {
872 if (server_msg) {
873 PurpleConversation *c;
874 c = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, sms->from, account);
875 if (c == NULL)
876 c = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, sms->from);
877 purple_conversation_write(c, NULL, server_msg, PURPLE_MESSAGE_SYSTEM, time(NULL));
878 }
879 else
880 purple_notify_error(gc, NULL, _("Your SMS was not delivered"), NULL);
881
882 g_free(sms->from);
883 g_free(sms);
884 return ;
885 }
886
887 if (!sms->from || !sms->msg) {
888 g_free(sms);
889 return;
890 }
891
892 m = yahoo_string_decode(gc, sms->msg, sms->utf8);
893 serv_got_im(gc, sms->from, m, 0, sms->time);
894
895 g_free(m);
896 g_free(sms->from);
897 g_free(sms);
898 }
899
900 /* pkt_type is YAHOO_PKT_TYPE_SERVER if pkt arrives from yahoo server, YAHOO_PKT_TYPE_P2P if pkt arrives through p2p */
901 static void yahoo_process_message(PurpleConnection *gc, struct yahoo_packet *pkt, yahoo_pkt_type pkt_type)
902 {
903 PurpleAccount *account;
904 struct yahoo_data *yd = gc->proto_data;
905 GSList *l = pkt->hash;
906 GSList *list = NULL;
907 struct _yahoo_im *im = NULL;
908 const char *imv = NULL;
909 gint val_11 = 0;
910
911 account = purple_connection_get_account(gc);
912
913 if (pkt->status <= 1 || pkt->status == 5 || pkt->status == YAHOO_STATUS_OFFLINE) {
914 /* messages are received with status YAHOO_STATUS_OFFLINE in case of p2p */
915 while (l != NULL) {
916 struct yahoo_pair *pair = l->data;
917 if (pair->key == 4 || pair->key == 1) {
918 im = g_new0(struct _yahoo_im, 1);
919 list = g_slist_append(list, im);
920 im->from = pair->value;
921 im->time = time(NULL);
922 im->utf8 = TRUE;
923 }
924 if (im && pair->key == 5)
925 im->active_id = pair->value;
926 if (pair->key == 97)
927 if (im)
928 im->utf8 = strtol(pair->value, NULL, 10);
929 if (pair->key == 15)
930 if (im)
931 im->time = strtol(pair->value, NULL, 10);
932 if (pair->key == 206)
933 if (im)
934 im->buddy_icon = strtol(pair->value, NULL, 10);
935 if (pair->key == 14) {
936 if (im)
937 im->msg = pair->value;
938 }
939 if (im && pair->key == 241) {
940 if(strtol(pair->value, NULL, 10) == 2)
941 im->msn = TRUE;
942 }
943 /* peer session id */
944 if (pair->key == 11) {
945 if (im)
946 val_11 = strtol(pair->value, NULL, 10);
947 }
948 /* IMV key */
949 if (pair->key == 63)
950 {
951 imv = pair->value;
952 }
953 if (pair->key == 429)
954 if (im)
955 im->id = pair->value;
956 l = l->next;
957 }
958 } else if (pkt->status == 2) {
959 purple_notify_error(gc, NULL,
960 _("Your Yahoo! message did not get sent."), NULL);
961 }
962
963 /* disconnect the peer if connected through p2p and sends wrong value for session id */
964 if( (pkt_type == YAHOO_PKT_TYPE_P2P) && (val_11 != yd->session_id) ) {
965 purple_debug_warning("yahoo","p2p: %s sent us message with wrong session id. Disconnecting p2p connection to peer\n", im->from);
966 /* remove from p2p connection lists, also calls yahoo_p2p_disconnect_destroy_data */
967 g_hash_table_remove(yd->peers, im->from);
968 return;
969 }
970
971 /** TODO: It seems that this check should be per IM, not global */
972 /* Check for the Doodle IMV */
973 if (im != NULL && imv!= NULL && im->from != NULL)
974 {
975 g_hash_table_replace(yd->imvironments, g_strdup(im->from), g_strdup(imv));
976
977 if (strstr(imv, "doodle;") != NULL)
978 {
979 PurpleWhiteboard *wb;
980
981 if (!purple_privacy_check(account, im->from)) {
982 purple_debug_info("yahoo", "Doodle request from %s dropped.\n", im->from);
983 return;
984 }
985
986 /* I'm not sure the following ever happens -DAA */
987
988 wb = purple_whiteboard_get_session(account, im->from);
989
990 /* If a Doodle session doesn't exist between this user */
991 if(wb == NULL)
992 {
993 doodle_session *ds;
994 wb = purple_whiteboard_create(account, im->from, DOODLE_STATE_REQUESTED);
995 ds = wb->proto_data;
996 ds->imv_key = g_strdup(imv);
997
998 yahoo_doodle_command_send_request(gc, im->from, imv);
999 yahoo_doodle_command_send_ready(gc, im->from, imv);
1000 }
1001 }
1002 }
1003
1004 for (l = list; l; l = l->next) {
1005 YahooFriend *f;
1006 char *m, *m2;
1007 char *msn_from = NULL;
1008 const char *from;
1009 PurpleConversation *c;
1010 im = l->data;
1011
1012 if (!im->from || !im->msg) {
1013 g_free(im);
1014 continue;
1015 }
1016
1017 if (!purple_privacy_check(account, im->from)) {
1018 purple_debug_info("yahoo", "Message from %s dropped.\n", im->from);
1019 return;
1020 }
1021
1022 /*
1023 * TODO: Is there anything else we should check when determining whether
1024 * we should send an acknowledgement?
1025 */
1026 if (im->id != NULL) {
1027 /* Send acknowledgement. If we don't do this then the official
1028 * Yahoo Messenger client for Windows will send us the same
1029 * message 7 seconds later as an offline message. This is true
1030 * for at least version 9.0.0.2162 on Windows XP. */
1031 struct yahoo_packet *pkt2;
1032 pkt2 = yahoo_packet_new(YAHOO_SERVICE_MESSAGE_ACK,
1033 YAHOO_STATUS_AVAILABLE, pkt->id);
1034 yahoo_packet_hash(pkt2, "ssisii",
1035 1, im->active_id, /* May not always be the connection's display name */
1036 5, im->from,
1037 302, 430,
1038 430, im->id,
1039 303, 430,
1040 450, 0);
1041 yahoo_packet_send_and_free(pkt2, yd);
1042 }
1043
1044 m = yahoo_string_decode(gc, im->msg, im->utf8);
1045 /* This may actually not be necessary, but it appears
1046 * that at least at one point some clients were sending
1047 * "\r\n" as line delimiters, so we want to avoid double
1048 * lines. */
1049 m2 = purple_strreplace(m, "\r\n", "\n");
1050 g_free(m);
1051 m = m2;
1052 purple_util_chrreplace(m, '\r', '\n');
1053
1054 if (im->msn) {
1055 msn_from = g_strconcat("msn/", im->from, NULL);
1056 from = msn_from;
1057 } else {
1058 from = im->from;
1059 }
1060
1061 c = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, from, account);
1062
1063 if (!strcmp(m, "<ding>")) {
1064 char *username;
1065
1066 if (c == NULL) {
1067 c = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, from);
1068 }
1069 username = g_markup_escape_text(from, -1);
1070 purple_prpl_got_attention(gc, username, YAHOO_BUZZ);
1071 g_free(username);
1072 g_free(m);
1073 g_free(im);
1074 g_free(msn_from);
1075 continue;
1076 }
1077
1078 m2 = yahoo_codes_to_html(m);
1079 g_free(m);
1080
1081 serv_got_im(gc, from, m2, 0, im->time);
1082 g_free(m2);
1083
1084 /* laters : implement buddy icon for msn friends */
1085 if (!im->msn) {
1086 if ((f = yahoo_friend_find(gc, im->from)) && im->buddy_icon == 2) {
1087 if (yahoo_friend_get_buddy_icon_need_request(f)) {
1088 yahoo_send_picture_request(gc, im->from);
1089 yahoo_friend_set_buddy_icon_need_request(f, FALSE);
1090 }
1091 }
1092 }
1093
1094 g_free(im);
1095 g_free(msn_from);
1096 }
1097 g_slist_free(list);
1098 }
1099
1100 static void yahoo_process_sysmessage(PurpleConnection *gc, struct yahoo_packet *pkt)
1101 {
1102 GSList *l = pkt->hash;
1103 char *prim, *me = NULL, *msg = NULL;
1104
1105 while (l) {
1106 struct yahoo_pair *pair = l->data;
1107
1108 if (pair->key == 5)
1109 me = pair->value;
1110 if (pair->key == 14)
1111 msg = pair->value;
1112
1113 l = l->next;
1114 }
1115
1116 if (!msg || !g_utf8_validate(msg, -1, NULL))
1117 return;
1118
1119 prim = g_strdup_printf(_("Yahoo! system message for %s:"),
1120 me?me:purple_connection_get_display_name(gc));
1121 purple_notify_info(NULL, NULL, prim, msg);
1122 g_free(prim);
1123 }
1124
1125 struct yahoo_add_request {
1126 PurpleConnection *gc;
1127 char *id;
1128 char *who;
1129 int protocol;
1130 };
1131
1132 static void
1133 yahoo_buddy_add_authorize_cb(gpointer data)
1134 {
1135 struct yahoo_add_request *add_req = data;
1136 struct yahoo_packet *pkt;
1137 struct yahoo_data *yd = add_req->gc->proto_data;
1138 const char *who = add_req->who;
1139
1140 if (add_req->protocol == 2)
1141 who += 4;
1142
1143 pkt = yahoo_packet_new(YAHOO_SERVICE_AUTH_REQ_15, YAHOO_STATUS_AVAILABLE, 0);
1144 yahoo_packet_hash(pkt, "ssiii",
1145 1, add_req->id,
1146 5, who,
1147 241, add_req->protocol,
1148 13, 1,
1149 334, 0);
1150 yahoo_packet_send_and_free(pkt, yd);
1151
1152 g_free(add_req->id);
1153 g_free(add_req->who);
1154 g_free(add_req);
1155 }
1156
1157 static void
1158 yahoo_buddy_add_deny_cb(struct yahoo_add_request *add_req, const char *msg)
1159 {
1160 struct yahoo_data *yd = add_req->gc->proto_data;
1161 struct yahoo_packet *pkt;
1162 char *encoded_msg = NULL;
1163 PurpleAccount *account = purple_connection_get_account(add_req->gc);
1164
1165 if (msg && *msg)
1166 encoded_msg = yahoo_string_encode(add_req->gc, msg, NULL);
1167
1168 pkt = yahoo_packet_new(YAHOO_SERVICE_AUTH_REQ_15,
1169 YAHOO_STATUS_AVAILABLE, 0);
1170
1171 yahoo_packet_hash(pkt, "ssiiis",
1172 1, purple_normalize(account, purple_account_get_username(account)),
1173 5, add_req->who,
1174 13, 2,
1175 334, 0,
1176 97, 1,
1177 14, encoded_msg ? encoded_msg : "");
1178
1179 yahoo_packet_send_and_free(pkt, yd);
1180
1181 g_free(encoded_msg);
1182
1183 g_free(add_req->id);
1184 g_free(add_req->who);
1185 g_free(add_req);
1186 }
1187
1188 static void
1189 yahoo_buddy_add_deny_noreason_cb(struct yahoo_add_request *add_req, const char*msg)
1190 {
1191 yahoo_buddy_add_deny_cb(add_req, NULL);
1192 }
1193
1194 static void
1195 yahoo_buddy_add_deny_reason_cb(gpointer data) {
1196 struct yahoo_add_request *add_req = data;
1197 purple_request_input(add_req->gc, NULL, _("Authorization denied message:"),
1198 NULL, _("No reason given."), TRUE, FALSE, NULL,
1199 _("OK"), G_CALLBACK(yahoo_buddy_add_deny_cb),
1200 _("Cancel"), G_CALLBACK(yahoo_buddy_add_deny_noreason_cb),
1201 purple_connection_get_account(add_req->gc), add_req->who, NULL,
1202 add_req);
1203 }
1204
1205 static void yahoo_buddy_denied_our_add(PurpleConnection *gc, const char *who, const char *reason)
1206 {
1207 char *notify_msg;
1208 struct yahoo_data *yd = gc->proto_data;
1209
1210 if (who == NULL)
1211 return;
1212
1213 if (reason != NULL) {
1214 char *msg2 = yahoo_string_decode(gc, reason, FALSE);
1215 notify_msg = g_strdup_printf(_("%s has (retroactively) denied your request to add them to your list for the following reason: %s."), who, msg2);
1216 g_free(msg2);
1217 } else
1218 notify_msg = g_strdup_printf(_("%s has (retroactively) denied your request to add them to your list."), who);
1219
1220 purple_notify_info(gc, NULL, _("Add buddy rejected"), notify_msg);
1221 g_free(notify_msg);
1222
1223 g_hash_table_remove(yd->friends, who);
1224 purple_prpl_got_user_status(purple_connection_get_account(gc), who, "offline", NULL); /* FIXME: make this set not on list status instead */
1225 /* TODO: Shouldn't we remove the buddy from our local list? */
1226 }
1227
1228 static void yahoo_buddy_auth_req_15(PurpleConnection *gc, struct yahoo_packet *pkt) {
1229 PurpleAccount *account;
1230 GSList *l = pkt->hash;
1231 const char *msg = NULL;
1232 int protocol = 0;
1233
1234 account = purple_connection_get_account(gc);
1235
1236 /* Buddy authorized/declined our addition */
1237 if (pkt->status == 1) {
1238 char *temp = NULL;
1239 char *who = NULL;
1240 int response = 0;
1241
1242 while (l) {
1243 struct yahoo_pair *pair = l->data;
1244
1245 switch (pair->key) {
1246 case 4:
1247 temp = pair->value;
1248 break;
1249 case 13:
1250 response = strtol(pair->value, NULL, 10);
1251 break;
1252 case 14:
1253 msg = pair->value;
1254 break;
1255 case 241:
1256 protocol = strtol(pair->value, NULL, 10);
1257 break;
1258 }
1259 l = l->next;
1260 }
1261
1262 if(protocol == 0)
1263 who = g_strdup(temp);
1264 else if(protocol == 2)
1265 who = g_strconcat("msn/", temp, NULL);
1266
1267 if (response == 1) /* Authorized */
1268 purple_debug_info("yahoo", "Received authorization from buddy '%s'.\n", who ? who : "(Unknown Buddy)");
1269 else if (response == 2) { /* Declined */
1270 purple_debug_info("yahoo", "Received authorization decline from buddy '%s'.\n", who ? who : "(Unknown Buddy)");
1271 yahoo_buddy_denied_our_add(gc, who, msg);
1272 } else
1273 purple_debug_error("yahoo", "Received unknown authorization response of %d from buddy '%s'.\n", response, who ? who : "(Unknown Buddy)");
1274 g_free(who);
1275 }
1276 /* Buddy requested authorization to add us. */
1277 else if (pkt->status == 3) {
1278 struct yahoo_add_request *add_req;
1279 const char *firstname = NULL, *lastname = NULL;
1280 char *temp = NULL;
1281
1282 add_req = g_new0(struct yahoo_add_request, 1);
1283 add_req->gc = gc;
1284
1285 while (l) {
1286 struct yahoo_pair *pair = l->data;
1287
1288 switch (pair->key) {
1289 case 4:
1290 temp = pair->value;
1291 add_req->who = g_strdup(pair->value);
1292 break;
1293 case 5:
1294 add_req->id = g_strdup(pair->value);
1295 break;
1296 case 14:
1297 msg = pair->value;
1298 break;
1299 case 216:
1300 firstname = pair->value;
1301 break;
1302 case 241:
1303 add_req->protocol = strtol(pair->value, NULL, 10);
1304 break;
1305 case 254:
1306 lastname = pair->value;
1307 break;
1308
1309 }
1310 l = l->next;
1311 }
1312 if(add_req->protocol == 2)
1313 add_req->who = g_strconcat("msn/", temp, NULL);
1314 else
1315 add_req->who = g_strdup(temp);
1316
1317 if (add_req->id && add_req->who) {
1318 char *alias = NULL, *dec_msg = NULL;
1319
1320 if (!purple_privacy_check(account, add_req->who))
1321 {
1322 purple_debug_misc("yahoo", "Auth. request from %s dropped and automatically denied due to privacy settings!\n",
1323 add_req->who);
1324 yahoo_buddy_add_deny_cb(add_req, NULL);
1325 return;
1326 }
1327
1328 if (msg)
1329 dec_msg = yahoo_string_decode(gc, msg, FALSE);
1330
1331 if (firstname && lastname)
1332 alias = g_strdup_printf("%s %s", firstname, lastname);
1333 else if (firstname)
1334 alias = g_strdup(firstname);
1335 else if (lastname)
1336 alias = g_strdup(lastname);
1337
1338 /* DONE! this is almost exactly the same as what MSN does,
1339 * this should probably be moved to the core.
1340 */
1341 purple_account_request_authorization(account, add_req->who, add_req->id,
1342 alias, dec_msg,
1343 purple_find_buddy(account, add_req->who) != NULL,
1344 yahoo_buddy_add_authorize_cb,
1345 yahoo_buddy_add_deny_reason_cb,
1346 add_req);
1347 g_free(alias);
1348 g_free(dec_msg);
1349 } else {
1350 g_free(add_req->id);
1351 g_free(add_req->who);
1352 g_free(add_req);
1353 }
1354 } else {
1355 purple_debug_error("yahoo", "Received authorization of unknown status (%d).\n", pkt->status);
1356 }
1357 }
1358
1359 /* I don't think this happens anymore in Version 15 */
1360 static void yahoo_buddy_added_us(PurpleConnection *gc, struct yahoo_packet *pkt) {
1361 PurpleAccount *account;
1362 struct yahoo_add_request *add_req;
1363 char *msg = NULL;
1364 GSList *l = pkt->hash;
1365
1366 account = purple_connection_get_account(gc);
1367
1368 add_req = g_new0(struct yahoo_add_request, 1);
1369 add_req->gc = gc;
1370
1371 while (l) {
1372 struct yahoo_pair *pair = l->data;
1373
1374 switch (pair->key) {
1375 case 1:
1376 add_req->id = g_strdup(pair->value);
1377 break;
1378 case 3:
1379 add_req->who = g_strdup(pair->value);
1380 break;
1381 case 15: /* time, for when they add us and we're offline */
1382 break;
1383 case 14:
1384 msg = pair->value;
1385 break;
1386 }
1387 l = l->next;
1388 }
1389
1390 if (add_req->id && add_req->who) {
1391 char *dec_msg = NULL;
1392
1393 if (!purple_privacy_check(account, add_req->who)) {
1394 purple_debug_misc("yahoo", "Auth. request from %s dropped and automatically denied due to privacy settings!\n",
1395 add_req->who);
1396 yahoo_buddy_add_deny_cb(add_req, NULL);
1397 return;
1398 }
1399
1400 if (msg)
1401 dec_msg = yahoo_string_decode(gc, msg, FALSE);
1402
1403 /* DONE! this is almost exactly the same as what MSN does,
1404 * this should probably be moved to the core.
1405 */
1406 purple_account_request_authorization(account, add_req->who, add_req->id,
1407 NULL, dec_msg,
1408 purple_find_buddy(account,add_req->who) != NULL,
1409 yahoo_buddy_add_authorize_cb,
1410 yahoo_buddy_add_deny_reason_cb, add_req);
1411 g_free(dec_msg);
1412 } else {
1413 g_free(add_req->id);
1414 g_free(add_req->who);
1415 g_free(add_req);
1416 }
1417 }
1418
1419 /* I have no idea if this every gets called in version 15 */
1420 static void yahoo_buddy_denied_our_add_old(PurpleConnection *gc, struct yahoo_packet *pkt)
1421 {
1422 char *who = NULL;
1423 char *msg = NULL;
1424 GSList *l = pkt->hash;
1425
1426 while (l) {
1427 struct yahoo_pair *pair = l->data;
1428
1429 switch (pair->key) {
1430 case 3:
1431 who = pair->value;
1432 break;
1433 case 14:
1434 msg = pair->value;
1435 break;
1436 }
1437 l = l->next;
1438 }
1439
1440 yahoo_buddy_denied_our_add(gc, who, msg);
1441 }
1442
1443 static void yahoo_process_contact(PurpleConnection *gc, struct yahoo_packet *pkt)
1444 {
1445 switch (pkt->status) {
1446 case 1:
1447 yahoo_process_status(gc, pkt);
1448 return;
1449 case 3:
1450 yahoo_buddy_added_us(gc, pkt);
1451 break;
1452 case 7:
1453 yahoo_buddy_denied_our_add_old(gc, pkt);
1454 break;
1455 default:
1456 break;
1457 }
1458 }
1459
1460 #define OUT_CHARSET "utf-8"
1461
1462 static char *yahoo_decode(const char *text)
1463 {
1464 char *converted = NULL;
1465 char *n, *new;
1466 const char *end, *p;
1467 int i, k;
1468
1469 n = new = g_malloc(strlen (text) + 1);
1470 end = text + strlen(text);
1471
1472 for (p = text; p < end; p++, n++) {
1473 if (*p == '\\') {
1474 if (p[1] >= '0' && p[1] <= '7') {
1475 p += 1;
1476 for (i = 0, k = 0; k < 3; k += 1) {
1477 char c = p[k];
1478 if (c < '0' || c > '7') break;
1479 i *= 8;
1480 i += c - '0';
1481 }
1482 *n = i;
1483 p += k - 1;
1484 } else { /* bug 959248 */
1485 /* If we see a \ not followed by an octal number,
1486 * it means that it is actually a \\ with one \
1487 * already eaten by some unknown function.
1488 * This is arguably broken.
1489 *
1490 * I think wing is wrong here, there is no function
1491 * called that I see that could have done it. I guess
1492 * it is just really sending single \'s. That's yahoo
1493 * for you.
1494 */
1495 *n = *p;
1496 }
1497 }
1498 else
1499 *n = *p;
1500 }
1501
1502 *n = '\0';
1503
1504 if (strstr(text, "\033$B"))
1505 converted = g_convert(new, n - new, OUT_CHARSET, "iso-2022-jp", NULL, NULL, NULL);
1506 if (!converted)
1507 converted = g_convert(new, n - new, OUT_CHARSET, "iso-8859-1", NULL, NULL, NULL);
1508 g_free(new);
1509
1510 return converted;
1511 }
1512
1513 static void yahoo_process_mail(PurpleConnection *gc, struct yahoo_packet *pkt)
1514 {
1515 PurpleAccount *account = purple_connection_get_account(gc);
1516 struct yahoo_data *yd = gc->proto_data;
1517 const char *who = NULL;
1518 const char *email = NULL;
1519 const char *subj = NULL;
1520 const char *yahoo_mail_url = (yd->jp? YAHOOJP_MAIL_URL: YAHOO_MAIL_URL);
1521 int count = 0;
1522 GSList *l = pkt->hash;
1523
1524 if (!purple_account_get_check_mail(account))
1525 return;
1526
1527 while (l) {
1528 struct yahoo_pair *pair = l->data;
1529 if (pair->key == 9)
1530 count = strtol(pair->value, NULL, 10);
1531 else if (pair->key == 43)
1532 who = pair->value;
1533 else if (pair->key == 42)
1534 email = pair->value;
1535 else if (pair->key == 18)
1536 subj = pair->value;
1537 l = l->next;
1538 }
1539
1540 if (who && subj && email && *email) {
1541 char *dec_who = yahoo_decode(who);
1542 char *dec_subj = yahoo_decode(subj);
1543 char *from = g_strdup_printf("%s (%s)", dec_who, email);
1544
1545 purple_notify_email(gc, dec_subj, from, purple_account_get_username(account),
1546 yahoo_mail_url, NULL, NULL);
1547
1548 g_free(dec_who);
1549 g_free(dec_subj);
1550 g_free(from);
1551 } else if (count > 0) {
1552 const char *tos[2] = { purple_account_get_username(account) };
1553 const char *urls[2] = { yahoo_mail_url };
1554
1555 purple_notify_emails(gc, count, FALSE, NULL, NULL, tos, urls,
1556 NULL, NULL);
1557 }
1558 }
1559
1560 /* We use this structure once while we authenticate */
1561 struct yahoo_auth_data
1562 {
1563 PurpleConnection *gc;
1564 char *seed;
1565 };
1566
1567 /* This is the y64 alphabet... it's like base64, but has a . and a _ */
1568 static const char base64digits[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789._";
1569
1570 /* This is taken from Sylpheed by Hiroyuki Yamamoto. We have our own tobase64 function
1571 * in util.c, but it is different from the one yahoo uses */
1572 static void to_y64(char *out, const unsigned char *in, gsize inlen)
1573 /* raw bytes in quasi-big-endian order to base 64 string (NUL-terminated) */
1574 {
1575 for (; inlen >= 3; inlen -= 3)
1576 {
1577 *out++ = base64digits[in[0] >> 2];
1578 *out++ = base64digits[((in[0] << 4) & 0x30) | (in[1] >> 4)];
1579 *out++ = base64digits[((in[1] << 2) & 0x3c) | (in[2] >> 6)];
1580 *out++ = base64digits[in[2] & 0x3f];
1581 in += 3;
1582 }
1583 if (inlen > 0)
1584 {
1585 unsigned char fragment;
1586
1587 *out++ = base64digits[in[0] >> 2];
1588 fragment = (in[0] << 4) & 0x30;
1589 if (inlen > 1)
1590 fragment |= in[1] >> 4;
1591 *out++ = base64digits[fragment];
1592 *out++ = (inlen < 2) ? '-' : base64digits[(in[1] << 2) & 0x3c];
1593 *out++ = '-';
1594 }
1595 *out = '\0';
1596 }
1597
1598 static void yahoo_auth16_stage3(PurpleConnection *gc, const char *crypt)
1599 {
1600 struct yahoo_data *yd = gc->proto_data;
1601 PurpleAccount *account = purple_connection_get_account(gc);
1602 const char *name = purple_normalize(account, purple_account_get_username(account));
1603 PurpleCipher *md5_cipher;
1604 PurpleCipherContext *md5_ctx;
1605 guchar md5_digest[16];
1606 gchar base64_string[25];
1607 struct yahoo_packet *pkt;
1608
1609 purple_debug_info("yahoo","Authentication: In yahoo_auth16_stage3\n");
1610
1611 md5_cipher = purple_ciphers_find_cipher("md5");
1612 md5_ctx = purple_cipher_context_new(md5_cipher, NULL);
1613 purple_cipher_context_append(md5_ctx, (guchar *)crypt, strlen(crypt));
1614 purple_cipher_context_digest(md5_ctx, sizeof(md5_digest), md5_digest, NULL);
1615
1616 to_y64(base64_string, md5_digest, 16);
1617
1618 purple_debug_info("yahoo", "yahoo status: %d\n", yd->current_status);
1619 pkt = yahoo_packet_new(YAHOO_SERVICE_AUTHRESP, yd->current_status, yd->session_id);
1620 if(yd->jp) {
1621 yahoo_packet_hash(pkt, "ssssssss",
1622 1, name,
1623 0, name,
1624 277, yd->cookie_y,
1625 278, yd->cookie_t,
1626 307, base64_string,
1627 2, name,
1628 2, "1",
1629 135, YAHOOJP_CLIENT_VERSION);
1630 } else {
1631 yahoo_packet_hash(pkt, "sssssssss",
1632 1, name,
1633 0, name,
1634 277, yd->cookie_y,
1635 278, yd->cookie_t,
1636 307, base64_string,
1637 244, YAHOO_CLIENT_VERSION_ID,
1638 2, name,
1639 2, "1",
1640 135, YAHOO_CLIENT_VERSION);
1641 }
1642 if (yd->picture_checksum)
1643 yahoo_packet_hash_int(pkt, 192, yd->picture_checksum);
1644 yahoo_packet_send_and_free(pkt, yd);
1645
1646 purple_cipher_context_destroy(md5_ctx);
1647 }
1648
1649 static void yahoo_auth16_stage2(PurpleUtilFetchUrlData *unused, gpointer user_data, const gchar *ret_data, size_t len, const gchar *error_message)
1650 {
1651 struct yahoo_auth_data *auth_data = user_data;
1652 PurpleConnection *gc = auth_data->gc;
1653 struct yahoo_data *yd;
1654 gboolean try_login_on_error = FALSE;
1655
1656 purple_debug_info("yahoo","Authentication: In yahoo_auth16_stage2\n");
1657
1658 if (!PURPLE_CONNECTION_IS_VALID(gc)) {
1659 g_free(auth_data->seed);
1660 g_free(auth_data);
1661 g_return_if_reached();
1662 }
1663
1664 yd = (struct yahoo_data *)gc->proto_data;
1665
1666 if (error_message != NULL) {
1667 purple_debug_error("yahoo", "Login Failed, unable to retrieve stage 2 url: %s\n", error_message);
1668 purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, error_message);
1669 g_free(auth_data->seed);
1670 g_free(auth_data);
1671 return;
1672 }
1673 else if (len > 0 && ret_data && *ret_data) {
1674 gchar **split_data = g_strsplit(ret_data, "\r\n", -1);
1675 int totalelements = 0;
1676 int response_no = -1;
1677 char *crumb = NULL;
1678 char *crypt = NULL;
1679
1680 #if GLIB_CHECK_VERSION(2,6,0)
1681 totalelements = g_strv_length(split_data);
1682 #else
1683 while (split_data[++totalelements] != NULL);
1684 #endif
1685 if (totalelements >= 4) {
1686 response_no = strtol(split_data[0], NULL, 10);
1687 crumb = g_strdup(split_data[1] + strlen("crumb="));
1688 yd->cookie_y = g_strdup(split_data[2] + strlen("Y="));
1689 yd->cookie_t = g_strdup(split_data[3] + strlen("T="));
1690 }
1691
1692 g_strfreev(split_data);
1693
1694 if(response_no != 0) {
1695 /* Some error in the login process */
1696 PurpleConnectionError error;
1697 char *error_reason = NULL;
1698
1699 switch(response_no) {
1700 case -1:
1701 /* Some error in the received stream */
1702 error_reason = g_strdup(_("Received invalid data"));
1703 error = PURPLE_CONNECTION_ERROR_NETWORK_ERROR;
1704 break;
1705 case 100:
1706 /* Unknown error */
1707 error_reason = g_strdup(_("Unknown error"));
1708 error = PURPLE_CONNECTION_ERROR_OTHER_ERROR;
1709 break;
1710 default:
1711 /* if we have everything we need, why not try to login irrespective of response */
1712 if((crumb != NULL) && (yd->cookie_y != NULL) && (yd->cookie_t != NULL)) {
1713 try_login_on_error = TRUE;
1714 break;
1715 }
1716 error_reason = g_strdup(_("Unknown error"));
1717 error = PURPLE_CONNECTION_ERROR_OTHER_ERROR;
1718 break;
1719 }
1720 if(error_reason) {
1721 purple_debug_error("yahoo", "Authentication error: %s\n",
1722 error_reason);
1723 purple_connection_error_reason(gc, error, error_reason);
1724 g_free(error_reason);
1725 g_free(auth_data->seed);
1726 g_free(auth_data);
1727 return;
1728 }
1729 }
1730
1731 crypt = g_strconcat(crumb, auth_data->seed, NULL);
1732 yahoo_auth16_stage3(gc, crypt);
1733 g_free(crypt);
1734 g_free(crumb);
1735 }
1736 g_free(auth_data->seed);
1737 g_free(auth_data);
1738 }
1739
1740 static void yahoo_auth16_stage1_cb(PurpleUtilFetchUrlData *unused, gpointer user_data, const gchar *ret_data, size_t len, const gchar *error_message)
1741 {
1742 struct yahoo_auth_data *auth_data = user_data;
1743 PurpleConnection *gc = auth_data->gc;
1744
1745 purple_debug_info("yahoo","Authentication: In yahoo_auth16_stage1_cb\n");
1746
1747 if (!PURPLE_CONNECTION_IS_VALID(gc)) {
1748 g_free(auth_data->seed);
1749 g_free(auth_data);
1750 g_return_if_reached();
1751 }
1752
1753 if (error_message != NULL) {
1754 purple_debug_error("yahoo", "Login Failed, unable to retrieve login url: %s\n", error_message);
1755 purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, error_message);
1756 g_free(auth_data->seed);
1757 g_free(auth_data);
1758 return;
1759 }
1760 else if (len > 0 && ret_data && *ret_data) {
1761 gchar **split_data = g_strsplit(ret_data, "\r\n", -1);
1762 int totalelements = 0;
1763 int response_no = -1;
1764 char *token = NULL;
1765
1766 #if GLIB_CHECK_VERSION(2,6,0)
1767 totalelements = g_strv_length(split_data);
1768 #else
1769 while (split_data[++totalelements] != NULL);
1770 #endif
1771 if(totalelements == 1)
1772 response_no = strtol(split_data[0], NULL, 10);
1773 else if(totalelements >= 2) {
1774 response_no = strtol(split_data[0], NULL, 10);
1775 token = g_strdup(split_data[1] + strlen("ymsgr="));
1776 }
1777
1778 g_strfreev(split_data);
1779
1780 if(response_no != 0) {
1781 /* Some error in the login process */
1782 PurpleConnectionError error;
1783 char *error_reason;
1784
1785 switch(response_no) {
1786 case -1:
1787 /* Some error in the received stream */
1788 error_reason = g_strdup(_("Received invalid data"));
1789 error = PURPLE_CONNECTION_ERROR_NETWORK_ERROR;
1790 break;
1791 case 1212:
1792 /* Password incorrect */
1793 /* Set password to NULL. Avoids account locking. Brings dialog to enter password if clicked on Re-enable account */
1794 if (!purple_account_get_remember_password(purple_connection_get_account(gc)))
1795 purple_account_set_password(purple_connection_get_account(gc), NULL);
1796 error_reason = g_strdup(_("Incorrect Password"));
1797 error = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED;
1798 break;
1799 case 1213:
1800 /* security lock from too many failed login attempts */
1801 error_reason = g_strdup(_("Account locked: Too many failed login attempts.\nLogging into the Yahoo! website may fix this."));
1802 error = PURPLE_CONNECTION_ERROR_OTHER_ERROR;
1803 break;
1804 case 1235:
1805 /* the username does not exist */
1806 error_reason = g_strdup(_("Username does not exist"));
1807 error = PURPLE_CONNECTION_ERROR_INVALID_USERNAME;
1808 break;
1809 case 1214:
1810 case 1236:
1811 /* indicates a lock of some description */
1812 error_reason = g_strdup(_("Account locked: Unknown reason.\nLogging into the Yahoo! website may fix this."));
1813 error = PURPLE_CONNECTION_ERROR_OTHER_ERROR;
1814 break;
1815 case 100:
1816 /* username or password missing */
1817 error_reason = g_strdup(_("Username or password missing"));
1818 error = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED;
1819 break;
1820 default:
1821 /* Unknown error! */
1822 error_reason = g_strdup(_("Unknown error"));
1823 error = PURPLE_CONNECTION_ERROR_OTHER_ERROR;
1824 break;
1825 }
1826 purple_debug_error("yahoo", "Authentication error: %s\n",
1827 error_reason);
1828 purple_connection_error_reason(gc, error, error_reason);
1829 g_free(error_reason);
1830 g_free(auth_data->seed);
1831 g_free(auth_data);
1832 }
1833 else {
1834 /* OK to login, correct information provided */
1835 PurpleUtilFetchUrlData *url_data = NULL;
1836 char *url = NULL;
1837 gboolean yahoojp = purple_account_get_bool(purple_connection_get_account(gc),
1838 "yahoojp", 0);
1839
1840 url = g_strdup_printf(yahoojp ? YAHOOJP_LOGIN_URL : YAHOO_LOGIN_URL, token);
1841 url_data = purple_util_fetch_url_request_len_with_account(
1842 purple_connection_get_account(gc), url, TRUE,
1843 YAHOO_CLIENT_USERAGENT, TRUE, NULL, FALSE, -1,
1844 yahoo_auth16_stage2, auth_data);
1845 g_free(url);
1846 g_free(token);
1847 }
1848 }
1849 }
1850
1851 static void yahoo_auth16_stage1(PurpleConnection *gc, const char *seed)
1852 {
1853 PurpleUtilFetchUrlData *url_data = NULL;
1854 struct yahoo_auth_data *auth_data = NULL;
1855 char *url = NULL;
1856 char *encoded_username;
1857 char *encoded_password;
1858 gboolean yahoojp;
1859
1860 purple_debug_info("yahoo", "Authentication: In yahoo_auth16_stage1\n");
1861
1862 if(!purple_ssl_is_supported()) {
1863 purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NO_SSL_SUPPORT, _("SSL support unavailable"));
1864 return;
1865 }
1866
1867 yahoojp = purple_account_get_bool(purple_connection_get_account(gc),
1868 "yahoojp", 0);
1869 auth_data = g_new0(struct yahoo_auth_data, 1);
1870 auth_data->gc = gc;
1871 auth_data->seed = g_strdup(seed);
1872
1873 encoded_username = g_strdup(purple_url_encode(purple_account_get_username(purple_connection_get_account(gc))));
1874 encoded_password = g_strdup(purple_url_encode(purple_connection_get_password(gc)));
1875 url = g_strdup_printf(yahoojp ? YAHOOJP_TOKEN_URL : YAHOO_TOKEN_URL,
1876 encoded_username, encoded_password, purple_url_encode(seed));
1877 g_free(encoded_password);
1878 g_free(encoded_username);
1879
1880 url_data = purple_util_fetch_url_request_len_with_account(
1881 purple_connection_get_account(gc), url, TRUE,
1882 YAHOO_CLIENT_USERAGENT, TRUE, NULL, FALSE, -1,
1883 yahoo_auth16_stage1_cb, auth_data);
1884
1885 g_free(url);
1886 }
1887
1888 static void yahoo_process_auth(PurpleConnection *gc, struct yahoo_packet *pkt)
1889 {
1890 char *seed = NULL;
1891 char *sn = NULL;
1892 GSList *l = pkt->hash;
1893 int m = 0;
1894 gchar *buf;
1895
1896 while (l) {
1897 struct yahoo_pair *pair = l->data;
1898 if (pair->key == 94)
1899 seed = pair->value;
1900 if (pair->key == 1)
1901 sn = pair->value;
1902 if (pair->key == 13)
1903 m = atoi(pair->value);
1904 l = l->next;
1905 }
1906
1907 if (seed) {
1908 switch (m) {
1909 case 0:
1910 /* used to be for really old auth routine, dont support now */
1911 case 1:
1912 case 2: /* Yahoo ver 16 authentication */
1913 yahoo_auth16_stage1(gc, seed);
1914 break;
1915 default:
1916 {
1917 GHashTable *ui_info = purple_core_get_ui_info();
1918
1919 buf = g_strdup_printf(_("The Yahoo server has requested the use of an unrecognized "
1920 "authentication method. You will probably not be able "
1921 "to successfully sign on to Yahoo. Check %s for updates."),
1922 ((ui_info && g_hash_table_lookup(ui_info, "website")) ? (char *)g_hash_table_lookup(ui_info, "website") : PURPLE_WEBSITE));
1923 purple_notify_error(gc, "", _("Failed Yahoo! Authentication"),
1924 buf);
1925 g_free(buf);
1926 yahoo_auth16_stage1(gc, seed); /* Can't hurt to try it anyway. */
1927 break;
1928 }
1929 }
1930 }
1931 }
1932
1933 static void ignore_buddy(PurpleBuddy *buddy) {
1934 PurpleGroup *group;
1935 PurpleAccount *account;
1936 gchar *name;
1937
1938 if (!buddy)
1939 return;
1940
1941 group = purple_buddy_get_group(buddy);
1942 name = g_strdup(purple_buddy_get_name(buddy));
1943 account = purple_buddy_get_account(buddy);
1944
1945 purple_debug_info("yahoo", "blist: Removing '%s' from buddy list.\n", name);
1946 purple_account_remove_buddy(account, buddy, group);
1947 purple_blist_remove_buddy(buddy);
1948
1949 serv_add_deny(purple_account_get_connection(account), name);
1950
1951 g_free(name);
1952 }
1953
1954 static void keep_buddy(PurpleBuddy *b)
1955 {
1956 purple_privacy_deny_remove(purple_buddy_get_account(b),
1957 purple_buddy_get_name(b), 1);
1958 }
1959
1960 static void yahoo_process_ignore(PurpleConnection *gc, struct yahoo_packet *pkt) {
1961 PurpleBuddy *b;
1962 GSList *l;
1963 gchar *who = NULL;
1964 gchar *me = NULL;
1965 gchar buf[BUF_LONG];
1966 gboolean ignore = TRUE;
1967 gint status = 0;
1968
1969 for (l = pkt->hash; l; l = l->next) {
1970 struct yahoo_pair *pair = l->data;
1971 switch (pair->key) {
1972 case 0:
1973 who = pair->value;
1974 break;
1975 case 1:
1976 me = pair->value;
1977 break;
1978 case 13:
1979 /* 1 == ignore, 2 == unignore */
1980 ignore = (strtol(pair->value, NULL, 10) == 1);
1981 break;
1982 case 66:
1983 status = strtol(pair->value, NULL, 10);
1984 break;
1985 default:
1986 break;
1987 }
1988 }
1989
1990 /*
1991 * status
1992 * 0 - ok
1993 * 2 - already in ignore list, could not add
1994 * 3 - not in ignore list, could not delete
1995 * 12 - is a buddy, could not add (and possibly also a not-in-ignore list condition?)
1996 */
1997 switch (status) {
1998 case 12:
1999 purple_debug_info("yahoo", "Server reported \"is a buddy\" for %s while %s",
2000 who, (ignore ? "ignoring" : "unignoring"));
2001
2002 if (ignore) {
2003 b = purple_find_buddy(gc->account, who);
2004 g_snprintf(buf, sizeof(buf), _("You have tried to ignore %s, but the "
2005 "user is on your buddy list. Clicking \"Yes\" "
2006 "will remove and ignore the buddy."), who);
2007 purple_request_yes_no(gc, NULL, _("Ignore buddy?"), buf, 0,
2008 gc->account, who, NULL,
2009 b,
2010 G_CALLBACK(ignore_buddy),
2011 G_CALLBACK(keep_buddy));
2012 break;
2013 }
2014 case 2:
2015 purple_debug_info("yahoo", "Server reported that %s is already in the ignore list.",
2016 who);
2017 break;
2018 case 3:
2019 purple_debug_info("yahoo", "Server reported that %s is not in the ignore list; could not delete",
2020 who);
2021 case 0:
2022 default:
2023 break;
2024 }
2025 }
2026
2027 static void yahoo_process_authresp(PurpleConnection *gc, struct yahoo_packet *pkt)
2028 {
2029 #ifdef TRY_WEBMESSENGER_LOGIN
2030 struct yahoo_data *yd = gc->proto_data;
2031 #endif /* TRY_WEBMESSENGER_LOGIN */
2032 GSList *l = pkt->hash;
2033 int err = 0;
2034 char *msg;
2035 char *url = NULL;
2036 char *fullmsg;
2037 PurpleAccount *account = gc->account;
2038 PurpleConnectionError reason = PURPLE_CONNECTION_ERROR_OTHER_ERROR;
2039
2040 while (l) {
2041 struct yahoo_pair *pair = l->data;
2042
2043 if (pair->key == 66)
2044 err = strtol(pair->value, NULL, 10);
2045 else if (pair->key == 20)
2046 url = pair->value;
2047
2048 l = l->next;
2049 }
2050
2051 switch (err) {
2052 case 0:
2053 msg = g_strdup(_("Unknown error."));
2054 reason = PURPLE_CONNECTION_ERROR_NETWORK_ERROR;
2055 break;
2056 case 3:
2057 msg = g_strdup(_("Invalid username."));
2058 reason = PURPLE_CONNECTION_ERROR_INVALID_USERNAME;
2059 break;
2060 case 13:
2061 #ifdef TRY_WEBMESSENGER_LOGIN
2062 if (!yd->wm) {
2063 PurpleUtilFetchUrlData *url_data;
2064 yd->wm = TRUE;
2065 if (yd->fd >= 0)
2066 close(yd->fd);
2067 if (gc->inpa)
2068 purple_input_remove(gc->inpa);
2069 url_data = purple_util_fetch_url(WEBMESSENGER_URL, TRUE,
2070 "Purple/" VERSION, FALSE, yahoo_login_page_cb, gc);
2071 if (url_data != NULL)
2072 yd->url_datas = g_slist_prepend(yd->url_datas, url_data);
2073 return;
2074 }
2075 #endif /* TRY_WEBMESSENGER_LOGIN */
2076 if (!purple_account_get_remember_password(account))
2077 purple_account_set_password(account, NULL);
2078
2079 msg = g_strdup(_("Incorrect password."));
2080 reason = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED;
2081 break;
2082 case 14:
2083 msg = g_strdup(_("Your account is locked, please log in to the Yahoo! website."));
2084 reason = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED;
2085 break;
2086 default:
2087 msg = g_strdup_printf(_("Unknown error number %d. Logging into the Yahoo! website may fix this."), err);
2088 }
2089
2090 if (url)
2091 fullmsg = g_strdup_printf("%s\n%s", msg, url);
2092 else
2093 fullmsg = g_strdup(msg);
2094
2095 purple_connection_error_reason(gc, reason, fullmsg);
2096 g_free(msg);
2097 g_free(fullmsg);
2098 }
2099
2100 static void yahoo_process_addbuddy(PurpleConnection *gc, struct yahoo_packet *pkt)
2101 {
2102 int err = 0;
2103 char *who = NULL;
2104 char *temp = NULL;
2105 char *group = NULL;
2106 char *decoded_group;
2107 char *buf;
2108 YahooFriend *f;
2109 GSList *l = pkt->hash;
2110 struct yahoo_data *yd = gc->proto_data;
2111 int protocol = 0;
2112 gboolean msn = FALSE;
2113
2114 while (l) {
2115 struct yahoo_pair *pair = l->data;
2116
2117 switch (pair->key) {
2118 case 66:
2119 err = strtol(pair->value, NULL, 10);
2120 break;
2121 case 7:
2122 temp = pair->value;
2123 break;
2124 case 65:
2125 group = pair->value;
2126 break;
2127 case 241:
2128 protocol = strtol(pair->value, NULL, 10);
2129 if(protocol == 2)
2130 msn = TRUE;
2131 break;
2132 }
2133
2134 l = l->next;
2135 }
2136
2137 if (!temp)
2138 return;
2139 if (!group)
2140 group = "";
2141
2142 if(msn)
2143 who = g_strconcat("msn/", temp, NULL);
2144 else
2145 who = g_strdup(temp);
2146
2147 if (!err || (err == 2)) { /* 0 = ok, 2 = already on serv list */
2148 f = yahoo_friend_find_or_new(gc, who);
2149 yahoo_update_status(gc, who, f);
2150 if(protocol)
2151 f->protocol = protocol;
2152
2153 if( !g_hash_table_lookup(yd->peers, who) ) {
2154 /* we are not connected as client, so set friend to not connected */
2155 if(msn)
2156 yahoo_friend_set_p2p_status(f, YAHOO_P2PSTATUS_DO_NOT_CONNECT);
2157 else {
2158 yahoo_friend_set_p2p_status(f, YAHOO_P2PSTATUS_NOT_CONNECTED);
2159 f->p2p_packet_sent = 0;
2160 }
2161 }
2162 else /* we are already connected. set friend to YAHOO_P2PSTATUS_WE_ARE_CLIENT */
2163 yahoo_friend_set_p2p_status(f, YAHOO_P2PSTATUS_WE_ARE_CLIENT);
2164 g_free(who);
2165 return;
2166 }
2167
2168 decoded_group = yahoo_string_decode(gc, group, FALSE);
2169 buf = g_strdup_printf(_("Could not add buddy %s to group %s to the server list on account %s."),
2170 who, decoded_group, purple_connection_get_display_name(gc));
2171 if (!purple_conv_present_error(who, purple_connection_get_account(gc), buf))
2172 purple_notify_error(gc, NULL, _("Could not add buddy to server list"), buf);
2173 g_free(buf);
2174 g_free(decoded_group);
2175 g_free(who);
2176 }
2177
2178 /* write pkt to the source */
2179 static void yahoo_p2p_write_pkt(gint source, struct yahoo_packet *pkt)
2180 {
2181 size_t pkt_len;
2182 guchar *raw_packet;
2183
2184 /*build the raw packet and send it to the host*/
2185 pkt_len = yahoo_packet_build(pkt, 0, 0, 0, &raw_packet);
2186 if(write(source, raw_packet, pkt_len) != pkt_len)
2187 purple_debug_warning("yahoo","p2p: couldn't write to the source\n");
2188 g_free(raw_packet);
2189 }
2190
2191 static void yahoo_p2p_keepalive_cb(gpointer key, gpointer value, gpointer user_data)
2192 {
2193 struct yahoo_p2p_data *p2p_data = value;
2194 PurpleConnection *gc = user_data;
2195 struct yahoo_packet *pkt_to_send;
2196 PurpleAccount *account;
2197 struct yahoo_data *yd = gc->proto_data;
2198
2199 account = purple_connection_get_account(gc);
2200
2201 pkt_to_send = yahoo_packet_new(YAHOO_SERVICE_P2PFILEXFER, YAHOO_STATUS_AVAILABLE, yd->session_id);
2202 yahoo_packet_hash(pkt_to_send, "ssisi",
2203 4, purple_normalize(account, purple_account_get_username(account)),
2204 5, p2p_data->host_username,
2205 241, 0, /* Protocol identifier */
2206 49, "PEERTOPEER",
2207 13, 7);
2208 yahoo_p2p_write_pkt(p2p_data->source, pkt_to_send);
2209
2210 yahoo_packet_free(pkt_to_send);
2211 }
2212
2213 static gboolean yahoo_p2p_keepalive(gpointer data)
2214 {
2215 PurpleConnection *gc = data;
2216 struct yahoo_data *yd = gc->proto_data;
2217
2218 g_hash_table_foreach(yd->peers, yahoo_p2p_keepalive_cb, gc);
2219
2220 return TRUE;
2221 }
2222
2223 /* destroy p2p_data associated with a peer and close p2p connection.
2224 * g_hash_table_remove() calls this function to destroy p2p_data associated with the peer,
2225 * call g_hash_table_remove() instead of this fucntion if peer has an entry in the table */
2226 static void yahoo_p2p_disconnect_destroy_data(gpointer data)
2227 {
2228 struct yahoo_p2p_data *p2p_data;
2229 YahooFriend *f;
2230
2231 if(!(p2p_data = data))
2232 return ;
2233
2234 /* If friend, set him not connected */
2235 f = yahoo_friend_find(p2p_data->gc, p2p_data->host_username);
2236 if (f)
2237 yahoo_friend_set_p2p_status(f, YAHOO_P2PSTATUS_NOT_CONNECTED);
2238
2239 if(p2p_data->source >= 0)
2240 close(p2p_data->source);
2241 purple_input_remove(p2p_data->input_event);
2242 g_free(p2p_data->host_ip);
2243 g_free(p2p_data->host_username);
2244 g_free(p2p_data);
2245 }
2246
2247 /* exchange of initial p2pfilexfer packets, service type YAHOO_SERVICE_P2PFILEXFER */
2248 static void yahoo_p2p_process_p2pfilexfer(gpointer data, gint source, struct yahoo_packet *pkt)
2249 {
2250 struct yahoo_p2p_data *p2p_data;
2251 char *who = NULL;
2252 GSList *l = pkt->hash;
2253 struct yahoo_packet *pkt_to_send;
2254 PurpleAccount *account;
2255 int val_13_to_send = 0;
2256 struct yahoo_data *yd;
2257 YahooFriend *f;
2258
2259 if(!(p2p_data = data))
2260 return ;
2261
2262 yd = p2p_data->gc->proto_data;
2263
2264 /* lets see whats in the packet */
2265 while (l) {
2266 struct yahoo_pair *pair = l->data;
2267
2268 switch (pair->key) {
2269 case 4:
2270 who = pair->value;
2271 if(strncmp(who, p2p_data->host_username, strlen(p2p_data->host_username)) != 0) {
2272 /* from whom are we receiving the packets ?? */
2273 purple_debug_warning("yahoo","p2p: received data from wrong user\n");
2274 return;
2275 }
2276 break;
2277 case 13:
2278 p2p_data->val_13 = strtol(pair->value, NULL, 10); /* Value should be 5-7 */
2279 break;
2280 /* case 5, 49 look laters, no use right now */
2281 }
2282 l = l->next;
2283 }
2284
2285 account = purple_connection_get_account(p2p_data->gc);
2286
2287 /* key_13: sort of a counter.
2288 * WHEN WE ARE CLIENT: yahoo server sends val_13 = 0, we send to peer val_13 = 1, receive back val_13 = 5,
2289 * we send val_13=6, receive val_13=7, we send val_13=7, HALT. Keep sending val_13 = 7 as keep alive.
2290 * WHEN WE ARE SERVER: we send val_13 = 0 to yahoo server, peer sends us val_13 = 1, we send val_13 = 5,
2291 * receive val_13 = 6, send val_13 = 7, receive val_13 = 7. HALT. Keep sending val_13 = 7 as keep alive. */
2292
2293 switch(p2p_data->val_13) {
2294 case 1 : val_13_to_send = 5; break;
2295 case 5 : val_13_to_send = 6; break;
2296 case 6 : val_13_to_send = 7; break;
2297 case 7 : if( g_hash_table_lookup(yd->peers, p2p_data->host_username) )
2298 return;
2299 val_13_to_send = 7; break;
2300 default: purple_debug_warning("yahoo","p2p:Unknown value for key 13\n");
2301 return;
2302 }
2303
2304 /* Build the yahoo packet */
2305 pkt_to_send = yahoo_packet_new(YAHOO_SERVICE_P2PFILEXFER, YAHOO_STATUS_AVAILABLE, yd->session_id);
2306 yahoo_packet_hash(pkt_to_send, "ssisi",
2307 4, purple_normalize(account, purple_account_get_username(account)),
2308 5, p2p_data->host_username,
2309 241, 0, /* Protocol identifier */
2310 49, "PEERTOPEER",
2311 13, val_13_to_send);
2312
2313 /* build the raw packet and send it to the host */
2314 yahoo_p2p_write_pkt(source, pkt_to_send);
2315 yahoo_packet_free(pkt_to_send);
2316
2317 if( val_13_to_send == 7 )
2318 if( !g_hash_table_lookup(yd->peers, p2p_data->host_username) ) {
2319 g_hash_table_insert(yd->peers, g_strdup(p2p_data->host_username), p2p_data);
2320 /* If the peer is a friend, set him connected */
2321 f = yahoo_friend_find(p2p_data->gc, p2p_data->host_username);
2322 if (f) {
2323 if(p2p_data->connection_type == YAHOO_P2P_WE_ARE_SERVER) {
2324 p2p_data->session_id = f->session_id;
2325 yahoo_friend_set_p2p_status(f, YAHOO_P2PSTATUS_WE_ARE_SERVER);
2326 }
2327 else
2328 yahoo_friend_set_p2p_status(f, YAHOO_P2PSTATUS_WE_ARE_CLIENT);
2329 }
2330 }
2331 }
2332
2333 /* callback function associated with receiving of data, not considering receipt of multiple YMSG packets in a single TCP packet */
2334 static void yahoo_p2p_read_pkt_cb(gpointer data, gint source, PurpleInputCondition cond)
2335 {
2336 guchar buf[1024]; /* is it safe to assume a fixed array length of 1024 ?? */
2337 int len;
2338 int pos = 0;
2339 int pktlen;
2340 struct yahoo_packet *pkt;
2341 guchar *start = NULL;
2342 struct yahoo_p2p_data *p2p_data;
2343 struct yahoo_data *yd;
2344
2345 if(!(p2p_data = data))
2346 return ;
2347 yd = p2p_data->gc->proto_data;
2348
2349 len = read(source, buf, sizeof(buf));
2350 if ((len < 0) && ((errno == EAGAIN) || (errno == EWOULDBLOCK)))
2351 return ; /* No Worries*/
2352 else if (len <= 0)
2353 {
2354 purple_debug_warning("yahoo","p2p: Error in connection, or host disconnected\n");
2355 /* remove from p2p connection lists, also calls yahoo_p2p_disconnect_destroy_data */
2356 if( g_hash_table_lookup(yd->peers, p2p_data->host_username) )
2357 g_hash_table_remove(yd->peers,p2p_data->host_username);
2358 else
2359 yahoo_p2p_disconnect_destroy_data(data);
2360 return;
2361 }
2362
2363 if(len < YAHOO_PACKET_HDRLEN)
2364 return;
2365
2366 if(strncmp((char *)buf, "YMSG", MIN(4, len)) != 0) {
2367 /* Not a YMSG packet */
2368 purple_debug_warning("yahoo","p2p: Got something other than YMSG packet\n");
2369
2370 start = memchr(buf + 1, 'Y', len - 1);
2371 if (start == NULL)
2372 return;
2373
2374 g_memmove(buf, start, len - (start - buf));
2375 len -= start - buf;
2376 }
2377
2378 pos += 4; /* YMSG */
2379 pos += 2;
2380 pos += 2;
2381
2382 pktlen = yahoo_get16(buf + pos); pos += 2;
2383 purple_debug_misc("yahoo", "p2p: %d bytes to read\n", len);
2384
2385 pkt = yahoo_packet_new(0, 0, 0);
2386 pkt->service = yahoo_get16(buf + pos); pos += 2;
2387 pkt->status = yahoo_get32(buf + pos); pos += 4;
2388 pkt->id = yahoo_get32(buf + pos); pos += 4;
2389
2390 purple_debug_misc("yahoo", "p2p: Yahoo Service: 0x%02x Status: %d\n",pkt->service, pkt->status);
2391 yahoo_packet_read(pkt, buf + pos, pktlen);
2392
2393 /* packet processing */
2394 switch(pkt->service) {
2395 case YAHOO_SERVICE_P2PFILEXFER:
2396 yahoo_p2p_process_p2pfilexfer(data, source, pkt);
2397 break;
2398 case YAHOO_SERVICE_MESSAGE:
2399 yahoo_process_message(p2p_data->gc, pkt, YAHOO_PKT_TYPE_P2P);
2400 break;
2401 case YAHOO_SERVICE_NOTIFY:
2402 yahoo_process_notify(p2p_data->gc, pkt, YAHOO_PKT_TYPE_P2P);
2403 break;
2404 default:
2405 purple_debug_warning("yahoo","p2p: p2p service %d Unhandled\n",pkt->service);
2406 }
2407
2408 yahoo_packet_free(pkt);
2409 }
2410
2411 static void yahoo_p2p_server_send_connected_cb(gpointer data, gint source, PurpleInputCondition cond)
2412 {
2413 int acceptfd;
2414 struct yahoo_p2p_data *p2p_data;
2415 struct yahoo_data *yd;
2416
2417 if(!(p2p_data = data))
2418 return ;
2419 yd = p2p_data->gc->proto_data;
2420
2421 acceptfd = accept(source, NULL, 0);
2422 if(acceptfd == -1 && (errno == EAGAIN || errno == EWOULDBLOCK))
2423 return;
2424 else if(acceptfd == -1) {
2425 purple_debug_warning("yahoo","yahoo_p2p_server_send_connected_cb: accept: %s\n", g_strerror(errno));
2426 yahoo_p2p_disconnect_destroy_data(data);
2427 return;
2428 }
2429
2430 /* remove timeout */
2431 if (yd->yahoo_p2p_server_timeout_handle) {
2432 purple_timeout_remove(yd->yahoo_p2p_server_timeout_handle);
2433 yd->yahoo_p2p_server_timeout_handle = 0;
2434 }
2435
2436 /* remove watcher and close p2p server */
2437 if (yd->yahoo_p2p_server_watcher) {
2438 purple_input_remove(yd->yahoo_p2p_server_watcher);
2439 yd->yahoo_p2p_server_watcher = 0;
2440 }
2441 if (yd->yahoo_local_p2p_server_fd >= 0) {
2442 close(yd->yahoo_local_p2p_server_fd);
2443 yd->yahoo_local_p2p_server_fd = -1;
2444 }
2445
2446 /* Add an Input Read event to the file descriptor */
2447 p2p_data->input_event = purple_input_add(acceptfd, PURPLE_INPUT_READ, yahoo_p2p_read_pkt_cb, data);
2448 p2p_data->source = acceptfd;
2449 }
2450
2451 static gboolean yahoo_cancel_p2p_server_listen_cb(gpointer data)
2452 {
2453 struct yahoo_p2p_data *p2p_data;
2454 struct yahoo_data *yd;
2455
2456 if(!(p2p_data = data))
2457 return FALSE;
2458
2459 yd = p2p_data->gc->proto_data;
2460
2461 purple_debug_warning("yahoo","yahoo p2p server timeout, peer failed to connect");
2462 yahoo_p2p_disconnect_destroy_data(data);
2463 purple_input_remove(yd->yahoo_p2p_server_watcher);
2464 yd->yahoo_p2p_server_watcher = 0;
2465 close(yd->yahoo_local_p2p_server_fd);
2466 yd->yahoo_local_p2p_server_fd = -1;
2467 yd->yahoo_p2p_server_timeout_handle = 0;
2468
2469 return FALSE;
2470 }
2471
2472 static void yahoo_p2p_server_listen_cb(int listenfd, gpointer data)
2473 {
2474 struct yahoo_p2p_data *p2p_data;
2475 struct yahoo_data *yd;
2476
2477 if(!(p2p_data = data))
2478 return ;
2479
2480 if(listenfd == -1) {
2481 purple_debug_warning("yahoo","p2p: error starting p2p server\n");
2482 yahoo_p2p_disconnect_destroy_data(data);
2483 return;
2484 }
2485
2486 yd = p2p_data->gc->proto_data;
2487
2488 /* Add an Input Read event to the file descriptor */
2489 yd->yahoo_local_p2p_server_fd = listenfd;
2490 yd->yahoo_p2p_server_watcher = purple_input_add(listenfd, PURPLE_INPUT_READ, yahoo_p2p_server_send_connected_cb,data);
2491
2492 /* add timeout */
2493 yd->yahoo_p2p_server_timeout_handle = purple_timeout_add_seconds(YAHOO_P2P_SERVER_TIMEOUT, yahoo_cancel_p2p_server_listen_cb, data);
2494 }
2495
2496 /* send p2p pkt containing our encoded ip, asking peer to connect to us */
2497 void yahoo_send_p2p_pkt(PurpleConnection *gc, const char *who, int val_13)
2498 {
2499 const char *public_ip;
2500 guint32 temp[4];
2501 guint32 ip;
2502 char temp_str[100];
2503 gchar *base64_ip = NULL;
2504 YahooFriend *f;
2505 struct yahoo_packet *pkt;
2506 PurpleAccount *account;
2507 struct yahoo_data *yd = gc->proto_data;
2508 struct yahoo_p2p_data *p2p_data;
2509
2510 f = yahoo_friend_find(gc, who);
2511 account = purple_connection_get_account(gc);
2512
2513 /* Do not send invitation if already listening for other connection */
2514 if(yd->yahoo_local_p2p_server_fd >= 0)
2515 return;
2516
2517 /* One shouldn't try to connect to self */
2518 if( strcmp(purple_normalize(account, purple_account_get_username(account)), who) == 0)
2519 return;
2520
2521 /* send packet to only those friends who arent p2p connected and to whom we havent already sent. Do not send if this condition doesn't hold good */
2522 if( !( f && (yahoo_friend_get_p2p_status(f) == YAHOO_P2PSTATUS_NOT_CONNECTED) && (f->p2p_packet_sent == 0)) )
2523 return;
2524
2525 /* Dont send p2p packet to buddies of other protocols */
2526 if(f->protocol)
2527 return;
2528
2529 /* Finally, don't try to connect to buddies not online or on sms */
2530 if( (f->status == YAHOO_STATUS_OFFLINE) || f->sms )
2531 return;
2532
2533 public_ip = purple_network_get_public_ip();
2534 if( (sscanf(public_ip, "%u.%u.%u.%u", &temp[0], &temp[1], &temp[2], &temp[3])) !=4 )
2535 return ;
2536
2537 ip = (temp[3] << 24) | (temp[2] <<16) | (temp[1] << 8) | temp[0];
2538 sprintf(temp_str, "%d", ip);
2539 base64_ip = purple_base64_encode( (guchar *)temp_str, strlen(temp_str) );
2540
2541 pkt = yahoo_packet_new(YAHOO_SERVICE_PEERTOPEER, YAHOO_STATUS_AVAILABLE, 0);
2542 yahoo_packet_hash(pkt, "sssissis",
2543 1, purple_normalize(account, purple_account_get_username(account)),
2544 4, purple_normalize(account, purple_account_get_username(account)),
2545 12, base64_ip, /* base64 encode ip */
2546 61, 0, /* To-do : figure out what is 61 for?? */
2547 2, "",
2548 5, who,
2549 13, val_13,
2550 49, "PEERTOPEER");
2551 yahoo_packet_send_and_free(pkt, yd);
2552
2553 f->p2p_packet_sent = 1; /* set p2p_packet_sent to sent */
2554
2555 p2p_data = g_new0(struct yahoo_p2p_data, 1);
2556
2557 p2p_data->gc = gc;
2558 p2p_data->host_ip = NULL;
2559 p2p_data->host_username = g_strdup(who);
2560 p2p_data->val_13 = val_13;
2561 p2p_data->connection_type = YAHOO_P2P_WE_ARE_SERVER;
2562 p2p_data->source = -1;
2563
2564 purple_network_listen(YAHOO_PAGER_PORT_P2P, SOCK_STREAM, yahoo_p2p_server_listen_cb, p2p_data);
2565
2566 g_free(base64_ip);
2567 }
2568
2569 /* function called when connection to p2p host is setup */
2570 static void yahoo_p2p_init_cb(gpointer data, gint source, const gchar *error_message)
2571 {
2572 struct yahoo_p2p_data *p2p_data;
2573 struct yahoo_packet *pkt_to_send;
2574 PurpleAccount *account;
2575 struct yahoo_data *yd;
2576
2577 if(!(p2p_data = data))
2578 return ;
2579 yd = p2p_data->gc->proto_data;
2580
2581 if(error_message != NULL) {
2582 purple_debug_warning("yahoo","p2p: %s\n",error_message);
2583 yahoo_send_p2p_pkt(p2p_data->gc, p2p_data->host_username, 2);/* send p2p init packet with val_13=2 */
2584
2585 yahoo_p2p_disconnect_destroy_data(p2p_data);
2586 return;
2587 }
2588
2589 /* Add an Input Read event to the file descriptor */
2590 p2p_data->input_event = purple_input_add(source, PURPLE_INPUT_READ, yahoo_p2p_read_pkt_cb, data);
2591 p2p_data->source = source;
2592
2593 account = purple_connection_get_account(p2p_data->gc);
2594
2595 /* Build the yahoo packet */
2596 pkt_to_send = yahoo_packet_new(YAHOO_SERVICE_P2PFILEXFER, YAHOO_STATUS_AVAILABLE, yd->session_id);
2597 yahoo_packet_hash(pkt_to_send, "ssisi",
2598 4, purple_normalize(account, purple_account_get_username(account)),
2599 5, p2p_data->host_username,
2600 241, 0, /* Protocol identifier */
2601 49, "PEERTOPEER",
2602 13, 1); /* we receive key13= 0 or 2, we send key13=1 */
2603
2604 yahoo_p2p_write_pkt(source, pkt_to_send); /* build raw packet and send */
2605 yahoo_packet_free(pkt_to_send);
2606 }
2607
2608 static void yahoo_process_p2p(PurpleConnection *gc, struct yahoo_packet *pkt)
2609 {
2610 GSList *l = pkt->hash;
2611 char *who = NULL;
2612 char *base64 = NULL;
2613 guchar *decoded;
2614 gsize len;
2615 gint val_13 = 0;
2616 gint val_11 = 0;
2617 PurpleAccount *account;
2618 YahooFriend *f;
2619
2620 /* if status is not 1 ie YAHOO_STATUS_BRB, the packet bounced back, so contains our own ip */
2621 if(!(pkt->status == YAHOO_STATUS_BRB))
2622 return ;
2623
2624 while (l) {
2625 struct yahoo_pair *pair = l->data;
2626
2627 switch (pair->key) {
2628 case 5:
2629 /* our identity */
2630 break;
2631 case 4:
2632 who = pair->value;
2633 break;
2634 case 1:
2635 /* who again, the master identity this time? */
2636 break;
2637 case 12:
2638 base64 = pair->value;
2639 /* so, this is an ip address. in base64. decoded it's in ascii.
2640 after strtol, it's in reversed byte order. Who thought this up?*/
2641 break;
2642 case 13:
2643 val_13 = strtol(pair->value, NULL, 10);
2644 break;
2645 case 11:
2646 val_11 = strtol(pair->value, NULL, 10); /* session id of peer */
2647 if( (f = yahoo_friend_find(gc, who)) )
2648 f->session_id = val_11;
2649 break;
2650 /*
2651 TODO: figure these out
2652 yahoo: Key: 61 Value: 0
2653 yahoo: Key: 2 Value:
2654 yahoo: Key: 13 Value: 0 packet count ??
2655 yahoo: Key: 49 Value: PEERTOPEER
2656 yahoo: Key: 140 Value: 1
2657 */
2658
2659 }
2660
2661 l = l->next;
2662 }
2663
2664 if (base64) {
2665 guint32 ip;
2666 YahooFriend *f;
2667 char *host_ip;
2668 struct yahoo_p2p_data *p2p_data;
2669
2670 decoded = purple_base64_decode(base64, &len);
2671 if (len) {
2672 char *tmp = purple_str_binary_to_ascii(decoded, len);
2673 purple_debug_info("yahoo", "Got P2P service packet (from server): who = %s, ip = %s\n", who, tmp);
2674 g_free(tmp);
2675 }
2676
2677 ip = strtol((gchar *)decoded, NULL, 10);
2678 g_free(decoded);
2679 host_ip = g_strdup_printf("%u.%u.%u.%u", ip & 0xff, (ip >> 8) & 0xff, (ip >> 16) & 0xff,
2680 (ip >> 24) & 0xff);
2681 f = yahoo_friend_find(gc, who);
2682 if (f)
2683 yahoo_friend_set_ip(f, host_ip);
2684 purple_debug_info("yahoo", "IP : %s\n", host_ip);
2685
2686 account = purple_connection_get_account(gc);
2687
2688 if(val_11==0) {
2689 if(!f)
2690 return;
2691 else
2692 val_11 = f->session_id;
2693 }
2694
2695 p2p_data = g_new0(struct yahoo_p2p_data, 1);
2696 p2p_data->host_username = g_strdup(who);
2697 p2p_data->val_13 = val_13;
2698 p2p_data->session_id = val_11;
2699 p2p_data->host_ip = host_ip;
2700 p2p_data->gc = gc;
2701 p2p_data->connection_type = YAHOO_P2P_WE_ARE_CLIENT;
2702 p2p_data->source = -1;
2703
2704 /* connect to host */
2705 if((purple_proxy_connect(gc, account, host_ip, YAHOO_PAGER_PORT_P2P, yahoo_p2p_init_cb, p2p_data))==NULL) {
2706 purple_debug_info("yahoo","p2p: Connection to %s failed\n", host_ip);
2707 g_free(p2p_data->host_ip);
2708 g_free(p2p_data->host_username);
2709 g_free(p2p_data);
2710 }
2711 }
2712 }
2713
2714 static void yahoo_process_audible(PurpleConnection *gc, struct yahoo_packet *pkt)
2715 {
2716 PurpleAccount *account;
2717 char *who = NULL, *msg = NULL, *id = NULL;
2718 GSList *l = pkt->hash;
2719
2720 account = purple_connection_get_account(gc);
2721
2722 while (l) {
2723 struct yahoo_pair *pair = l->data;
2724
2725 switch (pair->key) {
2726 case 4:
2727 who = pair->value;
2728 break;
2729 case 5:
2730 /* us */
2731 break;
2732 case 230:
2733 /* the audible, in foo.locale.bar.baz format
2734 eg: base.tw.smiley.smiley43 */
2735 id = pair->value;
2736 break;
2737 case 231:
2738 /* the text of the audible */
2739 msg = pair->value;
2740 break;
2741 case 232:
2742 /* weird number (md5 hash?), like 8ebab9094156135f5dcbaccbeee662a5c5fd1420 */
2743 break;
2744 }
2745
2746 l = l->next;
2747 }
2748
2749 if (!msg)
2750 msg = id;
2751 if (!who || !msg)
2752 return;
2753 if (!g_utf8_validate(msg, -1, NULL)) {
2754 purple_debug_misc("yahoo", "Warning, nonutf8 audible, ignoring!\n");
2755 return;
2756 }
2757 if (!purple_privacy_check(account, who)) {
2758 purple_debug_misc("yahoo", "Audible message from %s for %s dropped!\n",
2759 purple_account_get_username(account), who);
2760 return;
2761 }
2762 if (id) {
2763 /* "http://us.dl1.yimg.com/download.yahoo.com/dl/aud/"+locale+"/"+id+".swf" */
2764 char **audible_locale = g_strsplit(id, ".", 0);
2765 char *buf = g_strdup_printf(_("[ Audible %s/%s/%s.swf ] %s"), YAHOO_AUDIBLE_URL, audible_locale[1], id, msg);
2766 g_strfreev(audible_locale);
2767
2768 serv_got_im(gc, who, buf, 0, time(NULL));
2769 g_free(buf);
2770 } else
2771 serv_got_im(gc, who, msg, 0, time(NULL));
2772 }
2773
2774 static void yahoo_packet_process(PurpleConnection *gc, struct yahoo_packet *pkt)
2775 {
2776 switch (pkt->service) {
2777 case YAHOO_SERVICE_LOGON:
2778 case YAHOO_SERVICE_LOGOFF:
2779 case YAHOO_SERVICE_ISAWAY:
2780 case YAHOO_SERVICE_ISBACK:
2781 case YAHOO_SERVICE_GAMELOGON:
2782 case YAHOO_SERVICE_GAMELOGOFF:
2783 case YAHOO_SERVICE_CHATLOGON:
2784 case YAHOO_SERVICE_CHATLOGOFF:
2785 case YAHOO_SERVICE_Y6_STATUS_UPDATE:
2786 case YAHOO_SERVICE_STATUS_15:
2787 yahoo_process_status(gc, pkt);
2788 break;
2789 case YAHOO_SERVICE_NOTIFY:
2790 yahoo_process_notify(gc, pkt, YAHOO_PKT_TYPE_SERVER);
2791 break;
2792 case YAHOO_SERVICE_MESSAGE:
2793 case YAHOO_SERVICE_GAMEMSG:
2794 case YAHOO_SERVICE_CHATMSG:
2795 yahoo_process_message(gc, pkt, YAHOO_PKT_TYPE_SERVER);
2796 break;
2797 case YAHOO_SERVICE_SYSMESSAGE:
2798 yahoo_process_sysmessage(gc, pkt);
2799 break;
2800 case YAHOO_SERVICE_NEWMAIL:
2801 yahoo_process_mail(gc, pkt);
2802 break;
2803 case YAHOO_SERVICE_NEWCONTACT:
2804 yahoo_process_contact(gc, pkt);
2805 break;
2806 case YAHOO_SERVICE_AUTHRESP:
2807 yahoo_process_authresp(gc, pkt);
2808 break;
2809 case YAHOO_SERVICE_LIST:
2810 yahoo_process_list(gc, pkt);
2811 break;
2812 case YAHOO_SERVICE_LIST_15:
2813 yahoo_process_list_15(gc, pkt);
2814 break;
2815 case YAHOO_SERVICE_AUTH:
2816 yahoo_process_auth(gc, pkt);
2817 break;
2818 case YAHOO_SERVICE_AUTH_REQ_15:
2819 yahoo_buddy_auth_req_15(gc, pkt);
2820 break;
2821 case YAHOO_SERVICE_ADDBUDDY:
2822 yahoo_process_addbuddy(gc, pkt);
2823 break;
2824 case YAHOO_SERVICE_IGNORECONTACT:
2825 yahoo_process_ignore(gc, pkt);
2826 break;
2827 case YAHOO_SERVICE_CONFINVITE:
2828 case YAHOO_SERVICE_CONFADDINVITE:
2829 yahoo_process_conference_invite(gc, pkt);
2830 break;
2831 case YAHOO_SERVICE_CONFDECLINE:
2832 yahoo_process_conference_decline(gc, pkt);
2833 break;
2834 case YAHOO_SERVICE_CONFLOGON:
2835 yahoo_process_conference_logon(gc, pkt);
2836 break;
2837 case YAHOO_SERVICE_CONFLOGOFF:
2838 yahoo_process_conference_logoff(gc, pkt);
2839 break;
2840 case YAHOO_SERVICE_CONFMSG:
2841 yahoo_process_conference_message(gc, pkt);
2842 break;
2843 case YAHOO_SERVICE_CHATONLINE:
2844 yahoo_process_chat_online(gc, pkt);
2845 break;
2846 case YAHOO_SERVICE_CHATLOGOUT:
2847 yahoo_process_chat_logout(gc, pkt);
2848 break;
2849 case YAHOO_SERVICE_CHATGOTO:
2850 yahoo_process_chat_goto(gc, pkt);
2851 break;
2852 case YAHOO_SERVICE_CHATJOIN:
2853 yahoo_process_chat_join(gc, pkt);
2854 break;
2855 case YAHOO_SERVICE_CHATLEAVE: /* XXX is this right? */
2856 case YAHOO_SERVICE_CHATEXIT:
2857 yahoo_process_chat_exit(gc, pkt);
2858 break;
2859 case YAHOO_SERVICE_CHATINVITE: /* XXX never seen this one, might not do it right */
2860 case YAHOO_SERVICE_CHATADDINVITE:
2861 yahoo_process_chat_addinvite(gc, pkt);
2862 break;
2863 case YAHOO_SERVICE_COMMENT:
2864 yahoo_process_chat_message(gc, pkt);
2865 break;
2866 case YAHOO_SERVICE_PRESENCE_PERM:
2867 case YAHOO_SERVICE_PRESENCE_SESSION:
2868 yahoo_process_presence(gc, pkt);
2869 break;
2870 case YAHOO_SERVICE_P2PFILEXFER:
2871 /* This case had no break and continued; thus keeping it this way.*/
2872 yahoo_process_p2p(gc, pkt); /* P2PFILEXFER handled the same way as process_p2p */
2873 yahoo_process_p2pfilexfer(gc, pkt); /* redundant ??, need to have a break now */
2874 case YAHOO_SERVICE_FILETRANSFER:
2875 yahoo_process_filetransfer(gc, pkt);
2876 break;
2877 case YAHOO_SERVICE_PEERTOPEER:
2878 yahoo_process_p2p(gc, pkt);
2879 break;
2880 case YAHOO_SERVICE_PICTURE:
2881 yahoo_process_picture(gc, pkt);
2882 break;
2883 case YAHOO_SERVICE_PICTURE_CHECKSUM:
2884 yahoo_process_picture_checksum(gc, pkt);
2885 break;
2886 case YAHOO_SERVICE_PICTURE_UPLOAD:
2887 yahoo_process_picture_upload(gc, pkt);
2888 break;
2889 case YAHOO_SERVICE_PICTURE_UPDATE:
2890 case YAHOO_SERVICE_AVATAR_UPDATE:
2891 yahoo_process_avatar_update(gc, pkt);
2892 break;
2893 case YAHOO_SERVICE_AUDIBLE:
2894 yahoo_process_audible(gc, pkt);
2895 break;
2896 case YAHOO_SERVICE_FILETRANS_15:
2897 yahoo_process_filetrans_15(gc, pkt);
2898 break;
2899 case YAHOO_SERVICE_FILETRANS_INFO_15:
2900 yahoo_process_filetrans_info_15(gc, pkt);
2901 break;
2902 case YAHOO_SERVICE_FILETRANS_ACC_15:
2903 yahoo_process_filetrans_acc_15(gc, pkt);
2904 break;
2905 case YAHOO_SERVICE_SMS_MSG:
2906 yahoo_process_sms_message(gc, pkt);
2907 break;
2908
2909 default:
2910 purple_debug_error("yahoo", "Unhandled service 0x%02x\n", pkt->service);
2911 break;
2912 }
2913 }
2914
2915 static void yahoo_pending(gpointer data, gint source, PurpleInputCondition cond)
2916 {
2917 PurpleConnection *gc = data;
2918 struct yahoo_data *yd = gc->proto_data;
2919 char buf[1024];
2920 int len;
2921
2922 len = read(yd->fd, buf, sizeof(buf));
2923
2924 if (len < 0) {
2925 gchar *tmp;
2926
2927 if (errno == EAGAIN)
2928 /* No worries */
2929 return;
2930
2931 tmp = g_strdup_printf(_("Lost connection with server:\n%s"),
2932 g_strerror(errno));
2933 purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp);
2934 g_free(tmp);
2935 return;
2936 } else if (len == 0) {
2937 purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
2938 _("Server closed the connection."));
2939 return;
2940 }
2941 gc->last_received = time(NULL);
2942 yd->rxqueue = g_realloc(yd->rxqueue, len + yd->rxlen);
2943 memcpy(yd->rxqueue + yd->rxlen, buf, len);
2944 yd->rxlen += len;
2945
2946 while (1) {
2947 struct yahoo_packet *pkt;
2948 int pos = 0;
2949 int pktlen;
2950
2951 if (yd->rxlen < YAHOO_PACKET_HDRLEN)
2952 return;
2953
2954 if (strncmp((char *)yd->rxqueue, "YMSG", MIN(4, yd->rxlen)) != 0) {
2955 /* HEY! This isn't even a YMSG packet. What
2956 * are you trying to pull? */
2957 guchar *start;
2958
2959 purple_debug_warning("yahoo", "Error in YMSG stream, got something not a YMSG packet!\n");
2960
2961 start = memchr(yd->rxqueue + 1, 'Y', yd->rxlen - 1);
2962 if (start) {
2963 g_memmove(yd->rxqueue, start, yd->rxlen - (start - yd->rxqueue));
2964 yd->rxlen -= start - yd->rxqueue;
2965 continue;
2966 } else {
2967 g_free(yd->rxqueue);
2968 yd->rxqueue = NULL;
2969 yd->rxlen = 0;
2970 return;
2971 }
2972 }
2973
2974 pos += 4; /* YMSG */
2975 pos += 2;
2976 pos += 2;
2977
2978 pktlen = yahoo_get16(yd->rxqueue + pos); pos += 2;
2979 purple_debug_misc("yahoo", "%d bytes to read, rxlen is %d\n", pktlen, yd->rxlen);
2980
2981 if (yd->rxlen < (YAHOO_PACKET_HDRLEN + pktlen))
2982 return;
2983
2984 yahoo_packet_dump(yd->rxqueue, YAHOO_PACKET_HDRLEN + pktlen);
2985
2986 pkt = yahoo_packet_new(0, 0, 0);
2987
2988 pkt->service = yahoo_get16(yd->rxqueue + pos); pos += 2;
2989 pkt->status = yahoo_get32(yd->rxqueue + pos); pos += 4;
2990 purple_debug_misc("yahoo", "Yahoo Service: 0x%02x Status: %d\n",
2991 pkt->service, pkt->status);
2992 pkt->id = yahoo_get32(yd->rxqueue + pos); pos += 4;
2993
2994 yahoo_packet_read(pkt, yd->rxqueue + pos, pktlen);
2995
2996 yd->rxlen -= YAHOO_PACKET_HDRLEN + pktlen;
2997 if (yd->rxlen) {
2998 guchar *tmp = g_memdup(yd->rxqueue + YAHOO_PACKET_HDRLEN + pktlen, yd->rxlen);
2999 g_free(yd->rxqueue);
3000 yd->rxqueue = tmp;
3001 } else {
3002 g_free(yd->rxqueue);
3003 yd->rxqueue = NULL;
3004 }
3005
3006 yahoo_packet_process(gc, pkt);
3007
3008 yahoo_packet_free(pkt);
3009 }
3010 }
3011
3012 static void yahoo_got_connected(gpointer data, gint source, const gchar *error_message)
3013 {
3014 PurpleConnection *gc = data;
3015 struct yahoo_data *yd;
3016 struct yahoo_packet *pkt;
3017
3018 if (source < 0) {
3019 gchar *tmp;
3020 tmp = g_strdup_printf(_("Could not establish a connection with the server:\n%s"),
3021 error_message);
3022 purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp);
3023 g_free(tmp);
3024 return;
3025 }
3026
3027 yd = gc->proto_data;
3028 yd->fd = source;
3029
3030 pkt = yahoo_packet_new(YAHOO_SERVICE_AUTH, yd->current_status, 0);
3031
3032 yahoo_packet_hash_str(pkt, 1, purple_normalize(gc->account, purple_account_get_username(purple_connection_get_account(gc))));
3033 yahoo_packet_send_and_free(pkt, yd);
3034
3035 gc->inpa = purple_input_add(yd->fd, PURPLE_INPUT_READ, yahoo_pending, gc);
3036 }
3037
3038 #ifdef TRY_WEBMESSENGER_LOGIN
3039 static void yahoo_got_web_connected(gpointer data, gint source, const gchar *error_message)
3040 {
3041 PurpleConnection *gc = data;
3042 struct yahoo_data *yd;
3043 struct yahoo_packet *pkt;
3044
3045 if (source < 0) {
3046 gchar *tmp;
3047 tmp = g_strdup_printf(_("Could not establish a connection with the server:\n%s"),
3048 error_message);
3049 purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp);
3050 g_free(tmp);
3051 return;
3052 }
3053
3054 yd = gc->proto_data;
3055 yd->fd = source;
3056
3057 pkt = yahoo_packet_new(YAHOO_SERVICE_WEBLOGIN, YAHOO_STATUS_WEBLOGIN, 0);
3058
3059 yahoo_packet_hash(pkt, "sss", 0,
3060 purple_normalize(gc->account, purple_account_get_username(purple_connection_get_account(gc))),
3061 1, purple_normalize(gc->account, purple_account_get_username(purple_connection_get_account(gc))),
3062 6, yd->auth);
3063 yahoo_packet_send_and_free(pkt, yd);
3064
3065 g_free(yd->auth);
3066 gc->inpa = purple_input_add(yd->fd, PURPLE_INPUT_READ, yahoo_pending, gc);
3067 }
3068
3069 static void yahoo_web_pending(gpointer data, gint source, PurpleInputCondition cond)
3070 {
3071 PurpleConnection *gc = data;
3072 PurpleAccount *account = purple_connection_get_account(gc);
3073 struct yahoo_data *yd = gc->proto_data;
3074 char bufread[2048], *i = bufread, *buf = bufread;
3075 int len;
3076 GString *s;
3077
3078 len = read(source, bufread, sizeof(bufread) - 1);
3079
3080 if (len < 0) {
3081 gchar *tmp;
3082
3083 if (errno == EAGAIN)
3084 /* No worries */
3085 return;
3086
3087 tmp = g_strdup_printf(_("Lost connection with server:\n%s"),
3088 g_strerror(errno));
3089 purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp);
3090 g_free(tmp);
3091 return;
3092 } else if (len == 0) {
3093 purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
3094 _("Server closed the connection."));
3095 return;
3096 }
3097
3098 if (yd->rxlen > 0 || !g_strstr_len(buf, len, "\r\n\r\n")) {
3099 yd->rxqueue = g_realloc(yd->rxqueue, yd->rxlen + len + 1);
3100 memcpy(yd->rxqueue + yd->rxlen, buf, len);
3101 yd->rxlen += len;
3102 i = buf = (char *)yd->rxqueue;
3103 len = yd->rxlen;
3104 }
3105 buf[len] = '\0';
3106
3107 if ((strncmp(buf, "HTTP/1.0 302", strlen("HTTP/1.0 302")) &&
3108 strncmp(buf, "HTTP/1.1 302", strlen("HTTP/1.1 302")))) {
3109 purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
3110 _("Received unexpected HTTP response from server."));
3111 purple_debug_misc("yahoo", "Unexpected HTTP response: %s\n", buf);
3112 return;
3113 }
3114
3115 s = g_string_sized_new(len);
3116
3117 while ((i = strstr(i, "Set-Cookie: "))) {
3118
3119 i += strlen("Set-Cookie: ");
3120 for (;*i != ';' && *i != '\0'; i++)
3121 g_string_append_c(s, *i);
3122
3123 g_string_append(s, "; ");
3124 /* Should these cookies be included too when trying for xfer?
3125 * It seems to work without these
3126 */
3127 }
3128
3129 yd->auth = g_string_free(s, FALSE);
3130 purple_input_remove(gc->inpa);
3131 close(source);
3132 g_free(yd->rxqueue);
3133 yd->rxqueue = NULL;
3134 yd->rxlen = 0;
3135 /* Now we have our cookies to login with. I'll go get the milk. */
3136 if (purple_proxy_connect(gc, account, "wcs2.msg.dcn.yahoo.com",
3137 purple_account_get_int(account, "port", YAHOO_PAGER_PORT),
3138 yahoo_got_web_connected, gc) == NULL) {
3139 purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
3140 _("Connection problem"));
3141 return;
3142 }
3143 }
3144
3145 static void yahoo_got_cookies_send_cb(gpointer data, gint source, PurpleInputCondition cond)
3146 {
3147 PurpleConnection *gc;
3148 struct yahoo_data *yd;
3149 int written, remaining;
3150
3151 gc = data;
3152 yd = gc->proto_data;
3153
3154 remaining = strlen(yd->auth) - yd->auth_written;
3155 written = write(source, yd->auth + yd->auth_written, remaining);
3156
3157 if (written < 0 && errno == EAGAIN)
3158 written = 0;
3159 else if (written <= 0) {
3160 gchar *tmp;
3161 g_free(yd->auth);
3162 yd->auth = NULL;
3163 if (gc->inpa)
3164 purple_input_remove(gc->inpa);
3165 gc->inpa = 0;
3166 tmp = g_strdup_printf(_("Lost connection with %s:\n%s"),
3167 "login.yahoo.com:80", g_strerror(errno));
3168 purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp);
3169 g_free(tmp);
3170 return;
3171 }
3172
3173 if (written < remaining) {
3174 yd->auth_written += written;
3175 return;
3176 }
3177
3178 g_free(yd->auth);
3179 yd->auth = NULL;
3180 yd->auth_written = 0;
3181 purple_input_remove(gc->inpa);
3182 gc->inpa = purple_input_add(source, PURPLE_INPUT_READ, yahoo_web_pending, gc);
3183 }
3184
3185 static void yahoo_got_cookies(gpointer data, gint source, const gchar *error_message)
3186 {
3187 PurpleConnection *gc = data;
3188
3189 if (source < 0) {
3190 gchar *tmp;
3191 tmp = g_strdup_printf(_("Could not establish a connection with %s:\n%s"),
3192 "login.yahoo.com:80", error_message);
3193 purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp);
3194 g_free(tmp);
3195 return;
3196 }
3197
3198 if (gc->inpa == 0)
3199 {
3200 gc->inpa = purple_input_add(source, PURPLE_INPUT_WRITE,
3201 yahoo_got_cookies_send_cb, gc);
3202 yahoo_got_cookies_send_cb(gc, source, PURPLE_INPUT_WRITE);
3203 }
3204 }
3205
3206 static void yahoo_login_page_hash_iter(const char *key, const char *val, GString *url)
3207 {
3208 if (!strcmp(key, "passwd") || !strcmp(key, "login"))
3209 return;
3210 g_string_append_c(url, '&');
3211 g_string_append(url, key);
3212 g_string_append_c(url, '=');
3213 if (!strcmp(key, ".save") || !strcmp(key, ".js"))
3214 g_string_append_c(url, '1');
3215 else if (!strcmp(key, ".challenge"))
3216 g_string_append(url, val);
3217 else
3218 g_string_append(url, purple_url_encode(val));
3219 }
3220
3221 static GHashTable *yahoo_login_page_hash(const char *buf, size_t len)
3222 {
3223 GHashTable *hash = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
3224 const char *c = buf;
3225 char *d;
3226 char name[64], value[64];
3227 int count;
3228 int input_len = strlen("<input ");
3229 int name_len = strlen("name=\"");
3230 int value_len = strlen("value=\"");
3231 while ((len > ((c - buf) + input_len))
3232 && (c = strstr(c, "<input "))) {
3233 if (!(c = g_strstr_len(c, len - (c - buf), "name=\"")))
3234 continue;
3235 c += name_len;
3236 count = sizeof(name)-1;
3237 for (d = name; (len > ((c - buf) + 1)) && *c!='"'
3238 && count; c++, d++, count--)
3239 *d = *c;
3240 *d = '\0';
3241 count = sizeof(value)-1;
3242 if (!(d = g_strstr_len(c, len - (c - buf), "value=\"")))
3243 continue;
3244 d += value_len;
3245 if (strchr(c, '>') < d)
3246 break;
3247 for (c = d, d = value; (len > ((c - buf) + 1))
3248 && *c!='"' && count; c++, d++, count--)
3249 *d = *c;
3250 *d = '\0';
3251 g_hash_table_insert(hash, g_strdup(name), g_strdup(value));
3252 }
3253 return hash;
3254 }
3255
3256 static void
3257 yahoo_login_page_cb(PurpleUtilFetchUrlData *url_data, gpointer user_data,
3258 const gchar *url_text, size_t len, const gchar *error_message)
3259 {
3260 PurpleConnection *gc = (PurpleConnection *)user_data;
3261 PurpleAccount *account = purple_connection_get_account(gc);
3262 struct yahoo_data *yd = gc->proto_data;
3263 const char *sn = purple_account_get_username(account);
3264 const char *pass = purple_connection_get_password(gc);
3265 GHashTable *hash = yahoo_login_page_hash(url_text, len);
3266 GString *url = g_string_new("GET http://login.yahoo.com/config/login?login=");
3267 char md5[33], *hashp = md5, *chal;
3268 int i;
3269 PurpleCipher *cipher;
3270 PurpleCipherContext *context;
3271 guchar digest[16];
3272
3273 yd->url_datas = g_slist_remove(yd->url_datas, url_data);
3274
3275 if (error_message != NULL)
3276 {
3277 purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
3278 error_message);
3279 return;
3280 }
3281
3282 url = g_string_append(url, sn);
3283 url = g_string_append(url, "&passwd=");
3284
3285 cipher = purple_ciphers_find_cipher("md5");
3286 context = purple_cipher_context_new(cipher, NULL);
3287
3288 purple_cipher_context_append(context, (const guchar *)pass, strlen(pass));
3289 purple_cipher_context_digest(context, sizeof(digest), digest, NULL);
3290 for (i = 0; i < 16; ++i) {
3291 g_snprintf(hashp, 3, "%02x", digest[i]);
3292 hashp += 2;
3293 }
3294
3295 chal = g_strconcat(md5, g_hash_table_lookup(hash, ".challenge"), NULL);
3296 purple_cipher_context_reset(context, NULL);
3297 purple_cipher_context_append(context, (const guchar *)chal, strlen(chal));
3298 purple_cipher_context_digest(context, sizeof(digest), digest, NULL);
3299 hashp = md5;
3300 for (i = 0; i < 16; ++i) {
3301 g_snprintf(hashp, 3, "%02x", digest[i]);
3302 hashp += 2;
3303 }
3304 /*
3305 * I dunno why this is here and commented out.. but in case it's needed
3306 * I updated it..
3307
3308 purple_cipher_context_reset(context, NULL);
3309 purple_cipher_context_append(context, md5, strlen(md5));
3310 purple_cipher_context_digest(context, sizeof(digest), digest, NULL);
3311 hashp = md5;
3312 for (i = 0; i < 16; ++i) {
3313 g_snprintf(hashp, 3, "%02x", digest[i]);
3314 hashp += 2;
3315 }
3316 */
3317 g_free(chal);
3318
3319 url = g_string_append(url, md5);
3320 g_hash_table_foreach(hash, (GHFunc)yahoo_login_page_hash_iter, url);
3321
3322 url = g_string_append(url, "&.hash=1&.md5=1 HTTP/1.1\r\n"
3323 "Host: login.yahoo.com\r\n\r\n");
3324 g_hash_table_destroy(hash);
3325 yd->auth = g_string_free(url, FALSE);
3326 if (purple_proxy_connect(gc, account, "login.yahoo.com", 80, yahoo_got_cookies, gc) == NULL) {
3327 purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
3328 _("Connection problem"));
3329 return;
3330 }
3331
3332 purple_cipher_context_destroy(context);
3333 }
3334 #endif /* TRY_WEBMESSENGER_LOGIN */
3335
3336 static void yahoo_server_check(PurpleAccount *account)
3337 {
3338 const char *server;
3339
3340 server = purple_account_get_string(account, "server", YAHOO_PAGER_HOST);
3341
3342 if (*server == '\0' || g_str_equal(server, "scs.yahoo.com") ||
3343 g_str_equal(server, "scs.msg.yahoo.com"))
3344 purple_account_set_string(account, "server", YAHOO_PAGER_HOST);
3345 }
3346
3347 static void yahoo_picture_check(PurpleAccount *account)
3348 {
3349 PurpleConnection *gc = purple_account_get_connection(account);
3350 PurpleStoredImage *img = purple_buddy_icons_find_account_icon(account);
3351
3352 yahoo_set_buddy_icon(gc, img);
3353 purple_imgstore_unref(img);
3354 }
3355
3356 static int get_yahoo_status_from_purple_status(PurpleStatus *status)
3357 {
3358 PurplePresence *presence;
3359 const char *status_id;
3360 const char *msg;
3361
3362 presence = purple_status_get_presence(status);
3363 status_id = purple_status_get_id(status);
3364 msg = purple_status_get_attr_string(status, "message");
3365
3366 if (!strcmp(status_id, YAHOO_STATUS_TYPE_AVAILABLE)) {
3367 if ((msg != NULL) && (*msg != '\0'))
3368 return YAHOO_STATUS_CUSTOM;
3369 else
3370 return YAHOO_STATUS_AVAILABLE;
3371 } else if (!strcmp(status_id, YAHOO_STATUS_TYPE_BRB)) {
3372 return YAHOO_STATUS_BRB;
3373 } else if (!strcmp(status_id, YAHOO_STATUS_TYPE_BUSY)) {
3374 return YAHOO_STATUS_BUSY;
3375 } else if (!strcmp(status_id, YAHOO_STATUS_TYPE_NOTATHOME)) {
3376 return YAHOO_STATUS_NOTATHOME;
3377 } else if (!strcmp(status_id, YAHOO_STATUS_TYPE_NOTATDESK)) {
3378 return YAHOO_STATUS_NOTATDESK;
3379 } else if (!strcmp(status_id, YAHOO_STATUS_TYPE_NOTINOFFICE)) {
3380 return YAHOO_STATUS_NOTINOFFICE;
3381 } else if (!strcmp(status_id, YAHOO_STATUS_TYPE_ONPHONE)) {
3382 return YAHOO_STATUS_ONPHONE;
3383 } else if (!strcmp(status_id, YAHOO_STATUS_TYPE_ONVACATION)) {
3384 return YAHOO_STATUS_ONVACATION;
3385 } else if (!strcmp(status_id, YAHOO_STATUS_TYPE_OUTTOLUNCH)) {
3386 return YAHOO_STATUS_OUTTOLUNCH;
3387 } else if (!strcmp(status_id, YAHOO_STATUS_TYPE_STEPPEDOUT)) {
3388 return YAHOO_STATUS_STEPPEDOUT;
3389 } else if (!strcmp(status_id, YAHOO_STATUS_TYPE_INVISIBLE)) {
3390 return YAHOO_STATUS_INVISIBLE;
3391 } else if (!strcmp(status_id, YAHOO_STATUS_TYPE_AWAY)) {
3392 return YAHOO_STATUS_CUSTOM;
3393 } else if (purple_presence_is_idle(presence)) {
3394 return YAHOO_STATUS_IDLE;
3395 } else {
3396 purple_debug_error("yahoo", "Unexpected PurpleStatus!\n");
3397 return YAHOO_STATUS_AVAILABLE;
3398 }
3399 }
3400
3401 static void yahoo_login(PurpleAccount *account) {
3402 PurpleConnection *gc = purple_account_get_connection(account);
3403 struct yahoo_data *yd = gc->proto_data = g_new0(struct yahoo_data, 1);
3404 PurpleStatus *status = purple_account_get_active_status(account);
3405 gc->flags |= PURPLE_CONNECTION_HTML | PURPLE_CONNECTION_NO_BGCOLOR | PURPLE_CONNECTION_NO_URLDESC;
3406
3407 purple_connection_update_progress(gc, _("Connecting"), 1, 2);
3408
3409 purple_connection_set_display_name(gc, purple_account_get_username(account));
3410
3411 yd->yahoo_local_p2p_server_fd = -1;
3412 yd->fd = -1;
3413 yd->txhandler = 0;
3414 /* TODO: Is there a good grow size for the buffer? */
3415 yd->txbuf = purple_circ_buffer_new(0);
3416 yd->friends = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, yahoo_friend_free);
3417 yd->imvironments = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
3418 yd->xfer_peer_idstring_map = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, NULL);
3419 yd->peers = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, yahoo_p2p_disconnect_destroy_data);
3420 yd->sms_carrier = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
3421 yd->yahoo_p2p_timer = purple_timeout_add_seconds(YAHOO_P2P_KEEPALIVE_SECS, yahoo_p2p_keepalive, gc);
3422 yd->confs = NULL;
3423 yd->conf_id = 2;
3424 yd->last_keepalive = yd->last_ping = time(NULL);
3425
3426 yd->current_status = get_yahoo_status_from_purple_status(status);
3427
3428 yahoo_server_check(account);
3429 yahoo_picture_check(account);
3430
3431 if (purple_account_get_bool(account, "yahoojp", FALSE)) {
3432 yd->jp = TRUE;
3433 if (purple_proxy_connect(gc, account,
3434 purple_account_get_string(account, "serverjp", YAHOOJP_PAGER_HOST),
3435 purple_account_get_int(account, "port", YAHOO_PAGER_PORT),
3436 yahoo_got_connected, gc) == NULL)
3437 {
3438 purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
3439 _("Connection problem"));
3440 return;
3441 }
3442 } else {
3443 yd->jp = FALSE;
3444 if (purple_proxy_connect(gc, account,
3445 purple_account_get_string(account, "server", YAHOO_PAGER_HOST),
3446 purple_account_get_int(account, "port", YAHOO_PAGER_PORT),
3447 yahoo_got_connected, gc) == NULL)
3448 {
3449 purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
3450 _("Connection problem"));
3451 return;
3452 }
3453 }
3454 }
3455
3456 static void yahoo_close(PurpleConnection *gc) {
3457 struct yahoo_data *yd = (struct yahoo_data *)gc->proto_data;
3458 GSList *l;
3459
3460 if (gc->inpa)
3461 purple_input_remove(gc->inpa);
3462
3463 while (yd->url_datas) {
3464 purple_util_fetch_url_cancel(yd->url_datas->data);
3465 yd->url_datas = g_slist_delete_link(yd->url_datas, yd->url_datas);
3466 }
3467
3468 for (l = yd->confs; l; l = l->next) {
3469 PurpleConversation *conv = l->data;
3470
3471 yahoo_conf_leave(yd, purple_conversation_get_name(conv),
3472 purple_connection_get_display_name(gc),
3473 purple_conv_chat_get_users(PURPLE_CONV_CHAT(conv)));
3474 }
3475 g_slist_free(yd->confs);
3476
3477 for (l = yd->cookies; l; l = l->next) {
3478 g_free(l->data);
3479 l->data=NULL;
3480 }
3481 g_slist_free(yd->cookies);
3482
3483 yd->chat_online = FALSE;
3484 if (yd->in_chat)
3485 yahoo_c_leave(gc, 1); /* 1 = YAHOO_CHAT_ID */
3486
3487 purple_timeout_remove(yd->yahoo_p2p_timer);
3488 if(yd->yahoo_p2p_server_timeout_handle != 0) {
3489 purple_timeout_remove(yd->yahoo_p2p_server_timeout_handle);
3490 yd->yahoo_p2p_server_timeout_handle = 0;
3491 }
3492
3493 /* close p2p server if it is waiting for a peer to connect */
3494 if (yd->yahoo_p2p_server_watcher) {
3495 purple_input_remove(yd->yahoo_p2p_server_watcher);
3496 yd->yahoo_p2p_server_watcher = 0;
3497 }
3498 if (yd->yahoo_local_p2p_server_fd >= 0) {
3499 close(yd->yahoo_local_p2p_server_fd);
3500 yd->yahoo_local_p2p_server_fd = -1;
3501 }
3502
3503 g_hash_table_destroy(yd->sms_carrier);
3504 g_hash_table_destroy(yd->peers);
3505 g_hash_table_destroy(yd->friends);
3506 g_hash_table_destroy(yd->imvironments);
3507 g_hash_table_destroy(yd->xfer_peer_idstring_map);
3508 g_free(yd->chat_name);
3509
3510 g_free(yd->cookie_y);
3511 g_free(yd->cookie_t);
3512
3513 if (yd->txhandler)
3514 purple_input_remove(yd->txhandler);
3515
3516 purple_circ_buffer_destroy(yd->txbuf);
3517
3518 if (yd->fd >= 0)
3519 close(yd->fd);
3520
3521 g_free(yd->rxqueue);
3522 yd->rxlen = 0;
3523 g_free(yd->picture_url);
3524
3525 if (yd->buddy_icon_connect_data)
3526 purple_proxy_connect_cancel(yd->buddy_icon_connect_data);
3527 if (yd->picture_upload_todo)
3528 yahoo_buddy_icon_upload_data_free(yd->picture_upload_todo);
3529 if (yd->ycht)
3530 ycht_connection_close(yd->ycht);
3531
3532 g_free(yd->pending_chat_room);
3533 g_free(yd->pending_chat_id);
3534 g_free(yd->pending_chat_topic);
3535 g_free(yd->pending_chat_goto);
3536 g_strfreev(yd->profiles);
3537
3538 g_free(yd->current_list15_grp);
3539
3540 g_free(yd);
3541 gc->proto_data = NULL;
3542 }
3543
3544 static const char *yahoo_list_icon(PurpleAccount *a, PurpleBuddy *b)
3545 {
3546 return "yahoo";
3547 }
3548
3549 static const char *yahoo_list_emblem(PurpleBuddy *b)
3550 {
3551 PurpleAccount *account;
3552 PurpleConnection *gc;
3553 struct yahoo_data *yd;
3554 YahooFriend *f;
3555 PurplePresence *presence;
3556
3557 if (!b || !(account = purple_buddy_get_account(b)) ||
3558 !(gc = purple_account_get_connection(account)) ||
3559 !(yd = gc->proto_data))
3560 return NULL;
3561
3562 f = yahoo_friend_find(gc, purple_buddy_get_name(b));
3563 if (!f) {
3564 return "not-authorized";
3565 }
3566
3567 presence = purple_buddy_get_presence(b);
3568
3569 if (purple_presence_is_online(presence)) {
3570 if (yahoo_friend_get_game(f))
3571 return "game";
3572 if (f->protocol == 2)
3573 return "msn";
3574 }
3575 return NULL;
3576 }
3577
3578 static const char *yahoo_get_status_string(enum yahoo_status a)
3579 {
3580 switch (a) {
3581 case YAHOO_STATUS_BRB:
3582 return _("Be Right Back");
3583 case YAHOO_STATUS_BUSY:
3584 return _("Busy");
3585 case YAHOO_STATUS_NOTATHOME:
3586 return _("Not at Home");
3587 case YAHOO_STATUS_NOTATDESK:
3588 return _("Not at Desk");
3589 case YAHOO_STATUS_NOTINOFFICE:
3590 return _("Not in Office");
3591 case YAHOO_STATUS_ONPHONE:
3592 return _("On the Phone");
3593 case YAHOO_STATUS_ONVACATION:
3594 return _("On Vacation");
3595 case YAHOO_STATUS_OUTTOLUNCH:
3596 return _("Out to Lunch");
3597 case YAHOO_STATUS_STEPPEDOUT:
3598 return _("Stepped Out");
3599 case YAHOO_STATUS_INVISIBLE:
3600 return _("Invisible");
3601 case YAHOO_STATUS_IDLE:
3602 return _("Idle");
3603 case YAHOO_STATUS_OFFLINE:
3604 return _("Offline");
3605 default:
3606 return _("Available");
3607 }
3608 }
3609
3610 static void yahoo_initiate_conference(PurpleBlistNode *node, gpointer data) {
3611
3612 PurpleBuddy *buddy;
3613 PurpleConnection *gc;
3614
3615 GHashTable *components;
3616 struct yahoo_data *yd;
3617 int id;
3618
3619 g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
3620
3621 buddy = (PurpleBuddy *) node;
3622 gc = purple_account_get_connection(purple_buddy_get_account(buddy));
3623 yd = gc->proto_data;
3624 id = yd->conf_id;
3625
3626 components = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
3627 g_hash_table_replace(components, g_strdup("room"),
3628 g_strdup_printf("%s-%d", purple_connection_get_display_name(gc), id));
3629 g_hash_table_replace(components, g_strdup("topic"), g_strdup("Join my conference..."));
3630 g_hash_table_replace(components, g_strdup("type"), g_strdup("Conference"));
3631 yahoo_c_join(gc, components);
3632 g_hash_table_destroy(components);
3633
3634 yahoo_c_invite(gc, id, "Join my conference...", purple_buddy_get_name(buddy));
3635 }
3636
3637 static void yahoo_presence_settings(PurpleBlistNode *node, gpointer data) {
3638 PurpleBuddy *buddy;
3639 PurpleConnection *gc;
3640 int presence_val = GPOINTER_TO_INT(data);
3641
3642 buddy = (PurpleBuddy *) node;
3643 gc = purple_account_get_connection(purple_buddy_get_account(buddy));
3644
3645 yahoo_friend_update_presence(gc, purple_buddy_get_name(buddy), presence_val);
3646 }
3647
3648 static void yahoo_game(PurpleBlistNode *node, gpointer data) {
3649
3650 PurpleBuddy *buddy;
3651 PurpleConnection *gc;
3652
3653 struct yahoo_data *yd;
3654 const char *game;
3655 char *game2;
3656 char *t;
3657 char url[256];
3658 YahooFriend *f;
3659
3660 g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
3661
3662 buddy = (PurpleBuddy *) node;
3663 gc = purple_account_get_connection(purple_buddy_get_account(buddy));
3664 yd = (struct yahoo_data *) gc->proto_data;
3665
3666 f = yahoo_friend_find(gc, purple_buddy_get_name(buddy));
3667 if (!f)
3668 return;
3669
3670 game = yahoo_friend_get_game(f);
3671 if (!game)
3672 return;
3673
3674 t = game2 = g_strdup(strstr(game, "ante?room="));
3675 while (*t && *t != '\t')
3676 t++;
3677 *t = 0;
3678 g_snprintf(url, sizeof url, "http://games.yahoo.com/games/%s", game2);
3679 purple_notify_uri(gc, url);
3680 g_free(game2);
3681 }
3682
3683 static char *yahoo_status_text(PurpleBuddy *b)
3684 {
3685 YahooFriend *f = NULL;
3686 const char *msg;
3687 char *msg2;
3688 PurpleAccount *account;
3689
3690 account = purple_buddy_get_account(b);
3691 f = yahoo_friend_find(purple_account_get_connection(account), purple_buddy_get_name(b));
3692 if (!f)
3693 return g_strdup(_("Not on server list"));
3694
3695 switch (f->status) {
3696 case YAHOO_STATUS_AVAILABLE:
3697 return NULL;
3698 case YAHOO_STATUS_IDLE:
3699 if (f->idle == -1)
3700 return g_strdup(yahoo_get_status_string(f->status));
3701 return NULL;
3702 case YAHOO_STATUS_CUSTOM:
3703 if (!(msg = yahoo_friend_get_status_message(f)))
3704 return NULL;
3705 msg2 = g_markup_escape_text(msg, strlen(msg));
3706 purple_util_chrreplace(msg2, '\n', ' ');
3707 return msg2;
3708
3709 default:
3710 return g_strdup(yahoo_get_status_string(f->status));
3711 }
3712 }
3713
3714 void yahoo_tooltip_text(PurpleBuddy *b, PurpleNotifyUserInfo *user_info, gboolean full)
3715 {
3716 YahooFriend *f;
3717 char *escaped;
3718 char *status = NULL;
3719 const char *presence = NULL;
3720 PurpleAccount *account;
3721
3722 account = purple_buddy_get_account(b);
3723 f = yahoo_friend_find(purple_account_get_connection(account), purple_buddy_get_name(b));
3724 if (!f)
3725 status = g_strdup_printf("\n%s", _("Not on server list"));
3726 else {
3727 switch (f->status) {
3728 case YAHOO_STATUS_CUSTOM:
3729 if (!yahoo_friend_get_status_message(f))
3730 return;
3731 status = g_strdup(yahoo_friend_get_status_message(f));
3732 break;
3733 case YAHOO_STATUS_OFFLINE:
3734 break;
3735 default:
3736 status = g_strdup(yahoo_get_status_string(f->status));
3737 break;
3738 }
3739
3740 switch (f->presence) {
3741 case YAHOO_PRESENCE_ONLINE:
3742 presence = _("Appear Online");
3743 break;
3744 case YAHOO_PRESENCE_PERM_OFFLINE:
3745 presence = _("Appear Permanently Offline");
3746 break;
3747 case YAHOO_PRESENCE_DEFAULT:
3748 break;
3749 default:
3750 purple_debug_error("yahoo", "Unknown presence in yahoo_tooltip_text\n");
3751 break;
3752 }
3753 }
3754
3755 if (status != NULL) {
3756 escaped = g_markup_escape_text(status, strlen(status));
3757 purple_notify_user_info_add_pair(user_info, _("Status"), escaped);
3758 g_free(status);
3759 g_free(escaped);
3760 }
3761
3762 if (presence != NULL)
3763 purple_notify_user_info_add_pair(user_info, _("Presence"), presence);
3764 }
3765
3766 static void yahoo_addbuddyfrommenu_cb(PurpleBlistNode *node, gpointer data)
3767 {
3768 PurpleBuddy *buddy;
3769 PurpleConnection *gc;
3770
3771 g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
3772
3773 buddy = (PurpleBuddy *) node;
3774 gc = purple_account_get_connection(purple_buddy_get_account(buddy));
3775
3776 yahoo_add_buddy(gc, buddy, NULL);
3777 }
3778
3779
3780 static void yahoo_chat_goto_menu(PurpleBlistNode *node, gpointer data)
3781 {
3782 PurpleBuddy *buddy;
3783 PurpleConnection *gc;
3784
3785 g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
3786
3787 buddy = (PurpleBuddy *) node;
3788 gc = purple_account_get_connection(purple_buddy_get_account(buddy));
3789
3790 yahoo_chat_goto(gc, purple_buddy_get_name(buddy));
3791 }
3792
3793 static GList *build_presence_submenu(YahooFriend *f, PurpleConnection *gc) {
3794 GList *m = NULL;
3795 PurpleMenuAction *act;
3796 struct yahoo_data *yd = (struct yahoo_data *) gc->proto_data;
3797
3798 if (yd->current_status == YAHOO_STATUS_INVISIBLE) {
3799 if (f->presence != YAHOO_PRESENCE_ONLINE) {
3800 act = purple_menu_action_new(_("Appear Online"),
3801 PURPLE_CALLBACK(yahoo_presence_settings),
3802 GINT_TO_POINTER(YAHOO_PRESENCE_ONLINE),
3803 NULL);
3804 m = g_list_append(m, act);
3805 } else if (f->presence != YAHOO_PRESENCE_DEFAULT) {
3806 act = purple_menu_action_new(_("Appear Offline"),
3807 PURPLE_CALLBACK(yahoo_presence_settings),
3808 GINT_TO_POINTER(YAHOO_PRESENCE_DEFAULT),
3809 NULL);
3810 m = g_list_append(m, act);
3811 }
3812 }
3813
3814 if (f->presence == YAHOO_PRESENCE_PERM_OFFLINE) {
3815 act = purple_menu_action_new(_("Don't Appear Permanently Offline"),
3816 PURPLE_CALLBACK(yahoo_presence_settings),
3817 GINT_TO_POINTER(YAHOO_PRESENCE_DEFAULT),
3818 NULL);
3819 m = g_list_append(m, act);
3820 } else {
3821 act = purple_menu_action_new(_("Appear Permanently Offline"),
3822 PURPLE_CALLBACK(yahoo_presence_settings),
3823 GINT_TO_POINTER(YAHOO_PRESENCE_PERM_OFFLINE),
3824 NULL);
3825 m = g_list_append(m, act);
3826 }
3827
3828 return m;
3829 }
3830
3831 static void yahoo_doodle_blist_node(PurpleBlistNode *node, gpointer data)
3832 {
3833 PurpleBuddy *b = (PurpleBuddy *)node;
3834 PurpleAccount *account = purple_buddy_get_account(b);
3835 PurpleConnection *gc = purple_account_get_connection(account);
3836
3837 yahoo_doodle_initiate(gc, purple_buddy_get_name(b));
3838 }
3839
3840 static GList *yahoo_buddy_menu(PurpleBuddy *buddy)
3841 {
3842 GList *m = NULL;
3843 PurpleMenuAction *act;
3844
3845 PurpleConnection *gc = purple_account_get_connection(purple_buddy_get_account(buddy));
3846 struct yahoo_data *yd = gc->proto_data;
3847 static char buf2[1024];
3848 YahooFriend *f;
3849
3850 f = yahoo_friend_find(gc, purple_buddy_get_name(buddy));
3851
3852 if (!f && !yd->wm) {
3853 act = purple_menu_action_new(_("Add Buddy"),
3854 PURPLE_CALLBACK(yahoo_addbuddyfrommenu_cb),
3855 NULL, NULL);
3856 m = g_list_append(m, act);
3857
3858 return m;
3859
3860 }
3861
3862 if (f && f->status != YAHOO_STATUS_OFFLINE) {
3863 if (!yd->wm) {
3864 act = purple_menu_action_new(_("Join in Chat"),
3865 PURPLE_CALLBACK(yahoo_chat_goto_menu),
3866 NULL, NULL);
3867 m = g_list_append(m, act);
3868 }
3869
3870 act = purple_menu_action_new(_("Initiate Conference"),
3871 PURPLE_CALLBACK(yahoo_initiate_conference),
3872 NULL, NULL);
3873 m = g_list_append(m, act);
3874
3875 if (yahoo_friend_get_game(f)) {
3876 const char *game = yahoo_friend_get_game(f);
3877 char *room;
3878 char *t;
3879
3880 if ((room = strstr(game, "&follow="))) {/* skip ahead to the url */
3881 while (*room && *room != '\t') /* skip to the tab */
3882 room++;
3883 t = room++; /* room as now at the name */
3884 while (*t != '\n')
3885 t++; /* replace the \n with a space */
3886 *t = ' ';
3887 g_snprintf(buf2, sizeof buf2, "%s", room);
3888
3889 act = purple_menu_action_new(buf2,
3890 PURPLE_CALLBACK(yahoo_game),
3891 NULL, NULL);
3892 m = g_list_append(m, act);
3893 }
3894 }
3895 }
3896
3897 if (f) {
3898 act = purple_menu_action_new(_("Presence Settings"), NULL, NULL,
3899 build_presence_submenu(f, gc));
3900 m = g_list_append(m, act);
3901 }
3902
3903 if (f) {
3904 act = purple_menu_action_new(_("Start Doodling"),
3905 PURPLE_CALLBACK(yahoo_doodle_blist_node),
3906 NULL, NULL);
3907 m = g_list_append(m, act);
3908 }
3909
3910 return m;
3911 }
3912
3913 static GList *yahoo_blist_node_menu(PurpleBlistNode *node)
3914 {
3915 if(PURPLE_BLIST_NODE_IS_BUDDY(node)) {
3916 return yahoo_buddy_menu((PurpleBuddy *) node);
3917 } else {
3918 return NULL;
3919 }
3920 }
3921
3922 static void yahoo_act_id(PurpleConnection *gc, PurpleRequestFields *fields)
3923 {
3924 struct yahoo_data *yd = gc->proto_data;
3925 const char *name = yd->profiles[purple_request_fields_get_choice(fields, "id")];
3926
3927 struct yahoo_packet *pkt = yahoo_packet_new(YAHOO_SERVICE_IDACT, YAHOO_STATUS_AVAILABLE, 0);
3928 yahoo_packet_hash_str(pkt, 3, name);
3929 yahoo_packet_send_and_free(pkt, yd);
3930
3931 purple_connection_set_display_name(gc, name);
3932 }
3933
3934 static void
3935 yahoo_get_inbox_token_cb(PurpleUtilFetchUrlData *url_data, gpointer user_data,
3936 const gchar *token, size_t len, const gchar *error_message)
3937 {
3938 PurpleConnection *gc = user_data;
3939 gboolean set_cookie = FALSE;
3940 gchar *url;
3941 struct yahoo_data *yd = gc->proto_data;
3942
3943 g_return_if_fail(PURPLE_CONNECTION_IS_VALID(gc));
3944
3945 yd->url_datas = g_slist_remove(yd->url_datas, url_data);
3946
3947 if (error_message != NULL)
3948 purple_debug_error("yahoo", "Requesting mail login token failed: %s\n", error_message);
3949 else if (len > 0 && token && *token) {
3950 /* Should we not be hardcoding the rd url? */
3951 url = g_strdup_printf(
3952 "http://login.yahoo.com/config/reset_cookies_token?"
3953 ".token=%s"
3954 "&.done=http://us.rd.yahoo.com/messenger/client/%%3fhttp://mail.yahoo.com/",
3955 token);
3956 set_cookie = TRUE;
3957 }
3958
3959 if (!set_cookie) {
3960 purple_debug_error("yahoo", "No mail login token; forwarding to login screen.\n");
3961 url = g_strdup(yd->jp ? YAHOOJP_MAIL_URL : YAHOO_MAIL_URL);
3962 }
3963
3964 /* Open the mailbox with the parsed url data */
3965 purple_notify_uri(gc, url);
3966
3967 g_free(url);
3968 }
3969
3970
3971 static void yahoo_show_inbox(PurplePluginAction *action)
3972 {
3973 /* Setup a cookie that can be used by the browser */
3974 /* XXX I have no idea how this will work with Yahoo! Japan. */
3975
3976 PurpleConnection *gc = action->context;
3977 struct yahoo_data *yd = gc->proto_data;
3978
3979 PurpleUtilFetchUrlData *url_data;
3980 const char* base_url = "http://login.yahoo.com";
3981 /* use whole URL if using HTTP Proxy */
3982 gboolean use_whole_url = yahoo_account_use_http_proxy(gc);
3983 gchar *request = g_strdup_printf(
3984 "POST %s/config/cookie_token HTTP/1.0\r\n"
3985 "Cookie: T=%s; path=/; domain=.yahoo.com; Y=%s;\r\n"
3986 "User-Agent: " YAHOO_CLIENT_USERAGENT "\r\n"
3987 "Host: login.yahoo.com\r\n"
3988 "Content-Length: 0\r\n\r\n",
3989 use_whole_url ? base_url : "",
3990 yd->cookie_t, yd->cookie_y);
3991
3992 url_data = purple_util_fetch_url_request_len_with_account(
3993 purple_connection_get_account(gc), base_url, use_whole_url,
3994 YAHOO_CLIENT_USERAGENT, TRUE, request, FALSE, -1,
3995 yahoo_get_inbox_token_cb, gc);
3996
3997 g_free(request);
3998
3999 if (url_data != NULL)
4000 yd->url_datas = g_slist_prepend(yd->url_datas, url_data);
4001 else {
4002 const char *yahoo_mail_url = (yd->jp ? YAHOOJP_MAIL_URL : YAHOO_MAIL_URL);
4003 purple_debug_error("yahoo",
4004 "Unable to request mail login token; forwarding to login screen.");
4005 purple_notify_uri(gc, yahoo_mail_url);
4006 }
4007
4008 }
4009
4010
4011 static void yahoo_show_act_id(PurplePluginAction *action)
4012 {
4013 PurpleRequestFields *fields;
4014 PurpleRequestFieldGroup *group;
4015 PurpleRequestField *field;
4016 PurpleConnection *gc = (PurpleConnection *) action->context;
4017 struct yahoo_data *yd = purple_connection_get_protocol_data(gc);
4018 const char *name = purple_connection_get_display_name(gc);
4019 int iter;
4020
4021 fields = purple_request_fields_new();
4022 group = purple_request_field_group_new(NULL);
4023 purple_request_fields_add_group(fields, group);
4024 field = purple_request_field_choice_new("id", "Activate which ID?", 0);
4025 purple_request_field_group_add_field(group, field);
4026
4027 for (iter = 0; yd->profiles[iter]; iter++) {
4028 purple_request_field_choice_add(field, yd->profiles[iter]);
4029 if (purple_strequal(yd->profiles[iter], name))
4030 purple_request_field_choice_set_default_value(field, iter);
4031 }
4032
4033 purple_request_fields(gc, NULL, _("Select the ID you want to activate"), NULL,
4034 fields,
4035 _("OK"), G_CALLBACK(yahoo_act_id),
4036 _("Cancel"), NULL,
4037 purple_connection_get_account(gc), NULL, NULL,
4038 gc);
4039 }
4040
4041 static void yahoo_show_chat_goto(PurplePluginAction *action)
4042 {
4043 PurpleConnection *gc = (PurpleConnection *) action->context;
4044 purple_request_input(gc, NULL, _("Join whom in chat?"), NULL,
4045 "", FALSE, FALSE, NULL,
4046 _("OK"), G_CALLBACK(yahoo_chat_goto),
4047 _("Cancel"), NULL,
4048 purple_connection_get_account(gc), NULL, NULL,
4049 gc);
4050 }
4051
4052 static GList *yahoo_actions(PurplePlugin *plugin, gpointer context) {
4053 GList *m = NULL;
4054 PurplePluginAction *act;
4055
4056 act = purple_plugin_action_new(_("Activate ID..."),
4057 yahoo_show_act_id);
4058 m = g_list_append(m, act);
4059
4060 act = purple_plugin_action_new(_("Join User in Chat..."),
4061 yahoo_show_chat_goto);
4062 m = g_list_append(m, act);
4063
4064 m = g_list_append(m, NULL);
4065 act = purple_plugin_action_new(_("Open Inbox"),
4066 yahoo_show_inbox);
4067 m = g_list_append(m, act);
4068
4069 return m;
4070 }
4071
4072 struct yahoo_sms_carrier_cb_data {
4073 PurpleConnection *gc;
4074 char *who;
4075 char *what;
4076 };
4077
4078 static int yahoo_send_im(PurpleConnection *gc, const char *who, const char *what, PurpleMessageFlags flags);
4079
4080 static void yahoo_get_sms_carrier_cb(PurpleUtilFetchUrlData *url_data, gpointer user_data,
4081 const gchar *webdata, size_t len, const gchar *error_message)
4082 {
4083 struct yahoo_sms_carrier_cb_data *sms_cb_data = user_data;
4084 PurpleConnection *gc = sms_cb_data->gc;
4085 struct yahoo_data *yd = gc->proto_data;
4086 char *mobile_no = NULL;
4087 char *status = NULL;
4088 char *carrier = NULL;
4089 PurpleAccount *account = purple_connection_get_account(gc);
4090 PurpleConversation *conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, sms_cb_data->who, account);
4091
4092 if (error_message != NULL) {
4093 purple_conversation_write(conv, NULL, "Cant send SMS, Unable to obtain mobile carrier", PURPLE_MESSAGE_SYSTEM, time(NULL));
4094
4095 g_free(sms_cb_data->who);
4096 g_free(sms_cb_data->what);
4097 g_free(sms_cb_data);
4098 return ;
4099 }
4100 else if (len > 0 && webdata && *webdata) {
4101 xmlnode *validate_data_root = xmlnode_from_str(webdata, -1);
4102 xmlnode *validate_data_child = xmlnode_get_child(validate_data_root, "mobile_no");
4103 mobile_no = (char *)xmlnode_get_attrib(validate_data_child, "msisdn");
4104
4105 validate_data_root = xmlnode_copy(validate_data_child);
4106 validate_data_child = xmlnode_get_child(validate_data_root, "status");
4107 status = xmlnode_get_data(validate_data_child);
4108
4109 validate_data_child = xmlnode_get_child(validate_data_root, "carrier");
4110 carrier = xmlnode_get_data(validate_data_child);
4111
4112 purple_debug_info("yahoo","SMS validate data: Mobile:%s, Status:%s, Carrier:%s\n", mobile_no, status, carrier);
4113
4114 if( strcmp(status, "Valid") == 0) {
4115 g_hash_table_insert(yd->sms_carrier, g_strdup_printf("+%s", mobile_no), g_strdup(carrier));
4116 yahoo_send_im(sms_cb_data->gc, sms_cb_data->who, sms_cb_data->what, PURPLE_MESSAGE_SEND);
4117 }
4118 else {
4119 g_hash_table_insert(yd->sms_carrier, g_strdup_printf("+%s", mobile_no), g_strdup("Unknown"));
4120 purple_conversation_write(conv, NULL, "Cant send SMS, Unknown mobile carrier", PURPLE_MESSAGE_SYSTEM, time(NULL));
4121 }
4122
4123 xmlnode_free(validate_data_child);
4124 xmlnode_free(validate_data_root);
4125 g_free(sms_cb_data->who);
4126 g_free(sms_cb_data->what);
4127 g_free(sms_cb_data);
4128 g_free(mobile_no);
4129 g_free(status);
4130 g_free(carrier);
4131 }
4132 }
4133
4134 static void yahoo_get_sms_carrier(PurpleConnection *gc, gpointer data)
4135 {
4136 struct yahoo_data *yd = gc->proto_data;
4137 PurpleUtilFetchUrlData *url_data;
4138 struct yahoo_sms_carrier_cb_data *sms_cb_data;
4139 char *validate_request_str = NULL;
4140 char *request = NULL;
4141 gboolean use_whole_url = FALSE;
4142 xmlnode *validate_request_root = NULL;
4143 xmlnode *validate_request_child = NULL;
4144
4145 if(!(sms_cb_data = data))
4146 return;
4147
4148 validate_request_root = xmlnode_new("validate");
4149 xmlnode_set_attrib(validate_request_root, "intl", "us");
4150 xmlnode_set_attrib(validate_request_root, "version", YAHOO_CLIENT_VERSION);
4151 xmlnode_set_attrib(validate_request_root, "qos", "0");
4152
4153 validate_request_child = xmlnode_new_child(validate_request_root, "mobile_no");
4154 xmlnode_set_attrib(validate_request_child, "msisdn", sms_cb_data->who + 1);
4155
4156 validate_request_str = xmlnode_to_str(validate_request_root, NULL);
4157
4158 xmlnode_free(validate_request_child);
4159 xmlnode_free(validate_request_root);
4160
4161 request = g_strdup_printf(
4162 "POST /mobileno?intl=us&version=%s HTTP/1.1\r\n"
4163 "Cookie: T=%s; path=/; domain=.yahoo.com; Y=%s; path=/; domain=.yahoo.com;\r\n"
4164 "User-Agent: " YAHOO_CLIENT_USERAGENT "\r\n"
4165 "Host: validate.msg.yahoo.com\r\n"
4166 "Content-Length: %" G_GSIZE_FORMAT "\r\n"
4167 "Cache-Control: no-cache\r\n\r\n%s",
4168 YAHOO_CLIENT_VERSION, yd->cookie_t, yd->cookie_y, strlen(validate_request_str), validate_request_str);
4169
4170 /* use whole URL if using HTTP Proxy */
4171 if ((gc->account->proxy_info) && (gc->account->proxy_info->type == PURPLE_PROXY_HTTP))
4172 use_whole_url = TRUE;
4173
4174 url_data = purple_util_fetch_url_request_len_with_account(
4175 purple_connection_get_account(gc), YAHOO_SMS_CARRIER_URL, use_whole_url,
4176 YAHOO_CLIENT_USERAGENT, TRUE, request, FALSE, -1,
4177 yahoo_get_sms_carrier_cb, data);
4178
4179 g_free(request);
4180 g_free(validate_request_str);
4181
4182 if (!url_data) {
4183 PurpleAccount *account = purple_connection_get_account(gc);
4184 PurpleConversation *conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, sms_cb_data->who, account);
4185 purple_conversation_write(conv, NULL, "Cant send SMS, Unable to obtain mobile carrier", PURPLE_MESSAGE_SYSTEM, time(NULL));
4186 g_free(sms_cb_data->who);
4187 g_free(sms_cb_data->what);
4188 g_free(sms_cb_data);
4189 }
4190 }
4191
4192 static int yahoo_send_im(PurpleConnection *gc, const char *who, const char *what, PurpleMessageFlags flags)
4193 {
4194 struct yahoo_data *yd = gc->proto_data;
4195 struct yahoo_packet *pkt = NULL;
4196 char *msg = yahoo_html_to_codes(what);
4197 char *msg2;
4198 gboolean utf8 = TRUE;
4199 PurpleWhiteboard *wb;
4200 int ret = 1;
4201 YahooFriend *f = NULL;
4202 gsize lenb = 0;
4203 glong lenc = 0;
4204 struct yahoo_p2p_data *p2p_data;
4205 gboolean msn = FALSE;
4206 msg2 = yahoo_string_encode(gc, msg, &utf8);
4207
4208 if(msg2) {
4209 lenb = strlen(msg2);
4210 lenc = g_utf8_strlen(msg2, -1);
4211
4212 if(lenb > YAHOO_MAX_MESSAGE_LENGTH_BYTES || lenc > YAHOO_MAX_MESSAGE_LENGTH_CHARS) {
4213 purple_debug_info("yahoo", "Message too big. Length is %" G_GSIZE_FORMAT
4214 " bytes, %ld characters. Max is %d bytes, %d chars."
4215 " Message is '%s'.\n", lenb, lenc, YAHOO_MAX_MESSAGE_LENGTH_BYTES,
4216 YAHOO_MAX_MESSAGE_LENGTH_CHARS, msg2);
4217 g_free(msg);
4218 g_free(msg2);
4219 return -E2BIG;
4220 }
4221 }
4222
4223 msn = !g_strncasecmp(who, "msn/", 4);
4224
4225 if( strncmp(who, "+", 1) == 0 ) {
4226 /* we have an sms to be sent */
4227 gchar *carrier = NULL;
4228 const char *alias = NULL;
4229 PurpleAccount *account = purple_connection_get_account(gc);
4230 PurpleConversation *conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, who, account);
4231
4232 carrier = g_hash_table_lookup(yd->sms_carrier, who);
4233 if (!carrier) {
4234 struct yahoo_sms_carrier_cb_data *sms_cb_data;
4235 sms_cb_data = g_malloc(sizeof(struct yahoo_sms_carrier_cb_data));
4236 sms_cb_data->gc = gc;
4237 sms_cb_data->who = g_strdup(who);
4238 sms_cb_data->what = g_strdup(what);
4239
4240 purple_conversation_write(conv, NULL, "Getting mobile carrier to send the sms", PURPLE_MESSAGE_SYSTEM, time(NULL));
4241
4242 yahoo_get_sms_carrier(gc, sms_cb_data);
4243
4244 g_free(msg);
4245 g_free(msg2);
4246 return ret;
4247 }
4248 else if( strcmp(carrier,"Unknown") == 0 ) {
4249 purple_conversation_write(conv, NULL, "Cant send SMS, Unknown mobile carrier", PURPLE_MESSAGE_SYSTEM, time(NULL));
4250
4251 g_free(msg);
4252 g_free(msg2);
4253 return -1;
4254 }
4255
4256 alias = purple_account_get_alias(account);
4257 pkt = yahoo_packet_new(YAHOO_SERVICE_SMS_MSG, YAHOO_STATUS_AVAILABLE, 0);
4258 yahoo_packet_hash(pkt, "sssss",
4259 1, purple_connection_get_display_name(gc),
4260 69, alias,
4261 5, who + 1,
4262 68, carrier,
4263 14, msg2);
4264 yahoo_packet_send_and_free(pkt, yd);
4265
4266 g_free(msg);
4267 g_free(msg2);
4268
4269 return ret;
4270 }
4271
4272 pkt = yahoo_packet_new(YAHOO_SERVICE_MESSAGE, YAHOO_STATUS_OFFLINE, 0);
4273 if(msn) {
4274 yahoo_packet_hash(pkt, "ss", 1, purple_connection_get_display_name(gc), 5, who+4);
4275 yahoo_packet_hash_int(pkt, 241, 2);
4276 }
4277 else {
4278 yahoo_packet_hash(pkt, "ss", 1, purple_connection_get_display_name(gc), 5, who);
4279 if ((f = yahoo_friend_find(gc, who)) && f->protocol)
4280 yahoo_packet_hash_int(pkt, 241, f->protocol);
4281 }
4282
4283 if (utf8)
4284 yahoo_packet_hash_str(pkt, 97, "1");
4285 yahoo_packet_hash_str(pkt, 14, msg2);
4286
4287 /*
4288 * IMVironment.
4289 *
4290 * If this message is to a user who is also Doodling with the local user,
4291 * format the chat packet with the correct IMV information (thanks Yahoo!)
4292 *
4293 * Otherwise attempt to use the same IMVironment as the remote user,
4294 * just so that we don't inadvertantly reset their IMVironment back
4295 * to nothing.
4296 *
4297 * If they have no set an IMVironment, then use the default.
4298 */
4299 wb = purple_whiteboard_get_session(gc->account, who);
4300 if (wb)
4301 yahoo_packet_hash_str(pkt, 63, DOODLE_IMV_KEY);
4302 else
4303 {
4304 const char *imv;
4305 imv = g_hash_table_lookup(yd->imvironments, who);
4306 if (imv != NULL)
4307 yahoo_packet_hash_str(pkt, 63, imv);
4308 else
4309 yahoo_packet_hash_str(pkt, 63, ";0");
4310 }
4311
4312 yahoo_packet_hash_str(pkt, 64, "0"); /* no idea */
4313 yahoo_packet_hash_str(pkt, 1002, "1"); /* no idea, Yahoo 6 or later only it seems */
4314 if (!yd->picture_url)
4315 yahoo_packet_hash_str(pkt, 206, "0"); /* 0 = no picture, 2 = picture, maybe 1 = avatar? */
4316 else
4317 yahoo_packet_hash_str(pkt, 206, "2");
4318
4319 /* We may need to not send any packets over 2000 bytes, but I'm not sure yet. */
4320 if ((YAHOO_PACKET_HDRLEN + yahoo_packet_length(pkt)) <= 2000) {
4321 /* if p2p link exists, send through it. To-do: key 15, time value to be sent in case of p2p */
4322 if( (p2p_data = g_hash_table_lookup(yd->peers, who)) && !msn ) {
4323 yahoo_packet_hash_int(pkt, 11, p2p_data->session_id);
4324 yahoo_p2p_write_pkt(p2p_data->source, pkt);
4325 }
4326 else {
4327 yahoo_packet_send(pkt, yd);
4328 if(!msn)
4329 yahoo_send_p2p_pkt(gc, who, 0); /* send p2p packet, with val_13=0 */
4330 }
4331 }
4332 else
4333 ret = -E2BIG;
4334
4335 yahoo_packet_free(pkt);
4336
4337 g_free(msg);
4338 g_free(msg2);
4339
4340 return ret;
4341 }
4342
4343 static unsigned int yahoo_send_typing(PurpleConnection *gc, const char *who, PurpleTypingState state)
4344 {
4345 struct yahoo_data *yd = gc->proto_data;
4346 struct yahoo_p2p_data *p2p_data;
4347 gboolean msn = !g_strncasecmp(who, "msn/", 4);
4348 struct yahoo_packet *pkt = NULL;
4349
4350 /* Don't do anything if sms is being typed */
4351 if( strncmp(who, "+", 1) == 0 )
4352 return 0;
4353
4354 pkt = yahoo_packet_new(YAHOO_SERVICE_NOTIFY, YAHOO_STATUS_TYPING, 0);
4355
4356 /* check to see if p2p link exists, send through it */
4357 if( (p2p_data = g_hash_table_lookup(yd->peers, who)) && !msn ) {
4358 yahoo_packet_hash(pkt, "sssssis", 49, "TYPING", 1, purple_connection_get_display_name(gc),
4359 14, " ", 13, state == PURPLE_TYPING ? "1" : "0",
4360 5, who, 11, p2p_data->session_id, 1002, "1"); /* To-do: key 15 to be sent in case of p2p */
4361 yahoo_p2p_write_pkt(p2p_data->source, pkt);
4362 yahoo_packet_free(pkt);
4363 }
4364 else { /* send through yahoo server */
4365 if(msn)
4366 yahoo_packet_hash(pkt, "sssssss", 49, "TYPING", 1, purple_connection_get_display_name(gc),
4367 14, " ", 13, state == PURPLE_TYPING ? "1" : "0",
4368 5, who+4, 1002, "1", 241, "2");
4369 else
4370 yahoo_packet_hash(pkt, "ssssss", 49, "TYPING", 1, purple_connection_get_display_name(gc),
4371 14, " ", 13, state == PURPLE_TYPING ? "1" : "0",
4372 5, who+4, 1002, "1");
4373 yahoo_packet_send_and_free(pkt, yd);
4374 }
4375
4376 return 0;
4377 }
4378
4379 static void yahoo_session_presence_remove(gpointer key, gpointer value, gpointer data)
4380 {
4381 YahooFriend *f = value;
4382 if (f && f->presence == YAHOO_PRESENCE_ONLINE)
4383 f->presence = YAHOO_PRESENCE_DEFAULT;
4384 }
4385
4386 static void yahoo_set_status(PurpleAccount *account, PurpleStatus *status)
4387 {
4388 PurpleConnection *gc;
4389 PurplePresence *presence;
4390 struct yahoo_data *yd;
4391 struct yahoo_packet *pkt;
4392 int old_status;
4393 const char *msg = NULL;
4394 char *tmp = NULL;
4395 char *conv_msg = NULL;
4396 gboolean utf8 = TRUE;
4397
4398 if (!purple_status_is_active(status))
4399 return;
4400
4401 gc = purple_account_get_connection(account);
4402 presence = purple_status_get_presence(status);
4403 yd = (struct yahoo_data *)gc->proto_data;
4404 old_status = yd->current_status;
4405
4406 yd->current_status = get_yahoo_status_from_purple_status(status);
4407
4408 if (yd->current_status == YAHOO_STATUS_CUSTOM)
4409 {
4410 msg = purple_status_get_attr_string(status, "message");
4411
4412 if (purple_status_is_available(status)) {
4413 tmp = yahoo_string_encode(gc, msg, &utf8);
4414 conv_msg = purple_markup_strip_html(tmp);
4415 g_free(tmp);
4416 } else {
4417 if ((msg == NULL) || (*msg == '\0'))
4418 msg = _("Away");
4419 tmp = yahoo_string_encode(gc, msg, &utf8);
4420 conv_msg = purple_markup_strip_html(tmp);
4421 g_free(tmp);
4422 }
4423 }
4424
4425 if (yd->current_status == YAHOO_STATUS_INVISIBLE) {
4426 pkt = yahoo_packet_new(YAHOO_SERVICE_Y6_VISIBLE_TOGGLE, YAHOO_STATUS_AVAILABLE, 0);
4427 yahoo_packet_hash_str(pkt, 13, "2");
4428 yahoo_packet_send_and_free(pkt, yd);
4429
4430 return;
4431 }
4432
4433 pkt = yahoo_packet_new(YAHOO_SERVICE_Y6_STATUS_UPDATE, YAHOO_STATUS_AVAILABLE, 0);
4434 yahoo_packet_hash_int(pkt, 10, yd->current_status);
4435
4436 if (yd->current_status == YAHOO_STATUS_CUSTOM) {
4437 yahoo_packet_hash_str(pkt, 97, utf8 ? "1" : 0);
4438 yahoo_packet_hash_str(pkt, 19, conv_msg);
4439 } else {
4440 yahoo_packet_hash_str(pkt, 19, "");
4441 }
4442
4443 g_free(conv_msg);
4444
4445 if (purple_presence_is_idle(presence))
4446 yahoo_packet_hash_str(pkt, 47, "2");
4447 else if (!purple_status_is_available(status))
4448 yahoo_packet_hash_str(pkt, 47, "1");
4449
4450 yahoo_packet_send_and_free(pkt, yd);
4451
4452 if (old_status == YAHOO_STATUS_INVISIBLE) {
4453 pkt = yahoo_packet_new(YAHOO_SERVICE_Y6_VISIBLE_TOGGLE, YAHOO_STATUS_AVAILABLE, 0);
4454 yahoo_packet_hash_str(pkt, 13, "1");
4455 yahoo_packet_send_and_free(pkt, yd);
4456
4457 /* Any per-session presence settings are removed */
4458 g_hash_table_foreach(yd->friends, yahoo_session_presence_remove, NULL);
4459
4460 }
4461 }
4462
4463 static void yahoo_set_idle(PurpleConnection *gc, int idle)
4464 {
4465 struct yahoo_data *yd = gc->proto_data;
4466 struct yahoo_packet *pkt = NULL;
4467 char *msg = NULL, *msg2 = NULL;
4468 PurpleStatus *status = NULL;
4469
4470 if (idle && yd->current_status != YAHOO_STATUS_CUSTOM)
4471 yd->current_status = YAHOO_STATUS_IDLE;
4472 else if (!idle && yd->current_status == YAHOO_STATUS_IDLE) {
4473 status = purple_presence_get_active_status(purple_account_get_presence(purple_connection_get_account(gc)));
4474 yd->current_status = get_yahoo_status_from_purple_status(status);
4475 }
4476
4477 pkt = yahoo_packet_new(YAHOO_SERVICE_Y6_STATUS_UPDATE, YAHOO_STATUS_AVAILABLE, 0);
4478
4479 yahoo_packet_hash_int(pkt, 10, yd->current_status);
4480 if (yd->current_status == YAHOO_STATUS_CUSTOM) {
4481 const char *tmp;
4482 if (status == NULL)
4483 status = purple_presence_get_active_status(purple_account_get_presence(purple_connection_get_account(gc)));
4484 tmp = purple_status_get_attr_string(status, "message");
4485 if (tmp != NULL) {
4486 gboolean utf8 = TRUE;
4487 msg = yahoo_string_encode(gc, tmp, &utf8);
4488 msg2 = purple_markup_strip_html(msg);
4489 yahoo_packet_hash_str(pkt, 97, utf8 ? "1" : 0);
4490 yahoo_packet_hash_str(pkt, 19, msg2);
4491 } else {
4492 /* get_yahoo_status_from_purple_status() returns YAHOO_STATUS_CUSTOM for
4493 * the generic away state (YAHOO_STATUS_TYPE_AWAY) with no message */
4494 yahoo_packet_hash_str(pkt, 19, _("Away"));
4495 }
4496 } else {
4497 yahoo_packet_hash_str(pkt, 19, "");
4498 }
4499
4500 if (idle)
4501 yahoo_packet_hash_str(pkt, 47, "2");
4502 else if (!purple_presence_is_available(purple_account_get_presence(purple_connection_get_account(gc))))
4503 yahoo_packet_hash_str(pkt, 47, "1");
4504
4505 yahoo_packet_send_and_free(pkt, yd);
4506
4507 g_free(msg);
4508 g_free(msg2);
4509 }
4510
4511 static GList *yahoo_status_types(PurpleAccount *account)
4512 {
4513 PurpleStatusType *type;
4514 GList *types = NULL;
4515
4516 type = purple_status_type_new_with_attrs(PURPLE_STATUS_AVAILABLE, YAHOO_STATUS_TYPE_AVAILABLE,
4517 NULL, TRUE, TRUE, FALSE,
4518 "message", _("Message"),
4519 purple_value_new(PURPLE_TYPE_STRING), NULL);
4520 types = g_list_append(types, type);
4521
4522 type = purple_status_type_new_with_attrs(PURPLE_STATUS_AWAY, YAHOO_STATUS_TYPE_AWAY,
4523 NULL, TRUE, TRUE, FALSE,
4524 "message", _("Message"),
4525 purple_value_new(PURPLE_TYPE_STRING), NULL);
4526 types = g_list_append(types, type);
4527
4528 type = purple_status_type_new(PURPLE_STATUS_AWAY, YAHOO_STATUS_TYPE_BRB, _("Be Right Back"), TRUE);
4529 types = g_list_append(types, type);
4530
4531 type = purple_status_type_new(PURPLE_STATUS_UNAVAILABLE, YAHOO_STATUS_TYPE_BUSY, _("Busy"), TRUE);
4532 types = g_list_append(types, type);
4533
4534 type = purple_status_type_new(PURPLE_STATUS_AWAY, YAHOO_STATUS_TYPE_NOTATHOME, _("Not at Home"), TRUE);
4535 types = g_list_append(types, type);
4536
4537 type = purple_status_type_new(PURPLE_STATUS_AWAY, YAHOO_STATUS_TYPE_NOTATDESK, _("Not at Desk"), TRUE);
4538 types = g_list_append(types, type);
4539
4540 type = purple_status_type_new(PURPLE_STATUS_AWAY, YAHOO_STATUS_TYPE_NOTINOFFICE, _("Not in Office"), TRUE);
4541 types = g_list_append(types, type);
4542
4543 type = purple_status_type_new(PURPLE_STATUS_UNAVAILABLE, YAHOO_STATUS_TYPE_ONPHONE, _("On the Phone"), TRUE);
4544 types = g_list_append(types, type);
4545
4546 type = purple_status_type_new(PURPLE_STATUS_EXTENDED_AWAY, YAHOO_STATUS_TYPE_ONVACATION, _("On Vacation"), TRUE);
4547 types = g_list_append(types, type);
4548
4549 type = purple_status_type_new(PURPLE_STATUS_AWAY, YAHOO_STATUS_TYPE_OUTTOLUNCH, _("Out to Lunch"), TRUE);
4550 types = g_list_append(types, type);
4551
4552 type = purple_status_type_new(PURPLE_STATUS_AWAY, YAHOO_STATUS_TYPE_STEPPEDOUT, _("Stepped Out"), TRUE);
4553 types = g_list_append(types, type);
4554
4555
4556 type = purple_status_type_new(PURPLE_STATUS_INVISIBLE, YAHOO_STATUS_TYPE_INVISIBLE, NULL, TRUE);
4557 types = g_list_append(types, type);
4558
4559 type = purple_status_type_new(PURPLE_STATUS_OFFLINE, YAHOO_STATUS_TYPE_OFFLINE, NULL, TRUE);
4560 types = g_list_append(types, type);
4561
4562 type = purple_status_type_new_full(PURPLE_STATUS_MOBILE, YAHOO_STATUS_TYPE_MOBILE, NULL, FALSE, FALSE, TRUE);
4563 types = g_list_append(types, type);
4564
4565 return types;
4566 }
4567
4568 static void yahoo_keepalive(PurpleConnection *gc)
4569 {
4570 struct yahoo_packet *pkt;
4571 struct yahoo_data *yd = gc->proto_data;
4572 time_t now = time(NULL);
4573
4574 /* We're only allowed to send a ping once an hour or the servers will boot us */
4575 if ((now - yd->last_ping) >= PING_TIMEOUT) {
4576 yd->last_ping = now;
4577
4578 /* The native client will only send PING or CHATPING */
4579 if (yd->chat_online) {
4580 if (yd->wm) {
4581 ycht_chat_send_keepalive(yd->ycht);
4582 } else {
4583 pkt = yahoo_packet_new(YAHOO_SERVICE_CHATPING, YAHOO_STATUS_AVAILABLE, 0);
4584 yahoo_packet_hash_str(pkt, 109, purple_connection_get_display_name(gc));
4585 yahoo_packet_send_and_free(pkt, yd);
4586 }
4587 } else {
4588 pkt = yahoo_packet_new(YAHOO_SERVICE_PING, YAHOO_STATUS_AVAILABLE, 0);
4589 yahoo_packet_send_and_free(pkt, yd);
4590 }
4591 }
4592
4593 if ((now - yd->last_keepalive) >= KEEPALIVE_TIMEOUT) {
4594 yd->last_keepalive = now;
4595 pkt = yahoo_packet_new(YAHOO_SERVICE_KEEPALIVE, YAHOO_STATUS_AVAILABLE, 0);
4596 yahoo_packet_hash_str(pkt, 0, purple_connection_get_display_name(gc));
4597 yahoo_packet_send_and_free(pkt, yd);
4598 }
4599
4600 }
4601
4602 static void yahoo_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *g)
4603 {
4604 struct yahoo_data *yd = (struct yahoo_data *)gc->proto_data;
4605 struct yahoo_packet *pkt;
4606 const char *group = NULL;
4607 char *group2;
4608 YahooFriend *f;
4609 const char *bname;
4610 gboolean msn = FALSE;
4611
4612 if (!yd->logged_in)
4613 return;
4614
4615 bname = purple_buddy_get_name(buddy);
4616 if (!purple_privacy_check(purple_connection_get_account(gc), bname))
4617 return;
4618
4619 f = yahoo_friend_find(gc, bname);
4620 msn = !g_strncasecmp(bname, "msn/", 4);
4621
4622 g = purple_buddy_get_group(buddy);
4623 if (g)
4624 group = purple_group_get_name(g);
4625 else
4626 group = "Buddies";
4627
4628 group2 = yahoo_string_encode(gc, group, NULL);
4629 pkt = yahoo_packet_new(YAHOO_SERVICE_ADDBUDDY, YAHOO_STATUS_AVAILABLE, 0);
4630 if(msn) {
4631 yahoo_packet_hash(pkt, "sssssssssss",
4632 14, "",
4633 65, group2,
4634 97, "1",
4635 1, purple_connection_get_display_name(gc),
4636 302, "319",
4637 300, "319",
4638 7, bname + 4,
4639 241, "2",
4640 334, "0",
4641 301, "319",
4642 303, "319"
4643 );
4644 }
4645 else {
4646 yahoo_packet_hash(pkt, "ssssssssss",
4647 14, "",
4648 65, group2,
4649 97, "1",
4650 1, purple_connection_get_display_name(gc),
4651 302, "319",
4652 300, "319",
4653 7, bname,
4654 334, "0",
4655 301, "319",
4656 303, "319"
4657 );
4658 }
4659 if (f && f->protocol && !msn)
4660 yahoo_packet_hash_int(pkt, 241, f->protocol);
4661
4662 yahoo_packet_send_and_free(pkt, yd);
4663 g_free(group2);
4664 }
4665
4666 static void yahoo_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group)
4667 {
4668 struct yahoo_data *yd = (struct yahoo_data *)gc->proto_data;
4669 struct yahoo_packet *pkt;
4670 GSList *buddies, *l;
4671 PurpleGroup *g;
4672 gboolean remove = TRUE;
4673 char *cg;
4674 const char *bname, *gname;
4675 YahooFriend *f = NULL;
4676 gboolean msn = FALSE;
4677
4678 bname = purple_buddy_get_name(buddy);
4679 f = yahoo_friend_find(gc, bname);
4680 if (!f)
4681 return;
4682
4683 gname = purple_group_get_name(group);
4684 buddies = purple_find_buddies(purple_connection_get_account(gc), bname);
4685 if(f->protocol == 2)
4686 msn = TRUE;
4687 for (l = buddies; l; l = l->next) {
4688 g = purple_buddy_get_group(l->data);
4689 if (purple_utf8_strcasecmp(gname, purple_group_get_name(g))) {
4690 remove = FALSE;
4691 break;
4692 }
4693 }
4694
4695 g_slist_free(buddies);
4696
4697 if (remove)
4698 g_hash_table_remove(yd->friends, bname);
4699
4700 cg = yahoo_string_encode(gc, gname, NULL);
4701 pkt = yahoo_packet_new(YAHOO_SERVICE_REMBUDDY, YAHOO_STATUS_AVAILABLE, 0);
4702
4703 if(msn)
4704 yahoo_packet_hash(pkt, "sss", 1, purple_connection_get_display_name(gc),
4705 7, bname+4, 65, cg);
4706 else
4707 yahoo_packet_hash(pkt, "sss", 1, purple_connection_get_display_name(gc),
4708 7, bname, 65, cg);
4709 if(f->protocol)
4710 yahoo_packet_hash_int(pkt, 241, f->protocol);
4711 yahoo_packet_send_and_free(pkt, yd);
4712 g_free(cg);
4713 }
4714
4715 static void yahoo_add_deny(PurpleConnection *gc, const char *who) {
4716 struct yahoo_data *yd = (struct yahoo_data *)gc->proto_data;
4717 struct yahoo_packet *pkt;
4718
4719 if (!yd->logged_in)
4720 return;
4721
4722 if (!who || who[0] == '\0')
4723 return;
4724
4725 pkt = yahoo_packet_new(YAHOO_SERVICE_IGNORECONTACT, YAHOO_STATUS_AVAILABLE, 0);
4726 yahoo_packet_hash(pkt, "sss", 1, purple_connection_get_display_name(gc),
4727 7, who, 13, "1");
4728 yahoo_packet_send_and_free(pkt, yd);
4729 }
4730
4731 static void yahoo_rem_deny(PurpleConnection *gc, const char *who) {
4732 struct yahoo_data *yd = (struct yahoo_data *)gc->proto_data;
4733 struct yahoo_packet *pkt;
4734
4735 if (!yd->logged_in)
4736 return;
4737
4738 if (!who || who[0] == '\0')
4739 return;
4740
4741 pkt = yahoo_packet_new(YAHOO_SERVICE_IGNORECONTACT, YAHOO_STATUS_AVAILABLE, 0);
4742 yahoo_packet_hash(pkt, "sss", 1, purple_connection_get_display_name(gc), 7, who, 13, "2");
4743 yahoo_packet_send_and_free(pkt, yd);
4744 }
4745
4746 static void yahoo_set_permit_deny(PurpleConnection *gc)
4747 {
4748 PurpleAccount *account;
4749 GSList *deny;
4750
4751 account = purple_connection_get_account(gc);
4752
4753 switch (account->perm_deny)
4754 {
4755 case PURPLE_PRIVACY_ALLOW_ALL:
4756 for (deny = account->deny; deny; deny = deny->next)
4757 yahoo_rem_deny(gc, deny->data);
4758 break;
4759
4760 case PURPLE_PRIVACY_ALLOW_BUDDYLIST:
4761 case PURPLE_PRIVACY_ALLOW_USERS:
4762 case PURPLE_PRIVACY_DENY_USERS:
4763 case PURPLE_PRIVACY_DENY_ALL:
4764 for (deny = account->deny; deny; deny = deny->next)
4765 yahoo_add_deny(gc, deny->data);
4766 break;
4767 }
4768 }
4769
4770 static gboolean yahoo_unload_plugin(PurplePlugin *plugin)
4771 {
4772 yahoo_dest_colorht();
4773
4774 return TRUE;
4775 }
4776
4777 static void yahoo_change_buddys_group(PurpleConnection *gc, const char *who,
4778 const char *old_group, const char *new_group)
4779 {
4780 struct yahoo_data *yd = gc->proto_data;
4781 struct yahoo_packet *pkt;
4782 char *gpn, *gpo;
4783 YahooFriend *f = yahoo_friend_find(gc, who);
4784 gboolean msn = FALSE;
4785 const char *temp = NULL;
4786
4787 /* Step 0: If they aren't on the server list anyway,
4788 * don't bother letting the server know.
4789 */
4790 if (!f)
4791 return;
4792
4793 if(f->protocol == 2) {
4794 msn = TRUE;
4795 temp = who+4;
4796 } else
4797 temp = who;
4798
4799 /* If old and new are the same, we would probably
4800 * end up deleting the buddy, which would be bad.
4801 * This might happen because of the charset conversation.
4802 */
4803 gpn = yahoo_string_encode(gc, new_group, NULL);
4804 gpo = yahoo_string_encode(gc, old_group, NULL);
4805 if (!strcmp(gpn, gpo)) {
4806 g_free(gpn);
4807 g_free(gpo);
4808 return;
4809 }
4810
4811 pkt = yahoo_packet_new(YAHOO_SERVICE_CHGRP_15, YAHOO_STATUS_AVAILABLE, 0);
4812 if(f->protocol)
4813 yahoo_packet_hash(pkt, "ssssissss", 1, purple_connection_get_display_name(gc),
4814 302, "240", 300, "240", 7, temp, 241, f->protocol, 224, gpo, 264, gpn, 301,
4815 "240", 303, "240");
4816 else
4817 yahoo_packet_hash(pkt, "ssssssss", 1, purple_connection_get_display_name(gc),
4818 302, "240", 300, "240", 7, temp, 224, gpo, 264, gpn, 301,
4819 "240", 303, "240");
4820 yahoo_packet_send_and_free(pkt, yd);
4821
4822 g_free(gpn);
4823 g_free(gpo);
4824 }
4825
4826 static void yahoo_rename_group(PurpleConnection *gc, const char *old_name,
4827 PurpleGroup *group, GList *moved_buddies)
4828 {
4829 struct yahoo_data *yd = gc->proto_data;
4830 struct yahoo_packet *pkt;
4831 char *gpn, *gpo;
4832
4833 gpn = yahoo_string_encode(gc, purple_group_get_name(group), NULL);
4834 gpo = yahoo_string_encode(gc, old_name, NULL);
4835 if (!strcmp(gpn, gpo)) {
4836 g_free(gpn);
4837 g_free(gpo);
4838 return;
4839 }
4840
4841 pkt = yahoo_packet_new(YAHOO_SERVICE_GROUPRENAME, YAHOO_STATUS_AVAILABLE, 0);
4842 yahoo_packet_hash(pkt, "sss", 1, purple_connection_get_display_name(gc),
4843 65, gpo, 67, gpn);
4844 yahoo_packet_send_and_free(pkt, yd);
4845 g_free(gpn);
4846 g_free(gpo);
4847 }
4848
4849 /********************************* Commands **********************************/
4850
4851 static PurpleCmdRet
4852 yahoopurple_cmd_buzz(PurpleConversation *c, const gchar *cmd, gchar **args, gchar **error, void *data) {
4853 PurpleAccount *account = purple_conversation_get_account(c);
4854
4855 if (*args && args[0])
4856 return PURPLE_CMD_RET_FAILED;
4857
4858 purple_prpl_send_attention(account->gc, c->name, YAHOO_BUZZ);
4859
4860 return PURPLE_CMD_RET_OK;
4861 }
4862
4863 static PurplePlugin *my_protocol = NULL;
4864
4865 static PurpleCmdRet
4866 yahoopurple_cmd_chat_join(PurpleConversation *conv, const char *cmd,
4867 char **args, char **error, void *data)
4868 {
4869 GHashTable *comp;
4870 PurpleConnection *gc;
4871 struct yahoo_data *yd;
4872 int id;
4873
4874 if (!args || !args[0])
4875 return PURPLE_CMD_RET_FAILED;
4876
4877 gc = purple_conversation_get_gc(conv);
4878 yd = gc->proto_data;
4879 id = yd->conf_id;
4880 purple_debug_info("yahoo", "Trying to join %s \n", args[0]);
4881
4882 comp = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
4883 g_hash_table_replace(comp, g_strdup("room"), g_ascii_strdown(args[0], -1));
4884 g_hash_table_replace(comp, g_strdup("type"), g_strdup("Chat"));
4885
4886 yahoo_c_join(gc, comp);
4887
4888 g_hash_table_destroy(comp);
4889 return PURPLE_CMD_RET_OK;
4890 }
4891
4892 static PurpleCmdRet
4893 yahoopurple_cmd_chat_list(PurpleConversation *conv, const char *cmd,
4894 char **args, char **error, void *data)
4895 {
4896 PurpleAccount *account = purple_conversation_get_account(conv);
4897 if (*args && args[0])
4898 return PURPLE_CMD_RET_FAILED;
4899 purple_roomlist_show_with_account(account);
4900 return PURPLE_CMD_RET_OK;
4901 }
4902
4903 static gboolean yahoo_offline_message(const PurpleBuddy *buddy)
4904 {
4905 return TRUE;
4906 }
4907
4908 gboolean yahoo_send_attention(PurpleConnection *gc, const char *username, guint type)
4909 {
4910 PurpleConversation *c;
4911
4912 c = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM,
4913 username, gc->account);
4914
4915 g_return_val_if_fail(c != NULL, FALSE);
4916
4917 purple_debug_info("yahoo", "Sending <ding> on account %s to buddy %s.\n",
4918 username, c->name);
4919 purple_conv_im_send_with_flags(PURPLE_CONV_IM(c), "<ding>", PURPLE_MESSAGE_INVISIBLE);
4920
4921 return TRUE;
4922 }
4923
4924 GList *yahoo_attention_types(PurpleAccount *account)
4925 {
4926 static GList *list = NULL;
4927
4928 if (!list) {
4929 /* Yahoo only supports one attention command: the 'buzz'. */
4930 /* This is index number YAHOO_BUZZ. */
4931 list = g_list_append(list, purple_attention_type_new("Buzz", _("Buzz"),
4932 _("%s has buzzed you!"), _("Buzzing %s...")));
4933 }
4934
4935 return list;
4936 }
4937
4938 /************************** Plugin Initialization ****************************/
4939 static void
4940 yahoopurple_register_commands(void)
4941 {
4942 purple_cmd_register("join", "s", PURPLE_CMD_P_PRPL,
4943 PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT |
4944 PURPLE_CMD_FLAG_PRPL_ONLY,
4945 "prpl-yahoo", yahoopurple_cmd_chat_join,
4946 _("join &lt;room&gt;: Join a chat room on the Yahoo network"), NULL);
4947 purple_cmd_register("list", "", PURPLE_CMD_P_PRPL,
4948 PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT |
4949 PURPLE_CMD_FLAG_PRPL_ONLY,
4950 "prpl-yahoo", yahoopurple_cmd_chat_list,
4951 _("list: List rooms on the Yahoo network"), NULL);
4952 purple_cmd_register("buzz", "", PURPLE_CMD_P_PRPL,
4953 PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_PRPL_ONLY,
4954 "prpl-yahoo", yahoopurple_cmd_buzz,
4955 _("buzz: Buzz a user to get their attention"), NULL);
4956 purple_cmd_register("doodle", "", PURPLE_CMD_P_PRPL,
4957 PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_PRPL_ONLY,
4958 "prpl-yahoo", yahoo_doodle_purple_cmd_start,
4959 _("doodle: Request user to start a Doodle session"), NULL);
4960 }
4961
4962 static PurpleAccount *find_acct(const char *prpl, const char *acct_id)
4963 {
4964 PurpleAccount *acct = NULL;
4965
4966 /* If we have a specific acct, use it */
4967 if (acct_id) {
4968 acct = purple_accounts_find(acct_id, prpl);
4969 if (acct && !purple_account_is_connected(acct))
4970 acct = NULL;
4971 } else { /* Otherwise find an active account for the protocol */
4972 GList *l = purple_accounts_get_all();
4973 while (l) {
4974 if (!strcmp(prpl, purple_account_get_protocol_id(l->data))
4975 && purple_account_is_connected(l->data)) {
4976 acct = l->data;
4977 break;
4978 }
4979 l = l->next;
4980 }
4981 }
4982
4983 return acct;
4984 }
4985
4986 /* This may not be the best way to do this, but we find the first key w/o a value
4987 * and assume it is the buddy name */
4988 static void yahoo_find_uri_novalue_param(gpointer key, gpointer value, gpointer user_data)
4989 {
4990 char **retval = user_data;
4991
4992 if (value == NULL && *retval == NULL) {
4993 *retval = key;
4994 }
4995 }
4996
4997 static gboolean yahoo_uri_handler(const char *proto, const char *cmd, GHashTable *params)
4998 {
4999 char *acct_id = g_hash_table_lookup(params, "account");
5000 PurpleAccount *acct;
5001
5002 if (g_ascii_strcasecmp(proto, "ymsgr"))
5003 return FALSE;
5004
5005 acct = find_acct(purple_plugin_get_id(my_protocol), acct_id);
5006
5007 if (!acct)
5008 return FALSE;
5009
5010 /* ymsgr:SendIM?screename&m=The+Message */
5011 if (!g_ascii_strcasecmp(cmd, "SendIM")) {
5012 char *sname = NULL;
5013 g_hash_table_foreach(params, yahoo_find_uri_novalue_param, &sname);
5014 if (sname) {
5015 char *message = g_hash_table_lookup(params, "m");
5016
5017 PurpleConversation *conv = purple_find_conversation_with_account(
5018 PURPLE_CONV_TYPE_IM, sname, acct);
5019 if (conv == NULL)
5020 conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, acct, sname);
5021 purple_conversation_present(conv);
5022
5023 if (message) {
5024 /* Spaces are encoded as '+' */
5025 g_strdelimit(message, "+", ' ');
5026 purple_conv_send_confirm(conv, message);
5027 }
5028 }
5029 /* else
5030 **If pidgindialogs_im() was in the core, we could use it here.
5031 * It is all purple_request_* based, but I'm not sure it really belongs in the core
5032 pidgindialogs_im(); */
5033
5034 return TRUE;
5035 }
5036 /* ymsgr:Chat?roomname */
5037 else if (!g_ascii_strcasecmp(cmd, "Chat")) {
5038 char *rname = NULL;
5039 g_hash_table_foreach(params, yahoo_find_uri_novalue_param, &rname);
5040 if (rname) {
5041 /* This is somewhat hacky, but the params aren't useful after this command */
5042 g_hash_table_insert(params, g_strdup("room"), g_strdup(rname));
5043 g_hash_table_insert(params, g_strdup("type"), g_strdup("Chat"));
5044 serv_join_chat(purple_account_get_connection(acct), params);
5045 }
5046 /* else
5047 ** Same as above (except that this would have to be re-written using purple_request_*)
5048 pidgin_blist_joinchat_show(); */
5049
5050 return TRUE;
5051 }
5052 /* ymsgr:AddFriend?name */
5053 else if (!g_ascii_strcasecmp(cmd, "AddFriend")) {
5054 char *name = NULL;
5055 g_hash_table_foreach(params, yahoo_find_uri_novalue_param, &name);
5056 purple_blist_request_add_buddy(acct, name, NULL, NULL);
5057 return TRUE;
5058 }
5059
5060 return FALSE;
5061 }
5062
5063 static GHashTable *
5064 yahoo_get_account_text_table(PurpleAccount *account)
5065 {
5066 GHashTable *table;
5067 table = g_hash_table_new(g_str_hash, g_str_equal);
5068 g_hash_table_insert(table, "login_label", (gpointer)_("Yahoo ID..."));
5069 return table;
5070 }
5071
5072 static PurpleWhiteboardPrplOps yahoo_whiteboard_prpl_ops =
5073 {
5074 yahoo_doodle_start,
5075 yahoo_doodle_end,
5076 yahoo_doodle_get_dimensions,
5077 NULL,
5078 yahoo_doodle_get_brush,
5079 yahoo_doodle_set_brush,
5080 yahoo_doodle_send_draw_list,
5081 yahoo_doodle_clear,
5082
5083 /* padding */
5084 NULL,
5085 NULL,
5086 NULL,
5087 NULL
5088 };
5089
5090 static PurplePluginProtocolInfo prpl_info =
5091 {
5092 OPT_PROTO_MAIL_CHECK | OPT_PROTO_CHAT_TOPIC,
5093 NULL, /* user_splits */
5094 NULL, /* protocol_options */
5095 {"png,gif,jpeg", 96, 96, 96, 96, 0, PURPLE_ICON_SCALE_SEND},
5096 yahoo_list_icon,
5097 yahoo_list_emblem,
5098 yahoo_status_text,
5099 yahoo_tooltip_text,
5100 yahoo_status_types,
5101 yahoo_blist_node_menu,
5102 yahoo_c_info,
5103 yahoo_c_info_defaults,
5104 yahoo_login,
5105 yahoo_close,
5106 yahoo_send_im,
5107 NULL, /* set info */
5108 yahoo_send_typing,
5109 yahoo_get_info,
5110 yahoo_set_status,
5111 yahoo_set_idle,
5112 NULL, /* change_passwd*/
5113 yahoo_add_buddy,
5114 NULL, /* add_buddies */
5115 yahoo_remove_buddy,
5116 NULL, /* remove_buddies */
5117 NULL, /* add_permit */
5118 yahoo_add_deny,
5119 NULL, /* rem_permit */
5120 yahoo_rem_deny,
5121 yahoo_set_permit_deny,
5122 yahoo_c_join,
5123 NULL, /* reject chat invite */
5124 yahoo_get_chat_name,
5125 yahoo_c_invite,
5126 yahoo_c_leave,
5127 NULL, /* chat whisper */
5128 yahoo_c_send,
5129 yahoo_keepalive,
5130 NULL, /* register_user */
5131 NULL, /* get_cb_info */
5132 NULL, /* get_cb_away */
5133 yahoo_update_alias, /* alias_buddy */
5134 yahoo_change_buddys_group,
5135 yahoo_rename_group,
5136 NULL, /* buddy_free */
5137 NULL, /* convo_closed */
5138 purple_normalize_nocase, /* normalize */
5139 yahoo_set_buddy_icon,
5140 NULL, /* void (*remove_group)(PurpleConnection *gc, const char *group);*/
5141 NULL, /* char *(*get_cb_real_name)(PurpleConnection *gc, int id, const char *who); */
5142 NULL, /* set_chat_topic */
5143 NULL, /* find_blist_chat */
5144 yahoo_roomlist_get_list,
5145 yahoo_roomlist_cancel,
5146 yahoo_roomlist_expand_category,
5147 NULL, /* can_receive_file */
5148 yahoo_send_file,
5149 yahoo_new_xfer,
5150 yahoo_offline_message, /* offline_message */
5151 &yahoo_whiteboard_prpl_ops,
5152 NULL, /* send_raw */
5153 NULL, /* roomlist_room_serialize */
5154 NULL, /* unregister_user */
5155
5156 yahoo_send_attention,
5157 yahoo_attention_types,
5158
5159 sizeof(PurplePluginProtocolInfo), /* struct_size */
5160 yahoo_get_account_text_table, /* get_account_text_table */
5161 NULL, /* initiate_media */
5162 NULL /* can_do_media */
5163 };
5164
5165 static PurplePluginInfo info =
5166 {
5167 PURPLE_PLUGIN_MAGIC,
5168 PURPLE_MAJOR_VERSION,
5169 PURPLE_MINOR_VERSION,
5170 PURPLE_PLUGIN_PROTOCOL, /**< type */
5171 NULL, /**< ui_requirement */
5172 0, /**< flags */
5173 NULL, /**< dependencies */
5174 PURPLE_PRIORITY_DEFAULT, /**< priority */
5175 "prpl-yahoo", /**< id */
5176 "Yahoo", /**< name */
5177 DISPLAY_VERSION, /**< version */
5178 /** summary */
5179 N_("Yahoo Protocol Plugin"),
5180 /** description */
5181 N_("Yahoo Protocol Plugin"),
5182 NULL, /**< author */
5183 PURPLE_WEBSITE, /**< homepage */
5184 NULL, /**< load */
5185 yahoo_unload_plugin, /**< unload */
5186 NULL, /**< destroy */
5187 NULL, /**< ui_info */
5188 &prpl_info, /**< extra_info */
5189 NULL,
5190 yahoo_actions,
5191
5192 /* padding */
5193 NULL,
5194 NULL,
5195 NULL,
5196 NULL
5197 };
5198
5199 static void
5200 init_plugin(PurplePlugin *plugin)
5201 {
5202 PurpleAccountOption *option;
5203
5204 option = purple_account_option_bool_new(_("Yahoo Japan"), "yahoojp", FALSE);
5205 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
5206
5207 option = purple_account_option_string_new(_("Pager server"), "server", YAHOO_PAGER_HOST);
5208 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
5209
5210 option = purple_account_option_string_new(_("Japan Pager server"), "serverjp", YAHOOJP_PAGER_HOST);
5211 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
5212
5213 option = purple_account_option_int_new(_("Pager port"), "port", YAHOO_PAGER_PORT);
5214 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
5215
5216 option = purple_account_option_string_new(_("File transfer server"), "xfer_host", YAHOO_XFER_HOST);
5217 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
5218
5219 option = purple_account_option_string_new(_("Japan file transfer server"), "xferjp_host", YAHOOJP_XFER_HOST);
5220 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
5221
5222 option = purple_account_option_int_new(_("File transfer port"), "xfer_port", YAHOO_XFER_PORT);
5223 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
5224
5225 option = purple_account_option_string_new(_("Chat room locale"), "room_list_locale", YAHOO_ROOMLIST_LOCALE);
5226 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
5227
5228 option = purple_account_option_bool_new(_("Ignore conference and chatroom invitations"), "ignore_invites", FALSE);
5229 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
5230
5231 option = purple_account_option_string_new(_("Encoding"), "local_charset", "ISO-8859-1");
5232 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
5233
5234
5235 #if 0
5236 option = purple_account_option_string_new(_("Chat room list URL"), "room_list", YAHOO_ROOMLIST_URL);
5237 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
5238
5239 option = purple_account_option_string_new(_("Yahoo Chat server"), "ycht-server", YAHOO_YCHT_HOST);
5240 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
5241
5242 option = purple_account_option_int_new(_("Yahoo Chat port"), "ycht-port", YAHOO_YCHT_PORT);
5243 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
5244 #endif
5245
5246 my_protocol = plugin;
5247 yahoopurple_register_commands();
5248 yahoo_init_colorht();
5249
5250 purple_signal_connect(purple_get_core(), "uri-handler", plugin,
5251 PURPLE_CALLBACK(yahoo_uri_handler), NULL);
5252 }
5253
5254 PURPLE_INIT_PLUGIN(yahoo, init_plugin, info);