Migrate certificates, icons, logs to XDG dirs
[pidgin-git.git] / libpurple / protocols / yahoo / ymsg.c
blobd83482e11e91c3c8a640e800b679b74cb4bd19f1
1 /*
2 * purple
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.
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.
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.
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
25 * Note: When handling the list of struct yahoo_pair's from an incoming
26 * packet the value might not be UTF-8. You should either validate that
27 * it is UTF-8 using g_utf8_validate() or use yahoo_string_decode().
30 #include "internal.h"
32 #include "account.h"
33 #include "accountopt.h"
34 #include "buddylist.h"
35 #include "ciphers/md5hash.h"
36 #include "cmds.h"
37 #include "core.h"
38 #include "debug.h"
39 #include "http.h"
40 #include "network.h"
41 #include "notify.h"
42 #include "protocol.h"
43 #include "proxy.h"
44 #include "request.h"
45 #include "server.h"
46 #include "util.h"
47 #include "version.h"
48 #include "xmlnode.h"
50 #include "ymsg.h"
51 #include "yahoochat.h"
52 #include "yahoo_aliases.h"
53 #include "yahoo_doodle.h"
54 #include "yahoo_filexfer.h"
55 #include "yahoo_friend.h"
56 #include "yahoo_packet.h"
57 #include "yahoo_picture.h"
58 #include "ycht.h"
60 /* #define YAHOO_DEBUG */
62 /* It doesn't look like it is working (the previously used host is down, another
63 * one doesn't send us back cookies).
65 #define TRY_WEBMESSENGER_LOGIN 0
67 /* One hour */
68 #define PING_TIMEOUT 3600
70 /* One minute */
71 #define KEEPALIVE_TIMEOUT 60
73 #if TRY_WEBMESSENGER_LOGIN
74 static void
75 yahoo_login_page_cb(PurpleHttpConnection *http_conn,
76 PurpleHttpResponse *response, gpointer _unused);
77 #endif /* TRY_WEBMESSENGER_LOGIN */
79 static void yahoo_update_status(PurpleConnection *gc, const char *name, YahooFriend *f)
81 char *status = NULL;
83 if (!gc || !name || !f || !purple_blist_find_buddy(purple_connection_get_account(gc), name))
84 return;
86 switch (f->status) {
87 case YAHOO_STATUS_OFFLINE:
88 status = YAHOO_STATUS_TYPE_OFFLINE;
89 break;
90 case YAHOO_STATUS_AVAILABLE:
91 status = YAHOO_STATUS_TYPE_AVAILABLE;
92 break;
93 case YAHOO_STATUS_BRB:
94 status = YAHOO_STATUS_TYPE_BRB;
95 break;
96 case YAHOO_STATUS_BUSY:
97 status = YAHOO_STATUS_TYPE_BUSY;
98 break;
99 case YAHOO_STATUS_NOTATHOME:
100 status = YAHOO_STATUS_TYPE_NOTATHOME;
101 break;
102 case YAHOO_STATUS_NOTATDESK:
103 status = YAHOO_STATUS_TYPE_NOTATDESK;
104 break;
105 case YAHOO_STATUS_NOTINOFFICE:
106 status = YAHOO_STATUS_TYPE_NOTINOFFICE;
107 break;
108 case YAHOO_STATUS_ONPHONE:
109 status = YAHOO_STATUS_TYPE_ONPHONE;
110 break;
111 case YAHOO_STATUS_ONVACATION:
112 status = YAHOO_STATUS_TYPE_ONVACATION;
113 break;
114 case YAHOO_STATUS_OUTTOLUNCH:
115 status = YAHOO_STATUS_TYPE_OUTTOLUNCH;
116 break;
117 case YAHOO_STATUS_STEPPEDOUT:
118 status = YAHOO_STATUS_TYPE_STEPPEDOUT;
119 break;
120 case YAHOO_STATUS_INVISIBLE: /* this should never happen? */
121 status = YAHOO_STATUS_TYPE_INVISIBLE;
122 break;
123 case YAHOO_STATUS_CUSTOM:
124 case YAHOO_STATUS_IDLE:
125 if (!f->away)
126 status = YAHOO_STATUS_TYPE_AVAILABLE;
127 else
128 status = YAHOO_STATUS_TYPE_AWAY;
129 break;
130 default:
131 purple_debug_warning("yahoo", "Warning, unknown status %d\n", f->status);
132 break;
135 if (status) {
136 if (f->status == YAHOO_STATUS_CUSTOM)
137 purple_protocol_got_user_status(purple_connection_get_account(gc), name, status, "message",
138 yahoo_friend_get_status_message(f), NULL);
139 else
140 purple_protocol_got_user_status(purple_connection_get_account(gc), name, status, NULL);
143 if (f->idle != 0)
144 purple_protocol_got_user_idle(purple_connection_get_account(gc), name, TRUE, f->idle);
145 else
146 purple_protocol_got_user_idle(purple_connection_get_account(gc), name, FALSE, 0);
148 if (f->sms)
149 purple_protocol_got_user_status(purple_connection_get_account(gc), name, YAHOO_STATUS_TYPE_MOBILE, NULL);
150 else
151 purple_protocol_got_user_status_deactive(purple_connection_get_account(gc), name, YAHOO_STATUS_TYPE_MOBILE);
154 static void yahoo_process_status(PurpleConnection *gc, struct yahoo_packet *pkt)
156 PurpleAccount *account = purple_connection_get_account(gc);
157 GSList *l = pkt->hash;
158 YahooFriend *f = NULL;
159 char *name = NULL;
160 gboolean unicode = FALSE;
161 char *message = NULL;
163 if (pkt->service == YAHOO_SERVICE_LOGOFF && pkt->status == -1) {
164 if (!purple_account_get_remember_password(account))
165 purple_account_set_password(account, NULL, NULL, NULL);
166 purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NAME_IN_USE,
167 _("You have signed on from another location"));
168 return;
171 while (l) {
172 struct yahoo_pair *pair = l->data;
174 switch (pair->key) {
175 case 0: /* we won't actually do anything with this */
176 case 1: /* we won't actually do anything with this */
177 break;
178 case 8: /* how many online buddies we have */
179 break;
180 case 7: /* the current buddy */
181 /* update the previous buddy before changing the variables */
182 if (f) {
183 if (message)
184 yahoo_friend_set_status_message(f, yahoo_string_decode(gc, message, unicode));
185 if (name)
186 yahoo_update_status(gc, name, f);
188 name = message = NULL;
189 f = NULL;
190 if (pair->value && g_utf8_validate(pair->value, -1, NULL)) {
191 name = pair->value;
192 f = yahoo_friend_find_or_new(gc, name);
194 break;
195 case 10: /* state */
196 if (!f)
197 break;
199 f->status = strtol(pair->value, NULL, 10);
200 if ((f->status >= YAHOO_STATUS_BRB) && (f->status <= YAHOO_STATUS_STEPPEDOUT))
201 f->away = 1;
202 else
203 f->away = 0;
205 if (f->status == YAHOO_STATUS_IDLE) {
206 /* Idle may have already been set in a more precise way in case 137 */
207 if (f->idle == 0)
209 if(pkt->service == YAHOO_SERVICE_STATUS_15)
210 f->idle = -1;
211 else
212 f->idle = time(NULL);
214 } else
215 f->idle = 0;
217 if (f->status != YAHOO_STATUS_CUSTOM)
218 yahoo_friend_set_status_message(f, NULL);
220 f->sms = 0;
221 break;
222 case 19: /* custom message */
223 if (f)
224 message = pair->value;
225 break;
226 case 11: /* this is the buddy's session id */
227 if (f)
228 f->session_id = strtol(pair->value, NULL, 10);
229 break;
230 case 17: /* in chat? */
231 break;
232 case 47: /* is custom status away or not? 2=idle*/
233 if (!f)
234 break;
236 /* I have no idea what it means when this is
237 * set when someone's available, but it doesn't
238 * mean idle. */
239 if (f->status == YAHOO_STATUS_AVAILABLE)
240 break;
242 f->away = strtol(pair->value, NULL, 10);
243 if (f->away == 2) {
244 /* Idle may have already been set in a more precise way in case 137 */
245 if (f->idle == 0)
247 if(pkt->service == YAHOO_SERVICE_STATUS_15)
248 f->idle = -1;
249 else
250 f->idle = time(NULL);
254 break;
255 case 138: /* when value is 1, either we're not idle, or we are but won't say how long */
256 if (!f)
257 break;
259 if( (strtol(pair->value, NULL, 10) == 1) && (f->idle) )
260 f->idle = -1;
261 break;
262 case 137: /* usually idle time in seconds, sometimes login time */
263 if (!f)
264 break;
266 if (f->status != YAHOO_STATUS_AVAILABLE)
267 f->idle = time(NULL) - strtol(pair->value, NULL, 10);
268 break;
269 case 13: /* bitmask, bit 0 = pager, bit 1 = chat, bit 2 = game */
270 if (strtol(pair->value, NULL, 10) == 0) {
271 if (f)
272 f->status = YAHOO_STATUS_OFFLINE;
273 if (name) {
274 purple_protocol_got_user_status(account, name, "offline", NULL);
275 purple_protocol_got_user_status_deactive(account, name, YAHOO_STATUS_TYPE_MOBILE);
277 break;
279 break;
280 case 60: /* SMS */
281 if (f) {
282 f->sms = strtol(pair->value, NULL, 10);
283 yahoo_update_status(gc, name, f);
285 break;
286 case 197: /* Avatars */
288 guchar *decoded;
289 char *tmp;
290 gsize len;
292 if (pair->value) {
293 decoded = purple_base64_decode(pair->value, &len);
294 if (decoded && len > 0) {
295 tmp = purple_str_binary_to_ascii(decoded, len);
296 purple_debug_info("yahoo", "Got key 197, value = %s\n", tmp);
297 g_free(tmp);
299 g_free(decoded);
301 break;
303 case 192: /* Pictures, aka Buddy Icons, checksum */
305 /* FIXME: Please, if you know this protocol,
306 * FIXME: fix up the strtol() stuff if possible. */
307 int cksum = strtol(pair->value, NULL, 10);
308 const char *locksum = NULL;
309 PurpleBuddy *b;
311 if (!name)
312 break;
314 b = purple_blist_find_buddy(purple_connection_get_account(gc), name);
316 if (!cksum || (cksum == -1)) {
317 if (f)
318 yahoo_friend_set_buddy_icon_need_request(f, TRUE);
319 purple_buddy_icons_set_for_user(purple_connection_get_account(gc), name, NULL, 0, NULL);
320 break;
323 if (!f)
324 break;
326 yahoo_friend_set_buddy_icon_need_request(f, FALSE);
327 if (b) {
328 locksum = purple_buddy_icons_get_checksum_for_user(b);
329 if (!locksum || (cksum != strtol(locksum, NULL, 10)))
330 yahoo_send_picture_request(gc, name);
333 break;
335 case 16: /* Custom error message */
337 char *tmp = yahoo_string_decode(gc, pair->value, TRUE);
338 purple_notify_error(gc, NULL, tmp, NULL,
339 purple_request_cpar_from_connection(gc));
340 g_free(tmp);
342 break;
343 case 97: /* Unicode status message */
344 unicode = !strcmp(pair->value, "1");
345 break;
346 case 244: /* client version number. Yahoo Client Detection */
347 if(f && strtol(pair->value, NULL, 10))
348 f->version_id = strtol(pair->value, NULL, 10);
349 break;
350 default:
351 purple_debug_warning("yahoo",
352 "Unknown status key %d\n", pair->key);
353 break;
356 l = l->next;
359 if (f) {
360 if (pkt->service == YAHOO_SERVICE_LOGOFF)
361 f->status = YAHOO_STATUS_OFFLINE;
362 if (message)
363 yahoo_friend_set_status_message(f, yahoo_string_decode(gc, message, unicode));
365 if (name) /* update the last buddy */
366 yahoo_update_status(gc, name, f);
370 static void yahoo_do_group_check(PurpleAccount *account, GHashTable *ht, const char *name, const char *group)
372 PurpleBuddy *b;
373 PurpleGroup *g;
374 GSList *list, *i;
375 gboolean onlist = FALSE;
376 char *oname = NULL;
378 if (g_hash_table_lookup_extended(ht, name, (gpointer *)&oname, (gpointer *)&list))
379 g_hash_table_steal(ht, oname);
380 else
381 list = purple_blist_find_buddies(account, name);
383 for (i = list; i; i = i->next) {
384 b = i->data;
385 g = purple_buddy_get_group(b);
386 if (!purple_utf8_strcasecmp(group, purple_group_get_name(g))) {
387 purple_debug_misc("yahoo",
388 "Oh good, %s is in the right group (%s).\n", name, group);
389 list = g_slist_delete_link(list, i);
390 onlist = TRUE;
391 break;
395 if (!onlist) {
396 purple_debug_misc("yahoo",
397 "Uhoh, %s isn't on the list (or not in this group), adding him to group %s.\n", name, group);
398 if (!(g = purple_blist_find_group(group))) {
399 g = purple_group_new(group);
400 purple_blist_add_group(g, NULL);
402 b = purple_buddy_new(account, name, NULL);
403 purple_blist_add_buddy(b, NULL, g, NULL);
406 if (list) {
407 if (!oname)
408 oname = g_strdup(name);
409 g_hash_table_insert(ht, oname, list);
410 } else
411 g_free(oname);
414 static void yahoo_do_group_cleanup(gpointer key, gpointer value, gpointer user_data)
416 char *name = key;
417 GSList *list = value, *i;
418 PurpleBuddy *b;
419 PurpleGroup *g;
421 for (i = list; i; i = i->next) {
422 b = i->data;
423 g = purple_buddy_get_group(b);
424 purple_debug_misc("yahoo", "Deleting Buddy %s from group %s.\n", name,
425 purple_group_get_name(g));
426 purple_blist_remove_buddy(b);
430 static char *_getcookie(char *rawcookie)
432 char *cookie = NULL;
433 char *tmpcookie;
434 char *cookieend;
436 if (strlen(rawcookie) < 2)
437 return NULL;
438 tmpcookie = g_strdup(rawcookie+2);
439 cookieend = strchr(tmpcookie, ';');
441 if (cookieend)
442 *cookieend = '\0';
444 cookie = g_strdup(tmpcookie);
445 g_free(tmpcookie);
447 return cookie;
450 static void yahoo_process_cookie(YahooData *yd, char *c)
452 if (c[0] == 'Y') {
453 g_free(yd->cookie_y);
454 yd->cookie_y = _getcookie(c);
455 } else if (c[0] == 'T') {
456 g_free(yd->cookie_t);
457 yd->cookie_t = _getcookie(c);
458 } else
459 purple_debug_info("yahoo", "Unrecognized cookie '%c'\n", c[0]);
460 yd->cookies = g_slist_prepend(yd->cookies, g_strdup(c));
463 static void yahoo_process_list_15(PurpleConnection *gc, struct yahoo_packet *pkt)
465 GSList *l = pkt->hash;
467 PurpleAccount *account = purple_connection_get_account(gc);
468 YahooData *yd = purple_connection_get_protocol_data(gc);
469 GHashTable *ht;
470 char *norm_bud = NULL;
471 char *temp = NULL;
472 YahooFriend *f = NULL; /* It's your friends. They're going to want you to share your StarBursts. */
473 /* But what if you had no friends? */
474 int stealth = 0;
476 ht = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_slist_free);
478 while (l) {
479 struct yahoo_pair *pair = l->data;
480 l = l->next;
482 switch (pair->key) {
483 case 302:
484 /* This is always 318 before a group, 319 before the first s/n in a group, 320 before any ignored s/n.
485 * It is not sent for s/n's in a group after the first.
486 * All ignored s/n's are listed last, so when we see a 320 we clear the group and begin marking the
487 * s/n's as ignored. It is always followed by an identical 300 key.
489 if (pair->value && !strcmp(pair->value, "320")) {
490 /* No longer in any group; this indicates the start of the ignore list. */
491 g_free(yd->current_list15_grp);
492 yd->current_list15_grp = NULL;
495 break;
496 case 301: /* This is 319 before all s/n's in a group after the first. It is followed by an identical 300. */
497 if(temp != NULL) {
498 norm_bud = g_strdup(temp);
500 if (yd->current_list15_grp) {
501 /* This buddy is in a group */
502 f = yahoo_friend_find_or_new(gc, norm_bud);
503 if (!purple_blist_find_buddy(account, norm_bud)) {
504 PurpleBuddy *b;
505 PurpleGroup *g;
506 if (!(g = purple_blist_find_group(yd->current_list15_grp))) {
507 g = purple_group_new(yd->current_list15_grp);
508 purple_blist_add_group(g, NULL);
510 b = purple_buddy_new(account, norm_bud, NULL);
511 purple_blist_add_buddy(b, NULL, g, NULL);
513 yahoo_do_group_check(account, ht, norm_bud, yd->current_list15_grp);
515 if(stealth == 2)
516 f->presence = YAHOO_PRESENCE_PERM_OFFLINE;
518 yahoo_friend_set_p2p_status(f, YAHOO_P2PSTATUS_NOT_CONNECTED);
519 f->p2p_packet_sent = 0;
520 } else {
521 /* This buddy is on the ignore list (and therefore in no group) */
522 purple_debug_info("yahoo", "%s adding %s to the deny list because of the ignore list / no group was found\n", purple_account_get_username(account), norm_bud);
523 purple_account_privacy_deny_add(account, norm_bud, 1);
526 g_free(norm_bud);
527 norm_bud=NULL;
528 stealth = 0;
529 g_free(temp);
530 temp = NULL;
532 break;
533 case 300: /* This is 318 before a group, 319 before any s/n in a group, and 320 before any ignored s/n. */
534 break;
535 case 65: /* This is the group */
536 g_free(yd->current_list15_grp);
537 yd->current_list15_grp = yahoo_string_decode(gc, pair->value, FALSE);
538 break;
539 case 7: /* buddy's s/n */
540 if (g_utf8_validate(pair->value, -1, NULL)) {
541 g_free(temp);
542 temp = g_strdup(purple_normalize(account, pair->value));
543 } else {
544 purple_debug_warning("yahoo", "yahoo_process_list_15 "
545 "got non-UTF-8 string for key %d\n", pair->key);
547 break;
548 case 59: /* somebody told cookies come here too, but im not sure */
549 if (g_utf8_validate(pair->value, -1, NULL)) {
550 yahoo_process_cookie(yd, pair->value);
551 } else {
552 purple_debug_warning("yahoo", "yahoo_process_list_15 "
553 "got non-UTF-8 string for key %d\n", pair->key);
555 break;
556 case 317: /* Stealth Setting */
557 stealth = strtol(pair->value, NULL, 10);
558 break;
562 g_hash_table_foreach(ht, yahoo_do_group_cleanup, NULL);
564 /* The reporter of ticket #9745 determined that we weren't retrieving the
565 * aliases during buddy list retrieval, so we never updated aliases that
566 * changed while we were signed off. */
567 yahoo_fetch_aliases(gc);
569 /* Now that we have processed the buddy list, we can say yahoo has connected */
570 purple_connection_set_display_name(gc, purple_normalize(account, purple_account_get_username(account)));
571 yd->logged_in = TRUE;
572 purple_debug_info("yahoo","Authentication: Connection established\n");
573 purple_connection_set_state(gc, PURPLE_CONNECTION_CONNECTED);
574 if (yd->picture_upload_todo) {
575 yahoo_buddy_icon_upload(gc, yd->picture_upload_todo);
576 yd->picture_upload_todo = NULL;
578 yahoo_set_status(account, purple_account_get_active_status(account));
580 g_hash_table_destroy(ht);
581 g_free(temp);
584 static void yahoo_process_list(PurpleConnection *gc, struct yahoo_packet *pkt)
586 GSList *l = pkt->hash;
587 gboolean got_serv_list = FALSE;
588 YahooFriend *f = NULL;
589 PurpleAccount *account = purple_connection_get_account(gc);
590 YahooData *yd = purple_connection_get_protocol_data(gc);
591 GHashTable *ht;
593 char **lines;
594 char **split;
595 char **buddies;
596 char **tmp, **bud, *norm_bud;
597 char *grp = NULL;
599 if (pkt->id)
600 yd->session_id = pkt->id;
602 while (l) {
603 struct yahoo_pair *pair = l->data;
604 l = l->next;
606 switch (pair->key) {
607 case 87:
608 if (!yd->tmp_serv_blist)
609 yd->tmp_serv_blist = g_string_new(pair->value);
610 else
611 g_string_append(yd->tmp_serv_blist, pair->value);
612 break;
613 case 88:
614 if (g_utf8_validate(pair->value, -1, NULL)) {
615 if (!yd->tmp_serv_ilist)
616 yd->tmp_serv_ilist = g_string_new(pair->value);
617 else
618 g_string_append(yd->tmp_serv_ilist, pair->value);
619 } else {
620 purple_debug_warning("yahoo", "yahoo_process_list "
621 "got non-UTF-8 string for key %d\n", pair->key);
623 break;
624 case 89:
625 if (g_utf8_validate(pair->value, -1, NULL)) {
626 yd->profiles = g_strsplit(pair->value, ",", -1);
627 } else {
628 purple_debug_warning("yahoo", "yahoo_process_list "
629 "got non-UTF-8 string for key %d\n", pair->key);
631 break;
632 case 59: /* cookies, yum */
633 if (g_utf8_validate(pair->value, -1, NULL)) {
634 yahoo_process_cookie(yd, pair->value);
635 } else {
636 purple_debug_warning("yahoo", "yahoo_process_list "
637 "got non-UTF-8 string for key %d\n", pair->key);
639 break;
640 case YAHOO_SERVICE_PRESENCE_PERM:
641 if (g_utf8_validate(pair->value, -1, NULL)) {
642 if (!yd->tmp_serv_plist)
643 yd->tmp_serv_plist = g_string_new(pair->value);
644 else
645 g_string_append(yd->tmp_serv_plist, pair->value);
646 } else {
647 purple_debug_warning("yahoo", "yahoo_process_list "
648 "got non-UTF-8 string for key %d\n", pair->key);
650 break;
654 if (pkt->status != 0)
655 return;
657 if (yd->tmp_serv_blist) {
658 ht = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_slist_free);
660 lines = g_strsplit(yd->tmp_serv_blist->str, "\n", -1);
661 for (tmp = lines; *tmp; tmp++) {
662 split = g_strsplit(*tmp, ":", 2);
663 if (!split)
664 continue;
665 if (!split[0] || !split[1]) {
666 g_strfreev(split);
667 continue;
669 grp = yahoo_string_decode(gc, split[0], FALSE);
670 buddies = g_strsplit(split[1], ",", -1);
671 for (bud = buddies; bud && *bud; bud++) {
672 if (!g_utf8_validate(*bud, -1, NULL)) {
673 purple_debug_warning("yahoo", "yahoo_process_list "
674 "got non-UTF-8 string for bud\n");
675 continue;
678 norm_bud = g_strdup(purple_normalize(account, *bud));
679 f = yahoo_friend_find_or_new(gc, norm_bud);
681 if (!purple_blist_find_buddy(account, norm_bud)) {
682 PurpleBuddy *b;
683 PurpleGroup *g;
684 if (!(g = purple_blist_find_group(grp))) {
685 g = purple_group_new(grp);
686 purple_blist_add_group(g, NULL);
688 b = purple_buddy_new(account, norm_bud, NULL);
689 purple_blist_add_buddy(b, NULL, g, NULL);
692 yahoo_do_group_check(account, ht, norm_bud, grp);
693 /* set p2p status not connected and no p2p packet sent */
694 yahoo_friend_set_p2p_status(f, YAHOO_P2PSTATUS_NOT_CONNECTED);
695 f->p2p_packet_sent = 0;
697 g_free(norm_bud);
699 g_strfreev(buddies);
700 g_strfreev(split);
701 g_free(grp);
703 g_strfreev(lines);
705 g_string_free(yd->tmp_serv_blist, TRUE);
706 yd->tmp_serv_blist = NULL;
707 g_hash_table_foreach(ht, yahoo_do_group_cleanup, NULL);
708 g_hash_table_destroy(ht);
711 if (yd->tmp_serv_ilist) {
712 buddies = g_strsplit(yd->tmp_serv_ilist->str, ",", -1);
713 for (bud = buddies; bud && *bud; bud++) {
714 /* The server is already ignoring the user */
715 got_serv_list = TRUE;
716 purple_account_privacy_deny_add(account, *bud, 1);
718 g_strfreev(buddies);
720 g_string_free(yd->tmp_serv_ilist, TRUE);
721 yd->tmp_serv_ilist = NULL;
724 if (got_serv_list &&
725 ((purple_account_get_privacy_type(account) != PURPLE_ACCOUNT_PRIVACY_ALLOW_BUDDYLIST) &&
726 (purple_account_get_privacy_type(account) != PURPLE_ACCOUNT_PRIVACY_DENY_ALL) &&
727 (purple_account_get_privacy_type(account) != PURPLE_ACCOUNT_PRIVACY_ALLOW_USERS)))
729 purple_account_set_privacy_type(account, PURPLE_ACCOUNT_PRIVACY_DENY_USERS);
730 purple_debug_info("yahoo", "%s privacy defaulting to PURPLE_ACCOUNT_PRIVACY_DENY_USERS.\n",
731 purple_account_get_username(account));
734 if (yd->tmp_serv_plist) {
735 buddies = g_strsplit(yd->tmp_serv_plist->str, ",", -1);
736 for (bud = buddies; bud && *bud; bud++) {
737 f = yahoo_friend_find(gc, *bud);
738 if (f) {
739 purple_debug_info("yahoo", "%s setting presence for %s to PERM_OFFLINE\n",
740 purple_account_get_username(account), *bud);
741 f->presence = YAHOO_PRESENCE_PERM_OFFLINE;
744 g_strfreev(buddies);
745 g_string_free(yd->tmp_serv_plist, TRUE);
746 yd->tmp_serv_plist = NULL;
749 /* Now that we've got the list, request aliases */
750 yahoo_fetch_aliases(gc);
753 /* pkt_type is YAHOO_PKT_TYPE_SERVER if pkt arrives from yahoo server, YAHOO_PKT_TYPE_P2P if pkt arrives through p2p */
754 static void yahoo_process_notify(PurpleConnection *gc, struct yahoo_packet *pkt, yahoo_pkt_type pkt_type)
756 PurpleAccount *account;
757 char *msg = NULL;
758 char *from = NULL;
759 char *stat = NULL;
760 char *game = NULL;
761 YahooFriend *f = NULL;
762 GSList *l = pkt->hash;
763 gint val_11 = 0;
764 YahooData *yd = purple_connection_get_protocol_data(gc);
766 account = purple_connection_get_account(gc);
768 while (l) {
769 struct yahoo_pair *pair = l->data;
770 if (pair->key == 4 || pair->key == 1) {
771 if (g_utf8_validate(pair->value, -1, NULL)) {
772 from = pair->value;
773 } else {
774 purple_debug_warning("yahoo", "yahoo_process_notify "
775 "got non-UTF-8 string for key %d\n", pair->key);
777 } else if (pair->key == 49) {
778 msg = pair->value;
779 } else if (pair->key == 13) {
780 stat = pair->value;
781 } else if (pair->key == 14) {
782 if (g_utf8_validate(pair->value, -1, NULL)) {
783 game = pair->value;
784 } else {
785 purple_debug_warning("yahoo", "yahoo_process_notify "
786 "got non-UTF-8 string for key %d\n", pair->key);
788 } else if (pair->key == 11) {
789 val_11 = strtol(pair->value, NULL, 10);
791 l = l->next;
794 if (!from || !msg)
795 return;
797 /* disconnect the peer if connected through p2p and sends wrong value for session id */
798 if ((pkt_type == YAHOO_PKT_TYPE_P2P) && (val_11 != yd->session_id)) {
799 purple_debug_warning("yahoo","p2p: %s sent us notify with wrong session id. Disconnecting p2p connection to peer\n", from);
800 /* remove from p2p connection lists, also calls yahoo_p2p_disconnect_destroy_data */
801 g_hash_table_remove(yd->peers, from);
802 return;
805 if (!g_ascii_strncasecmp(msg, "TYPING", strlen("TYPING"))
806 && (purple_account_privacy_check(account, from)))
809 if (stat && *stat == '1')
810 purple_serv_got_typing(gc, from, 0, PURPLE_IM_TYPING);
811 else
812 purple_serv_got_typing_stopped(gc, from);
814 } else if (!g_ascii_strncasecmp(msg, "GAME", strlen("GAME"))) {
815 PurpleBuddy *bud = purple_blist_find_buddy(account, from);
817 if (!bud) {
818 purple_debug_warning("yahoo",
819 "%s is playing a game, and doesn't want you to know.\n", from);
822 f = yahoo_friend_find(gc, from);
823 if (!f)
824 return; /* if they're not on the list, don't bother */
826 yahoo_friend_set_game(f, NULL);
828 if (stat && *stat == '1') {
829 yahoo_friend_set_game(f, game);
830 if (bud)
831 yahoo_update_status(gc, from, f);
833 } else if (!g_ascii_strncasecmp(msg, "WEBCAMINVITE", strlen("WEBCAMINVITE"))) {
834 PurpleIMConversation *im = purple_conversations_find_im_with_account(from, account);
835 char *buf = g_strdup_printf(_("%s has sent you a webcam invite, which is not yet supported."), from);
836 purple_conversation_write_system_message(PURPLE_CONVERSATION(im),
837 buf, PURPLE_MESSAGE_NOTIFY);
838 g_free(buf);
843 struct _yahoo_im {
844 char *from;
845 char *active_id;
846 int time;
847 int utf8;
848 int buddy_icon;
849 char *id;
850 char *msg;
853 static void yahoo_process_sms_message(PurpleConnection *gc, struct yahoo_packet *pkt)
855 PurpleAccount *account;
856 GSList *l = pkt->hash;
857 struct _yahoo_im *sms = NULL;
858 YahooData *yd;
859 char *server_msg = NULL;
860 char *m;
862 yd = purple_connection_get_protocol_data(gc);
863 account = purple_connection_get_account(gc);
865 while (l != NULL) {
866 struct yahoo_pair *pair = l->data;
867 if (pair->key == 4) {
868 if (g_utf8_validate(pair->value, -1, NULL)) {
869 sms = g_new0(struct _yahoo_im, 1);
870 sms->from = g_strdup_printf("+%s", pair->value);
871 sms->time = time(NULL);
872 sms->utf8 = TRUE;
873 } else {
874 purple_debug_warning("yahoo", "yahoo_process_sms_message "
875 "got non-UTF-8 string for key %d\n", pair->key);
877 } else if (pair->key == 14) {
878 if (sms)
879 sms->msg = pair->value;
880 } else if (pair->key == 68) {
881 if(sms)
882 g_hash_table_insert(yd->sms_carrier, g_strdup(sms->from), g_strdup(pair->value));
883 } else if (pair->key == 16) {
884 if (g_utf8_validate(pair->value, -1, NULL)) {
885 server_msg = pair->value;
886 } else {
887 purple_debug_warning("yahoo", "yahoo_process_sms_message "
888 "got non-UTF-8 string for key %d\n", pair->key);
891 l = l->next;
894 if(!sms) {
895 purple_debug_info("yahoo", "Received a malformed SMS packet!\n");
896 return;
899 if ((int)pkt->status < 0)
900 pkt->status = YAHOO_STATUS_DISCONNECTED;
901 if (pkt->status == YAHOO_STATUS_DISCONNECTED) {
902 if (server_msg) {
903 PurpleIMConversation *im;
904 im = purple_conversations_find_im_with_account(sms->from, account);
905 if (im == NULL)
906 im = purple_im_conversation_new(account, sms->from);
907 purple_conversation_write_system_message(PURPLE_CONVERSATION(im),
908 server_msg, 0);
910 else {
911 purple_notify_error(gc, NULL,
912 _("Your SMS was not delivered"), NULL,
913 purple_request_cpar_from_connection(gc));
916 g_free(sms->from);
917 g_free(sms);
918 return ;
921 if (!sms->from || !sms->msg) {
922 g_free(sms);
923 return;
926 m = yahoo_string_decode(gc, sms->msg, sms->utf8);
927 purple_serv_got_im(gc, sms->from, m, 0, sms->time);
929 g_free(m);
930 g_free(sms->from);
931 g_free(sms);
934 /* pkt_type is YAHOO_PKT_TYPE_SERVER if pkt arrives from yahoo server, YAHOO_PKT_TYPE_P2P if pkt arrives through p2p */
935 static void yahoo_process_message(PurpleConnection *gc, struct yahoo_packet *pkt, yahoo_pkt_type pkt_type)
937 PurpleAccount *account;
938 YahooData *yd = purple_connection_get_protocol_data(gc);
939 GSList *l = pkt->hash;
940 GSList *list = NULL;
941 struct _yahoo_im *im = NULL;
943 account = purple_connection_get_account(gc);
945 if (pkt->status <= 1 || pkt->status == 5 || pkt->status == YAHOO_STATUS_OFFLINE) {
946 /* messages are received with status YAHOO_STATUS_OFFLINE in case of p2p */
947 while (l != NULL) {
948 struct yahoo_pair *pair = l->data;
949 if (pair->key == 4 || pair->key == 1) {
950 if (g_utf8_validate(pair->value, -1, NULL)) {
951 im = g_new0(struct _yahoo_im, 1);
952 list = g_slist_append(list, im);
953 im->from = pair->value;
954 im->time = time(NULL);
955 im->utf8 = TRUE;
956 } else {
957 purple_debug_warning("yahoo", "yahoo_process_message "
958 "got non-UTF-8 string for key %d\n", pair->key);
960 } else if (im && pair->key == 5) {
961 im->active_id = pair->value;
962 } else if (pair->key == 97) {
963 if (im)
964 im->utf8 = strtol(pair->value, NULL, 10);
965 } else if (pair->key == 15) {
966 if (im)
967 im->time = strtol(pair->value, NULL, 10);
968 } else if (pair->key == 206) {
969 if (im)
970 im->buddy_icon = strtol(pair->value, NULL, 10);
971 } else if (pair->key == 14) {
972 if (im)
973 im->msg = pair->value;
974 } else if (im && (pair->key == 11)) {
975 /* peer session id */
976 /* disconnect the peer if connected through p2p and sends wrong value for session id */
977 if( (pkt_type == YAHOO_PKT_TYPE_P2P)
978 && (yd->session_id != strtol(pair->value, NULL, 10)) )
980 purple_debug_warning("yahoo","p2p: %s sent us message with wrong session id. Disconnecting p2p connection to peer\n", im->from);
981 /* remove from p2p connection lists, also calls yahoo_p2p_disconnect_destroy_data */
982 g_hash_table_remove(yd->peers, im->from);
983 g_free(im);
984 return; /* Not sure whether we should process remaining IMs in this packet */
987 } else if (im && pair->key == 63 && g_utf8_validate(pair->value, -1, NULL)) {
988 /* IMV key */
989 /* Check for the Doodle IMV */
990 if (im->from != NULL)
992 g_hash_table_replace(yd->imvironments, g_strdup(im->from), g_strdup(pair->value));
994 if (strstr(pair->value, "doodle;") != NULL)
996 PurpleWhiteboard *wb;
998 if (!purple_account_privacy_check(account, im->from)) {
999 purple_debug_info("yahoo", "Doodle request from %s dropped.\n",
1000 im->from);
1001 g_free(im);
1002 return;
1004 /* I'm not sure the following ever happens -DAA */
1005 wb = purple_whiteboard_get_session(account, im->from);
1007 /* If a Doodle session doesn't exist between this user */
1008 if(wb == NULL)
1010 doodle_session *ds;
1011 wb = purple_whiteboard_new(account, im->from,
1012 DOODLE_STATE_REQUESTED);
1013 ds = purple_whiteboard_get_protocol_data(wb);
1014 ds->imv_key = g_strdup(pair->value);
1016 yahoo_doodle_command_send_request(gc, im->from, pair->value);
1017 yahoo_doodle_command_send_ready(gc, im->from, pair->value);
1021 } else if (pair->key == 429) {
1022 if (im)
1023 im->id = pair->value;
1025 l = l->next;
1027 } else if (pkt->status == 2) {
1028 purple_notify_error(gc, NULL,
1029 _("Your Yahoo! message did not get sent."), NULL,
1030 purple_request_cpar_from_connection(gc));
1033 for (l = list; l; l = l->next) {
1034 char *m, *m2;
1035 im = l->data;
1037 if (!im->msg) {
1038 g_free(im);
1039 continue;
1042 if (!purple_account_privacy_check(account, im->from)) {
1043 purple_debug_info("yahoo", "Message from %s dropped.\n", im->from);
1044 return;
1048 * TODO: Is there anything else we should check when determining whether
1049 * we should send an acknowledgement?
1051 if (im->id != NULL) {
1052 /* Send acknowledgement. If we don't do this then the official
1053 * Yahoo Messenger client for Windows will send us the same
1054 * message 7 seconds later as an offline message. This is true
1055 * for at least version 9.0.0.2162 on Windows XP. */
1056 struct yahoo_packet *pkt2;
1057 pkt2 = yahoo_packet_new(YAHOO_SERVICE_MESSAGE_ACK,
1058 YAHOO_STATUS_AVAILABLE, pkt->id);
1059 yahoo_packet_hash(pkt2, "ssisii",
1060 1, im->active_id, /* May not always be the connection's display name */
1061 5, im->from,
1062 302, 430,
1063 430, im->id,
1064 303, 430,
1065 450, 0);
1066 yahoo_packet_send_and_free(pkt2, yd);
1069 m = yahoo_string_decode(gc, im->msg, im->utf8);
1070 /* This may actually not be necessary, but it appears
1071 * that at least at one point some clients were sending
1072 * "\r\n" as line delimiters, so we want to avoid double
1073 * lines. */
1074 m2 = purple_strreplace(m, "\r\n", "\n");
1075 g_free(m);
1076 m = m2;
1077 purple_util_chrreplace(m, '\r', '\n');
1078 if (!strcmp(m, "<ding>")) {
1079 char *username;
1081 username = g_markup_escape_text(im->from, -1);
1082 purple_protocol_got_attention(gc, username, YAHOO_BUZZ);
1083 g_free(username);
1084 g_free(m);
1085 g_free(im);
1086 continue;
1089 m2 = yahoo_codes_to_html(m);
1090 g_free(m);
1092 purple_serv_got_im(gc, im->from, m2, 0, im->time);
1093 g_free(m2);
1094 g_free(im);
1097 g_slist_free(list);
1100 static void yahoo_process_sysmessage(PurpleConnection *gc, struct yahoo_packet *pkt)
1102 GSList *l = pkt->hash;
1103 char *prim, *me = NULL, *msg = NULL;
1105 while (l) {
1106 struct yahoo_pair *pair = l->data;
1108 if (pair->key == 5) {
1109 if (g_utf8_validate(pair->value, -1, NULL)) {
1110 me = pair->value;
1111 } else {
1112 purple_debug_warning("yahoo", "yahoo_process_sysmessage "
1113 "got non-UTF-8 string for key %d\n", pair->key);
1115 } else if (pair->key == 14) {
1116 if (g_utf8_validate(pair->value, -1, NULL)) {
1117 msg = pair->value;
1118 } else {
1119 purple_debug_warning("yahoo", "yahoo_process_sysmessage "
1120 "got non-UTF-8 string for key %d\n", pair->key);
1124 l = l->next;
1127 if (!msg || !g_utf8_validate(msg, -1, NULL))
1128 return;
1130 prim = g_strdup_printf(_("Yahoo! system message for %s:"),
1131 me?me:purple_connection_get_display_name(gc));
1132 purple_notify_info(NULL, NULL, prim, msg,
1133 purple_request_cpar_from_connection(gc));
1134 g_free(prim);
1137 struct yahoo_add_request {
1138 PurpleConnection *gc;
1139 char *id;
1140 char *who;
1143 static void
1144 yahoo_buddy_add_authorize_cb(const char *message, gpointer data)
1146 struct yahoo_add_request *add_req = data;
1147 struct yahoo_packet *pkt;
1148 YahooData *yd = purple_connection_get_protocol_data(add_req->gc);
1149 const char *who = add_req->who;
1151 pkt = yahoo_packet_new(YAHOO_SERVICE_AUTH_REQ_15, YAHOO_STATUS_AVAILABLE, yd->session_id);
1152 yahoo_packet_hash(pkt, "ssii",
1153 1, add_req->id,
1154 5, who,
1155 13, 1,
1156 334, 0);
1158 yahoo_packet_send_and_free(pkt, yd);
1160 g_free(add_req->id);
1161 g_free(add_req->who);
1162 g_free(add_req);
1165 static void
1166 yahoo_buddy_add_deny_cb(const char *msg, gpointer data)
1168 struct yahoo_add_request *add_req = data;
1169 YahooData *yd = purple_connection_get_protocol_data(add_req->gc);
1170 struct yahoo_packet *pkt;
1171 char *encoded_msg = NULL;
1172 const char *who = add_req->who;
1174 if (msg && *msg)
1175 encoded_msg = yahoo_string_encode(add_req->gc, msg, FALSE);
1177 pkt = yahoo_packet_new(YAHOO_SERVICE_AUTH_REQ_15,
1178 YAHOO_STATUS_AVAILABLE, yd->session_id);
1180 yahoo_packet_hash(pkt, "ssiiis",
1181 1, add_req->id,
1182 5, who,
1183 13, 2,
1184 334, 0,
1185 97, 1, /* UTF-8 */
1186 14, encoded_msg ? encoded_msg : "");
1189 yahoo_packet_send_and_free(pkt, yd);
1191 g_free(encoded_msg);
1193 g_free(add_req->id);
1194 g_free(add_req->who);
1195 g_free(add_req);
1198 static void yahoo_buddy_denied_our_add(PurpleConnection *gc, const char *who, const char *reason)
1200 char *notify_msg;
1201 YahooData *yd = purple_connection_get_protocol_data(gc);
1203 if (who == NULL)
1204 return;
1206 if (reason != NULL) {
1207 char *msg2 = yahoo_string_decode(gc, reason, FALSE);
1208 notify_msg = g_strdup_printf(_("%s has (retroactively) denied your request to add them to your list for the following reason: %s."), who, msg2);
1209 g_free(msg2);
1210 } else
1211 notify_msg = g_strdup_printf(_("%s has (retroactively) denied your request to add them to your list."), who);
1213 purple_notify_info(gc, NULL, _("Add buddy rejected"), notify_msg,
1214 purple_request_cpar_from_connection(gc));
1215 g_free(notify_msg);
1217 g_hash_table_remove(yd->friends, who);
1218 purple_protocol_got_user_status(purple_connection_get_account(gc), who, "offline", NULL); /* FIXME: make this set not on list status instead */
1219 /* TODO: Shouldn't we remove the buddy from our local list? */
1222 static void yahoo_buddy_auth_req_15(PurpleConnection *gc, struct yahoo_packet *pkt) {
1223 PurpleAccount *account;
1224 GSList *l = pkt->hash;
1225 const char *msg = NULL;
1227 account = purple_connection_get_account(gc);
1229 /* Buddy authorized/declined our addition */
1230 if (pkt->status == 1) {
1231 char *who = NULL;
1232 int response = 0;
1234 while (l) {
1235 struct yahoo_pair *pair = l->data;
1237 switch (pair->key) {
1238 case 4:
1239 break;
1240 case 13:
1241 response = strtol(pair->value, NULL, 10);
1242 break;
1243 case 14:
1244 msg = pair->value;
1245 break;
1247 l = l->next;
1250 if (response == 1) /* Authorized */
1251 purple_debug_info("yahoo", "Received authorization from buddy '%s'.\n", who ? who : "(Unknown Buddy)");
1252 else if (response == 2) { /* Declined */
1253 purple_debug_info("yahoo", "Received authorization decline from buddy '%s'.\n", who ? who : "(Unknown Buddy)");
1254 yahoo_buddy_denied_our_add(gc, who, msg);
1255 } else
1256 purple_debug_error("yahoo", "Received unknown authorization response of %d from buddy '%s'.\n", response, who ? who : "(Unknown Buddy)");
1257 g_free(who);
1259 /* Buddy requested authorization to add us. */
1260 else if (pkt->status == 3) {
1261 struct yahoo_add_request *add_req;
1262 const char *firstname = NULL, *lastname = NULL;
1264 add_req = g_new0(struct yahoo_add_request, 1);
1265 add_req->gc = gc;
1267 while (l) {
1268 struct yahoo_pair *pair = l->data;
1270 switch (pair->key) {
1271 case 4:
1272 break;
1273 case 5:
1274 if (g_utf8_validate(pair->value, -1, NULL)) {
1275 add_req->id = g_strdup(pair->value);
1276 } else {
1277 purple_debug_warning("yahoo", "yahoo_buddy_auth_req_15 "
1278 "got non-UTF-8 string for key %d\n", pair->key);
1280 break;
1281 case 14:
1282 msg = pair->value;
1283 break;
1284 case 216:
1285 if (g_utf8_validate(pair->value, -1, NULL)) {
1286 firstname = pair->value;
1287 } else {
1288 purple_debug_warning("yahoo", "yahoo_buddy_auth_req_15 "
1289 "got non-UTF-8 string for key %d\n", pair->key);
1291 break;
1292 case 254:
1293 if (g_utf8_validate(pair->value, -1, NULL)) {
1294 lastname = pair->value;
1295 } else {
1296 purple_debug_warning("yahoo", "yahoo_buddy_auth_req_15 "
1297 "got non-UTF-8 string for key %d\n", pair->key);
1299 break;
1302 l = l->next;
1305 if (add_req->id && add_req->who) {
1306 char *alias = NULL, *dec_msg = NULL;
1308 if (!purple_account_privacy_check(account, add_req->who))
1310 purple_debug_misc("yahoo", "Auth. request from %s dropped and automatically denied due to privacy settings!\n",
1311 add_req->who);
1312 yahoo_buddy_add_deny_cb(NULL, add_req);
1313 return;
1316 if (msg)
1317 dec_msg = yahoo_string_decode(gc, msg, FALSE);
1319 if (firstname && lastname)
1320 alias = g_strdup_printf("%s %s", firstname, lastname);
1321 else if (firstname)
1322 alias = g_strdup(firstname);
1323 else if (lastname)
1324 alias = g_strdup(lastname);
1326 /* DONE! this is almost exactly the same as what MSN does,
1327 * this should probably be moved to the core.
1329 purple_account_request_authorization(account, add_req->who, add_req->id,
1330 alias, dec_msg,
1331 purple_blist_find_buddy(account, add_req->who) != NULL,
1332 yahoo_buddy_add_authorize_cb,
1333 yahoo_buddy_add_deny_cb,
1334 add_req);
1335 g_free(alias);
1336 g_free(dec_msg);
1337 } else {
1338 g_free(add_req->id);
1339 g_free(add_req->who);
1340 g_free(add_req);
1342 } else {
1343 purple_debug_error("yahoo", "Received authorization of unknown status (%d).\n", pkt->status);
1347 /* I don't think this happens anymore in Version 15 */
1348 static void yahoo_buddy_added_us(PurpleConnection *gc, struct yahoo_packet *pkt) {
1349 PurpleAccount *account;
1350 struct yahoo_add_request *add_req;
1351 char *msg = NULL;
1352 GSList *l = pkt->hash;
1354 account = purple_connection_get_account(gc);
1356 add_req = g_new0(struct yahoo_add_request, 1);
1357 add_req->gc = gc;
1359 while (l) {
1360 struct yahoo_pair *pair = l->data;
1362 switch (pair->key) {
1363 case 1:
1364 if (g_utf8_validate(pair->value, -1, NULL)) {
1365 add_req->id = g_strdup(pair->value);
1366 } else {
1367 purple_debug_warning("yahoo", "yahoo_buddy_added_us "
1368 "got non-UTF-8 string for key %d\n", pair->key);
1370 break;
1371 case 3:
1372 if (g_utf8_validate(pair->value, -1, NULL)) {
1373 add_req->who = g_strdup(pair->value);
1374 } else {
1375 purple_debug_warning("yahoo", "yahoo_buddy_added_us "
1376 "got non-UTF-8 string for key %d\n", pair->key);
1378 break;
1379 case 15: /* time, for when they add us and we're offline */
1380 break;
1381 case 14:
1382 msg = pair->value;
1383 break;
1385 l = l->next;
1388 if (add_req->id && add_req->who) {
1389 char *dec_msg = NULL;
1391 if (!purple_account_privacy_check(account, add_req->who)) {
1392 purple_debug_misc("yahoo", "Auth. request from %s dropped and automatically denied due to privacy settings!\n",
1393 add_req->who);
1394 yahoo_buddy_add_deny_cb(NULL, add_req);
1395 return;
1398 if (msg)
1399 dec_msg = yahoo_string_decode(gc, msg, FALSE);
1401 /* DONE! this is almost exactly the same as what MSN does,
1402 * this should probably be moved to the core.
1404 purple_account_request_authorization(account, add_req->who, add_req->id,
1405 NULL, dec_msg,
1406 purple_blist_find_buddy(account,add_req->who) != NULL,
1407 yahoo_buddy_add_authorize_cb,
1408 yahoo_buddy_add_deny_cb, add_req);
1409 g_free(dec_msg);
1410 } else {
1411 g_free(add_req->id);
1412 g_free(add_req->who);
1413 g_free(add_req);
1417 /* I have no idea if this every gets called in version 15 */
1418 static void yahoo_buddy_denied_our_add_old(PurpleConnection *gc, struct yahoo_packet *pkt)
1420 char *who = NULL;
1421 char *msg = NULL;
1422 GSList *l = pkt->hash;
1424 while (l) {
1425 struct yahoo_pair *pair = l->data;
1427 switch (pair->key) {
1428 case 3:
1429 if (g_utf8_validate(pair->value, -1, NULL)) {
1430 who = pair->value;
1431 } else {
1432 purple_debug_warning("yahoo", "yahoo_buddy_denied_our_add_old "
1433 "got non-UTF-8 string for key %d\n", pair->key);
1435 break;
1436 case 14:
1437 if (g_utf8_validate(pair->value, -1, NULL)) {
1438 msg = pair->value;
1439 } else {
1440 purple_debug_warning("yahoo", "yahoo_buddy_denied_our_add_old "
1441 "got non-UTF-8 string for key %d\n", pair->key);
1443 break;
1445 l = l->next;
1448 yahoo_buddy_denied_our_add(gc, who, msg);
1451 static void yahoo_process_contact(PurpleConnection *gc, struct yahoo_packet *pkt)
1453 switch (pkt->status) {
1454 case 1:
1455 yahoo_process_status(gc, pkt);
1456 return;
1457 case 3:
1458 yahoo_buddy_added_us(gc, pkt);
1459 break;
1460 case 7:
1461 yahoo_buddy_denied_our_add_old(gc, pkt);
1462 break;
1463 default:
1464 break;
1468 #define OUT_CHARSET "utf-8"
1470 static char *yahoo_decode(const char *text)
1472 char *converted = NULL;
1473 char *n, *new;
1474 const char *end, *p;
1475 int i, k;
1477 n = new = g_malloc(strlen (text) + 1);
1478 end = text + strlen(text);
1480 for (p = text; p < end; p++, n++) {
1481 if (*p == '\\') {
1482 if (p[1] >= '0' && p[1] <= '7') {
1483 p += 1;
1484 for (i = 0, k = 0; k < 3; k += 1) {
1485 char c = p[k];
1486 if (c < '0' || c > '7') break;
1487 i *= 8;
1488 i += c - '0';
1490 *n = i;
1491 p += k - 1;
1492 } else { /* bug 959248 */
1493 /* If we see a \ not followed by an octal number,
1494 * it means that it is actually a \\ with one \
1495 * already eaten by some unknown function.
1496 * This is arguably broken.
1498 * I think wing is wrong here, there is no function
1499 * called that I see that could have done it. I guess
1500 * it is just really sending single \'s. That's yahoo
1501 * for you.
1503 *n = *p;
1506 else
1507 *n = *p;
1510 *n = '\0';
1512 /* XXX: Is this related to Yahoo! Japan? If so, it should be removed. -mmcco */
1513 if (strstr(text, "\033$B"))
1514 converted = g_convert(new, n - new, OUT_CHARSET, "iso-2022-jp", NULL, NULL, NULL);
1515 if (!converted)
1516 converted = g_convert(new, n - new, OUT_CHARSET, "iso-8859-1", NULL, NULL, NULL);
1517 g_free(new);
1519 return converted;
1522 static void yahoo_process_mail(PurpleConnection *gc, struct yahoo_packet *pkt)
1524 PurpleAccount *account = purple_connection_get_account(gc);
1525 const char *who = NULL;
1526 const char *email = NULL;
1527 const char *subj = NULL;
1528 const char *yahoo_mail_url = YAHOO_MAIL_URL;
1529 int count = 0;
1530 GSList *l = pkt->hash;
1532 if (!purple_account_get_check_mail(account))
1533 return;
1535 while (l) {
1536 struct yahoo_pair *pair = l->data;
1537 if (pair->key == 9) {
1538 count = strtol(pair->value, NULL, 10);
1539 } else if (pair->key == 43) {
1540 if (g_utf8_validate(pair->value, -1, NULL)) {
1541 who = pair->value;
1542 } else {
1543 purple_debug_warning("yahoo", "yahoo_process_mail "
1544 "got non-UTF-8 string for key %d\n", pair->key);
1546 } else if (pair->key == 42) {
1547 if (g_utf8_validate(pair->value, -1, NULL)) {
1548 email = pair->value;
1549 } else {
1550 purple_debug_warning("yahoo", "yahoo_process_mail "
1551 "got non-UTF-8 string for key %d\n", pair->key);
1553 } else if (pair->key == 18) {
1554 if (g_utf8_validate(pair->value, -1, NULL)) {
1555 subj = pair->value;
1556 } else {
1557 purple_debug_warning("yahoo", "yahoo_process_mail "
1558 "got non-UTF-8 string for key %d\n", pair->key);
1561 l = l->next;
1564 if (who && subj && email && *email) {
1565 char *dec_who = yahoo_decode(who);
1566 char *dec_subj = yahoo_decode(subj);
1567 char *from = g_strdup_printf("%s (%s)", dec_who, email);
1569 purple_notify_email(gc, dec_subj, from, purple_account_get_username(account),
1570 yahoo_mail_url, NULL, NULL);
1572 g_free(dec_who);
1573 g_free(dec_subj);
1574 g_free(from);
1575 } else if (count > 0) {
1576 const char *tos[2] = { purple_account_get_username(account) };
1577 const char *urls[2] = { yahoo_mail_url };
1579 purple_notify_emails(gc, count, FALSE, NULL, NULL, tos, urls,
1580 NULL, NULL);
1584 /* We use this structure once while we authenticate */
1585 struct yahoo_auth_data
1587 PurpleConnection *gc;
1588 char *seed;
1591 /* This is the y64 alphabet... it's like base64, but has a . and a _ */
1592 static const char base64digits[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789._";
1594 /* This is taken from Sylpheed by Hiroyuki Yamamoto. We have our own tobase64 function
1595 * in util.c, but it is different from the one yahoo uses */
1596 static void to_y64(char *out, const unsigned char *in, gsize inlen)
1597 /* raw bytes in quasi-big-endian order to base 64 string (NUL-terminated) */
1599 for (; inlen >= 3; inlen -= 3)
1601 *out++ = base64digits[in[0] >> 2];
1602 *out++ = base64digits[((in[0] << 4) & 0x30) | (in[1] >> 4)];
1603 *out++ = base64digits[((in[1] << 2) & 0x3c) | (in[2] >> 6)];
1604 *out++ = base64digits[in[2] & 0x3f];
1605 in += 3;
1607 if (inlen > 0)
1609 unsigned char fragment;
1611 *out++ = base64digits[in[0] >> 2];
1612 fragment = (in[0] << 4) & 0x30;
1613 if (inlen > 1)
1614 fragment |= in[1] >> 4;
1615 *out++ = base64digits[fragment];
1616 *out++ = (inlen < 2) ? '-' : base64digits[(in[1] << 2) & 0x3c];
1617 *out++ = '-';
1619 *out = '\0';
1622 static void yahoo_auth16_stage3(PurpleConnection *gc, const char *crypt)
1624 YahooData *yd = purple_connection_get_protocol_data(gc);
1625 PurpleAccount *account = purple_connection_get_account(gc);
1626 const char *name = purple_normalize(account, purple_account_get_username(account));
1627 PurpleHash *md5_hash;
1628 guchar md5_digest[16];
1629 gchar base64_string[25];
1630 struct yahoo_packet *pkt;
1632 purple_debug_info("yahoo","Authentication: In yahoo_auth16_stage3\n");
1634 g_return_if_fail(crypt != NULL);
1636 md5_hash = purple_md5_hash_new();
1637 purple_hash_append(md5_hash, (guchar *)crypt, strlen(crypt));
1638 purple_hash_digest(md5_hash, md5_digest, sizeof(md5_digest));
1640 to_y64(base64_string, md5_digest, 16);
1642 purple_debug_info("yahoo", "yahoo status: %d\n", yd->current_status);
1643 pkt = yahoo_packet_new(YAHOO_SERVICE_AUTHRESP, yd->current_status, yd->session_id);
1645 if(yd->cookie_b) { /* send B cookie if we have it */
1646 yahoo_packet_hash(pkt, "ssssssssss",
1647 1, name,
1648 0, name,
1649 277, yd->cookie_y,
1650 278, yd->cookie_t,
1651 307, base64_string,
1652 244, YAHOO_CLIENT_VERSION_ID,
1653 2, name,
1654 2, "1",
1655 59, yd->cookie_b,
1656 98, purple_account_get_string(account, "room_list_locale", "us"),
1657 135, YAHOO_CLIENT_VERSION);
1658 } else { /* don't try to send an empty B cookie - the server will be mad */
1659 yahoo_packet_hash(pkt, "sssssssss",
1660 1, name,
1661 0, name,
1662 277, yd->cookie_y,
1663 278, yd->cookie_t,
1664 307, base64_string,
1665 244, YAHOO_CLIENT_VERSION_ID,
1666 2, name,
1667 2, "1",
1668 98, purple_account_get_string(account, "room_list_locale", "us"),
1669 135, YAHOO_CLIENT_VERSION);
1672 if (yd->picture_checksum)
1673 yahoo_packet_hash_int(pkt, 192, yd->picture_checksum);
1674 yahoo_packet_send_and_free(pkt, yd);
1676 g_object_unref(md5_hash);
1679 static void yahoo_auth16_stage2(PurpleHttpConnection *http_conn,
1680 PurpleHttpResponse *response, gpointer _auth_data)
1682 struct yahoo_auth_data *auth_data = _auth_data;
1683 PurpleConnection *gc = auth_data->gc;
1684 YahooData *yd = purple_connection_get_protocol_data(gc);
1686 int i;
1687 gchar **splits;
1688 int response_no = -1;
1689 char *crumb = NULL;
1690 char *crypt = NULL;
1691 PurpleHttpCookieJar *cookiejar;
1693 purple_debug_info("yahoo","Authentication: In yahoo_auth16_stage2\n");
1695 if (!purple_http_response_is_successful(response)) {
1696 const gchar *error_message = purple_http_response_get_error(response);
1697 purple_debug_error("yahoo", "Login Failed, unable to retrieve stage 2 url: %s\n", error_message);
1698 purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, error_message);
1699 g_free(auth_data->seed);
1700 g_free(auth_data);
1701 return;
1704 splits = g_strsplit(purple_http_response_get_data(response, NULL),
1705 "\r\n", -1);
1707 cookiejar = purple_http_conn_get_cookie_jar(http_conn);
1708 yd->cookie_b = purple_http_cookie_jar_get(cookiejar, "B");
1709 yd->cookie_t = purple_http_cookie_jar_get(cookiejar, "T");
1710 yd->cookie_y = purple_http_cookie_jar_get(cookiejar, "Y");
1712 i = 0;
1713 while (splits[i]) {
1714 /* I'm not exactly a fan of the magic numbers, but it's obvious,
1715 * so no sense in wasting a bajillion vars or calls to strlen */
1717 if (i == 0 && g_ascii_isdigit(splits[i][0])) {
1718 response_no = strtol(splits[i], NULL, 10);
1719 purple_debug_info("yahoo", "Got auth16 stage 2 response code: %d\n",
1720 response_no);
1721 } else if (strncmp(splits[i], "crumb=", 6) == 0) {
1722 crumb = g_strdup(&splits[i][6]);
1724 if (purple_debug_is_unsafe())
1725 purple_debug_info("yahoo", "Got crumb: %s\n", crumb);
1727 i++;
1730 g_strfreev(splits);
1732 if (crumb == NULL)
1733 response_no = -1;
1735 if(response_no != 0) {
1736 /* Some error in the login process */
1737 PurpleConnectionError error;
1738 char *error_reason = NULL;
1740 switch (response_no) {
1741 case -1:
1742 /* Some error in the received stream */
1743 error_reason = g_strdup(_("Received invalid data"));
1744 error = PURPLE_CONNECTION_ERROR_NETWORK_ERROR;
1745 break;
1746 case 100:
1747 /* Unknown error */
1748 error_reason = g_strdup(_("Unknown error"));
1749 error = PURPLE_CONNECTION_ERROR_OTHER_ERROR;
1750 break;
1751 default:
1752 /* if we have everything we need, why not try to login irrespective of response */
1753 if ((crumb != NULL) && (yd->cookie_y != NULL) && (yd->cookie_t != NULL)) {
1754 #if 0
1755 try_login_on_error = TRUE;
1756 #endif
1757 break;
1759 error_reason = g_strdup(_("Unknown error"));
1760 error = PURPLE_CONNECTION_ERROR_OTHER_ERROR;
1761 break;
1763 if (error_reason) {
1764 purple_debug_error("yahoo", "Authentication error: %s. "
1765 "Code %d\n", error_reason, response_no);
1766 purple_connection_error(gc, error, error_reason);
1767 g_free(error_reason);
1768 g_free(crumb);
1769 g_free(auth_data->seed);
1770 g_free(auth_data);
1771 return;
1775 crypt = g_strconcat(crumb, auth_data->seed, NULL);
1776 yahoo_auth16_stage3(gc, crypt);
1777 g_free(crypt);
1778 g_free(crumb);
1779 g_free(auth_data->seed);
1780 g_free(auth_data);
1783 static void yahoo_auth16_stage1_cb(PurpleHttpConnection *http_conn,
1784 PurpleHttpResponse *response, gpointer _auth_data)
1786 struct yahoo_auth_data *auth_data = _auth_data;
1787 PurpleConnection *gc = auth_data->gc;
1788 YahooData *yd = purple_connection_get_protocol_data(gc);
1790 purple_debug_info("yahoo","Authentication: In yahoo_auth16_stage1_cb\n");
1792 if (!purple_http_response_is_successful(response)) {
1793 const gchar *error_message = purple_http_response_get_error(response);
1794 purple_debug_error("yahoo", "Login Failed, unable to retrieve login url: %s\n", error_message);
1795 purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, error_message);
1796 g_free(auth_data->seed);
1797 g_free(auth_data);
1798 return;
1799 } else {
1800 PurpleAccount *account = purple_connection_get_account(gc);
1801 gchar **split_data = g_strsplit(purple_http_response_get_data(
1802 response, NULL), "\r\n", -1);
1803 int totalelements = 0;
1804 int response_no = -1;
1805 char *token = NULL;
1807 totalelements = g_strv_length(split_data);
1809 if(totalelements == 1) { /* Received an error code */
1810 response_no = strtol(split_data[0], NULL, 10);
1811 } else if(totalelements == 2 || totalelements == 3 ) { /* received valid data */
1812 response_no = strtol(split_data[0], NULL, 10);
1813 token = g_strdup(split_data[1] + strlen("ymsgr="));
1814 } else { /* It looks like a transparent proxy has returned a document we don't want */
1815 response_no = -1;
1818 g_strfreev(split_data);
1820 if(response_no != 0) {
1821 /* Some error in the login process */
1822 PurpleConnectionError error;
1823 char *error_reason;
1825 switch(response_no) {
1826 case -1:
1827 /* Some error in the received stream */
1828 error_reason = g_strdup(_("Received invalid data"));
1829 error = PURPLE_CONNECTION_ERROR_NETWORK_ERROR;
1830 break;
1831 case 1212:
1832 /* Password incorrect */
1833 /* Set password to NULL. Avoids account locking. Brings dialog to enter password if clicked on Re-enable account */
1834 if (!purple_account_get_remember_password(account))
1835 purple_account_set_password(account, NULL, NULL, NULL);
1836 error_reason = g_strdup(_("Incorrect password"));
1837 error = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED;
1838 break;
1839 case 1213:
1840 /* security lock from too many failed login attempts */
1841 error_reason = g_strdup(_("Account locked: Too many failed login "
1842 "attempts. Logging into the Yahoo! website may fix this."));
1843 error = PURPLE_CONNECTION_ERROR_OTHER_ERROR;
1844 break;
1845 case 1235:
1846 /* the username does not exist */
1847 error_reason = g_strdup(_("Username does not exist"));
1848 error = PURPLE_CONNECTION_ERROR_INVALID_USERNAME;
1849 break;
1850 case 1214:
1851 /* indicates a lock of some description */
1852 error_reason = g_strdup(_("Account locked: Unknown reason. Logging "
1853 "into the Yahoo! website may fix this."));
1854 error = PURPLE_CONNECTION_ERROR_OTHER_ERROR;
1855 break;
1856 case 1236:
1857 /* indicates a lock due to logging in too frequently */
1858 error_reason = g_strdup(_("Account locked: You have been logging in too "
1859 "frequently. Wait a few minutes before trying to connect "
1860 "again. Logging into the Yahoo! website may help."));
1861 error = PURPLE_CONNECTION_ERROR_OTHER_ERROR;
1862 break;
1863 case 100:
1864 /* username or password missing */
1865 error_reason = g_strdup(_("Username or password missing"));
1866 error = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED;
1867 break;
1868 default:
1869 /* Unknown error! */
1870 error_reason = g_strdup_printf(_("Unknown error (%d)"), response_no);
1871 error = PURPLE_CONNECTION_ERROR_OTHER_ERROR;
1872 break;
1874 purple_debug_error("yahoo", "Authentication error: %s. Code %d\n",
1875 error_reason, response_no);
1876 purple_connection_error(gc, error, error_reason);
1877 g_free(error_reason);
1878 g_free(auth_data->seed);
1879 g_free(auth_data);
1880 g_free(token);
1882 else {
1883 PurpleHttpRequest *req;
1885 req = purple_http_request_new(NULL);
1886 purple_http_request_set_url_printf(req, YAHOO_LOGIN_URL, token);
1887 purple_http_request_header_set(req, "User-Agent",
1888 YAHOO_CLIENT_USERAGENT);
1889 purple_http_connection_set_add(yd->http_reqs,
1890 purple_http_request(gc, req,
1891 yahoo_auth16_stage2, auth_data));
1892 purple_http_request_unref(req);
1894 g_free(token);
1899 static void yahoo_auth16_stage1(PurpleConnection *gc, const char *seed)
1901 YahooData *yd = purple_connection_get_protocol_data(gc);
1902 PurpleHttpRequest *req;
1903 struct yahoo_auth_data *auth_data = NULL;
1904 char *encoded_username;
1905 char *encoded_password;
1907 purple_debug_info("yahoo", "Authentication: In yahoo_auth16_stage1\n");
1909 auth_data = g_new0(struct yahoo_auth_data, 1);
1910 auth_data->gc = gc;
1911 auth_data->seed = g_strdup(seed);
1913 encoded_username = g_strdup(purple_url_encode(purple_account_get_username(purple_connection_get_account(gc))));
1914 encoded_password = g_strdup(purple_url_encode(purple_connection_get_password(gc)));
1916 req = purple_http_request_new(NULL);
1917 purple_http_request_set_url_printf(req, YAHOO_TOKEN_URL,
1918 encoded_username, encoded_password, purple_url_encode(seed));
1919 purple_http_request_header_set(req, "User-Agent", YAHOO_CLIENT_USERAGENT);
1920 purple_http_connection_set_add(yd->http_reqs, purple_http_request(gc,
1921 req, yahoo_auth16_stage1_cb, auth_data));
1922 purple_http_request_unref(req);
1924 purple_str_wipe(encoded_password);
1925 g_free(encoded_username);
1928 static void yahoo_process_auth(PurpleConnection *gc, struct yahoo_packet *pkt)
1930 char *seed = NULL;
1931 GSList *l = pkt->hash;
1932 int m = 0;
1933 gchar *buf;
1935 while (l) {
1936 struct yahoo_pair *pair = l->data;
1937 /* (pair->key == 1) -> sn */
1938 if (pair->key == 94) {
1939 if (g_utf8_validate(pair->value, -1, NULL)) {
1940 seed = pair->value;
1941 } else {
1942 purple_debug_warning("yahoo", "yahoo_process_auth "
1943 "got non-UTF-8 string for key %d\n", pair->key);
1945 } else if (pair->key == 13) {
1946 m = atoi(pair->value);
1948 l = l->next;
1951 if (seed) {
1952 switch (m) {
1953 case 0:
1954 /* used to be for really old auth routine, dont support now */
1955 case 1:
1956 case 2: /* Yahoo ver 16 authentication */
1957 yahoo_auth16_stage1(gc, seed);
1958 break;
1959 default:
1961 GHashTable *ui_info = purple_core_get_ui_info();
1963 buf = g_strdup_printf(_("The Yahoo server has requested the use of an unrecognized "
1964 "authentication method. You will probably not be able "
1965 "to successfully sign on to Yahoo. Check %s for updates."),
1966 ((ui_info && g_hash_table_lookup(ui_info, "website")) ? (char *)g_hash_table_lookup(ui_info, "website") : PURPLE_WEBSITE));
1967 purple_notify_error(gc, "",
1968 _("Failed Yahoo! Authentication"), buf,
1969 purple_request_cpar_from_connection(gc));
1970 g_free(buf);
1971 yahoo_auth16_stage1(gc, seed); /* Can't hurt to try it anyway. */
1972 break;
1978 static void ignore_buddy(PurpleBuddy *buddy) {
1979 PurpleGroup *group;
1980 PurpleAccount *account;
1981 gchar *name;
1983 if (!buddy)
1984 return;
1986 group = purple_buddy_get_group(buddy);
1987 name = g_strdup(purple_buddy_get_name(buddy));
1988 account = purple_buddy_get_account(buddy);
1990 purple_debug_info("yahoo", "blist: Removing '%s' from buddy list.\n", name);
1991 purple_account_remove_buddy(account, buddy, group);
1992 purple_blist_remove_buddy(buddy);
1994 purple_serv_add_deny(purple_account_get_connection(account), name);
1996 g_free(name);
1999 static void keep_buddy(PurpleBuddy *b)
2001 purple_account_privacy_deny_remove(purple_buddy_get_account(b),
2002 purple_buddy_get_name(b), 1);
2005 static void yahoo_process_ignore(PurpleConnection *gc, struct yahoo_packet *pkt) {
2006 PurpleBuddy *b;
2007 GSList *l;
2008 gchar *who = NULL;
2009 gchar buf[BUF_LONG];
2010 gboolean ignore = TRUE;
2011 gint status = 0;
2013 for (l = pkt->hash; l; l = l->next) {
2014 struct yahoo_pair *pair = l->data;
2015 switch (pair->key) {
2016 case 0:
2017 if (g_utf8_validate(pair->value, -1, NULL)) {
2018 who = pair->value;
2019 } else {
2020 purple_debug_warning("yahoo", "yahoo_process_ignore "
2021 "got non-UTF-8 string for key %d\n", pair->key);
2023 break;
2024 /* 1 -> me */
2025 case 13:
2026 /* 1 == ignore, 2 == unignore */
2027 ignore = (strtol(pair->value, NULL, 10) == 1);
2028 break;
2029 case 66:
2030 status = strtol(pair->value, NULL, 10);
2031 break;
2032 default:
2033 break;
2038 * status
2039 * 0 - ok
2040 * 2 - already in ignore list, could not add
2041 * 3 - not in ignore list, could not delete
2042 * 12 - is a buddy, could not add (and possibly also a not-in-ignore list condition?)
2044 switch (status) {
2045 case 12:
2046 purple_debug_info("yahoo", "Server reported \"is a buddy\" for %s while %s",
2047 who, (ignore ? "ignoring" : "unignoring"));
2049 if (ignore) {
2050 b = purple_blist_find_buddy(purple_connection_get_account(gc), who);
2051 g_snprintf(buf, sizeof(buf), _("You have tried to ignore %s, but the "
2052 "user is on your buddy list. Clicking \"Yes\" "
2053 "will remove and ignore the buddy."), who);
2054 purple_request_yes_no(gc, NULL,
2055 _("Ignore buddy?"), buf, 0,
2056 purple_request_cpar_from_connection(gc),
2057 b, G_CALLBACK(ignore_buddy),
2058 G_CALLBACK(keep_buddy));
2059 break;
2061 case 2:
2062 purple_debug_info("yahoo", "Server reported that %s is already in the ignore list.\n",
2063 who);
2064 break;
2065 case 3:
2066 purple_debug_info("yahoo", "Server reported that %s is not in the ignore list; could not delete\n",
2067 who);
2068 case 0:
2069 default:
2070 break;
2074 #if TRY_WEBMESSENGER_LOGIN
2076 static gboolean
2077 yahoo_try_webmessenger_login(PurpleConnection *gc)
2079 YahooData *yd = purple_connection_get_protocol_data(gc);
2080 PurpleHttpRequest *req;
2082 if (yd->wm)
2083 return FALSE;
2085 yd->wm = TRUE;
2086 if (yd->fd >= 0)
2087 close(yd->fd);
2088 if (yd->inpa) {
2089 purple_input_remove(yd->inpa);
2090 yd->inpa = 0;
2093 req = purple_http_request_new(WEBMESSENGER_URL);
2094 purple_http_request_header_set(req, "User-Agent", "Purple/" VERSION);
2095 purple_http_connection_set_add(yd->http_reqs, purple_http_request(gc,
2096 req, yahoo_login_page_cb, NULL));
2097 purple_http_request_unref(req);
2099 return TRUE;
2102 #endif /* TRY_WEBMESSENGER_LOGIN */
2104 static void yahoo_process_authresp(PurpleConnection *gc, struct yahoo_packet *pkt)
2106 GSList *l = pkt->hash;
2107 int err = 0;
2108 char *msg;
2109 char *url = NULL;
2110 char *fullmsg;
2111 PurpleAccount *account = purple_connection_get_account(gc);
2112 PurpleConnectionError reason = PURPLE_CONNECTION_ERROR_OTHER_ERROR;
2114 while (l) {
2115 struct yahoo_pair *pair = l->data;
2117 if (pair->key == 66) {
2118 err = strtol(pair->value, NULL, 10);
2119 } else if (pair->key == 20) {
2120 if (g_utf8_validate(pair->value, -1, NULL)) {
2121 url = pair->value;
2122 } else {
2123 purple_debug_warning("yahoo", "yahoo_process_authresp "
2124 "got non-UTF-8 string for key %d\n", pair->key);
2128 l = l->next;
2131 switch (err) {
2132 case 0:
2133 msg = g_strdup(_("Unknown error"));
2134 reason = PURPLE_CONNECTION_ERROR_NETWORK_ERROR;
2135 break;
2136 case 3:
2137 msg = g_strdup(_("Username does not exist"));
2138 reason = PURPLE_CONNECTION_ERROR_INVALID_USERNAME;
2139 break;
2140 case 13:
2141 #if TRY_WEBMESSENGER_LOGIN
2142 if (yahoo_try_webmessenger_login(gc))
2143 return;
2144 #else
2145 purple_debug_info("yahoo", "Web messenger login is disabled\n");
2146 #endif /* TRY_WEBMESSENGER_LOGIN */
2147 if (!purple_account_get_remember_password(account))
2148 purple_account_set_password(account, NULL, NULL, NULL);
2150 msg = g_strdup(_("Invalid username or password"));
2151 reason = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED;
2152 break;
2153 case 14:
2154 msg = g_strdup(_("Your account has been locked due to too many failed login attempts."
2155 " Please try logging into the Yahoo! website."));
2156 reason = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED;
2157 break;
2158 case 52:
2159 /* See #9660. As much as we know, reconnecting shouldn't hurt */
2160 purple_debug_info("yahoo", "Got error 52, Set to autoreconnect\n");
2161 msg = g_strdup(_("Unknown error 52. Reconnecting should fix this."));
2162 reason = PURPLE_CONNECTION_ERROR_NETWORK_ERROR;
2163 break;
2164 case 1013:
2165 msg = g_strdup(_("Error 1013: The username you have entered is invalid."
2166 " The most common cause of this error is entering your email"
2167 " address instead of your Yahoo! ID."));
2168 reason = PURPLE_CONNECTION_ERROR_INVALID_USERNAME;
2169 break;
2170 default:
2171 msg = g_strdup_printf(_("Unknown error number %d. Logging into the Yahoo! website may fix this."), err);
2174 if (url)
2175 fullmsg = g_strdup_printf("%s\n%s", msg, url);
2176 else
2177 fullmsg = g_strdup(msg);
2179 purple_connection_error(gc, reason, fullmsg);
2180 g_free(msg);
2181 g_free(fullmsg);
2184 static void yahoo_process_addbuddy(PurpleConnection *gc, struct yahoo_packet *pkt)
2186 int err = 0;
2187 char *who = NULL;
2188 char *temp = NULL;
2189 char *group = NULL;
2190 char *decoded_group;
2191 char *buf;
2192 YahooFriend *f;
2193 GSList *l = pkt->hash;
2194 YahooData *yd = purple_connection_get_protocol_data(gc);
2196 while (l) {
2197 struct yahoo_pair *pair = l->data;
2199 switch (pair->key) {
2200 case 66:
2201 err = strtol(pair->value, NULL, 10);
2202 break;
2203 case 7:
2204 if (g_utf8_validate(pair->value, -1, NULL)) {
2205 temp = pair->value;
2206 } else {
2207 purple_debug_warning("yahoo", "yahoo_process_addbuddy "
2208 "got non-UTF-8 string for key %d\n", pair->key);
2210 break;
2211 case 65:
2212 group = pair->value;
2213 break;
2216 l = l->next;
2219 if (!temp)
2220 return;
2221 if (!group)
2222 group = "";
2224 who = g_strdup(temp);
2226 if (!err || (err == 2)) { /* 0 = ok, 2 = already on serv list */
2227 f = yahoo_friend_find_or_new(gc, who);
2228 yahoo_update_status(gc, who, f);
2230 if( !g_hash_table_lookup(yd->peers, who) ) {
2231 /* we are not connected as client, so set friend to not connected */
2232 yahoo_friend_set_p2p_status(f, YAHOO_P2PSTATUS_NOT_CONNECTED);
2233 f->p2p_packet_sent = 0;
2235 else /* we are already connected. set friend to YAHOO_P2PSTATUS_WE_ARE_CLIENT */
2236 yahoo_friend_set_p2p_status(f, YAHOO_P2PSTATUS_WE_ARE_CLIENT);
2237 g_free(who);
2238 return;
2241 decoded_group = yahoo_string_decode(gc, group, FALSE);
2242 buf = g_strdup_printf(_("Unable to add buddy %s to group %s to the server list on account %s."),
2243 who, decoded_group, purple_connection_get_display_name(gc));
2244 if (!purple_conversation_present_error(who, purple_connection_get_account(gc), buf))
2245 purple_notify_error(gc, NULL, _("Unable to add buddy to server list"), buf,
2246 purple_request_cpar_from_connection(gc));
2247 g_free(buf);
2248 g_free(decoded_group);
2249 g_free(who);
2252 /* write pkt to the source */
2253 static void yahoo_p2p_write_pkt(gint source, struct yahoo_packet *pkt)
2255 size_t pkt_len;
2256 gssize written;
2257 guchar *raw_packet;
2259 /*build the raw packet and send it to the host*/
2260 pkt_len = yahoo_packet_build(pkt, 0, 0, &raw_packet);
2261 written = write(source, raw_packet, pkt_len);
2262 if (written < 0 || (gsize)written != pkt_len)
2263 purple_debug_warning("yahoo","p2p: couldn't write to the source\n");
2264 g_free(raw_packet);
2267 static void yahoo_p2p_keepalive_cb(gpointer key, gpointer value, gpointer user_data)
2269 struct yahoo_p2p_data *p2p_data = value;
2270 PurpleConnection *gc = user_data;
2271 struct yahoo_packet *pkt_to_send;
2272 PurpleAccount *account;
2273 YahooData *yd = purple_connection_get_protocol_data(gc);
2275 account = purple_connection_get_account(gc);
2277 pkt_to_send = yahoo_packet_new(YAHOO_SERVICE_P2PFILEXFER, YAHOO_STATUS_AVAILABLE, yd->session_id);
2278 yahoo_packet_hash(pkt_to_send, "ssisi",
2279 4, purple_normalize(account, purple_account_get_username(account)),
2280 5, p2p_data->host_username,
2281 241, 0, /* Protocol identifier */
2282 49, "PEERTOPEER",
2283 13, 7);
2284 yahoo_p2p_write_pkt(p2p_data->source, pkt_to_send);
2286 yahoo_packet_free(pkt_to_send);
2289 static gboolean yahoo_p2p_keepalive(gpointer data)
2291 PurpleConnection *gc = data;
2292 YahooData *yd = purple_connection_get_protocol_data(gc);
2294 g_hash_table_foreach(yd->peers, yahoo_p2p_keepalive_cb, gc);
2296 return TRUE;
2299 /* destroy p2p_data associated with a peer and close p2p connection.
2300 * g_hash_table_remove() calls this function to destroy p2p_data associated with the peer,
2301 * call g_hash_table_remove() instead of this fucntion if peer has an entry in the table */
2302 static void yahoo_p2p_disconnect_destroy_data(gpointer data)
2304 struct yahoo_p2p_data *p2p_data;
2305 YahooFriend *f;
2307 if(!(p2p_data = data))
2308 return ;
2310 /* If friend, set him not connected */
2311 f = yahoo_friend_find(p2p_data->gc, p2p_data->host_username);
2312 if (f)
2313 yahoo_friend_set_p2p_status(f, YAHOO_P2PSTATUS_NOT_CONNECTED);
2315 if(p2p_data->source >= 0)
2316 close(p2p_data->source);
2317 if (p2p_data->input_event > 0)
2318 purple_input_remove(p2p_data->input_event);
2319 g_free(p2p_data->host_ip);
2320 g_free(p2p_data->host_username);
2321 g_free(p2p_data);
2324 /* exchange of initial p2pfilexfer packets, service type YAHOO_SERVICE_P2PFILEXFER */
2325 static void yahoo_p2p_process_p2pfilexfer(gpointer data, gint source, struct yahoo_packet *pkt)
2327 struct yahoo_p2p_data *p2p_data;
2328 char *who = NULL;
2329 GSList *l = pkt->hash;
2330 struct yahoo_packet *pkt_to_send;
2331 PurpleAccount *account;
2332 int val_13_to_send = 0;
2333 YahooData *yd;
2334 YahooFriend *f;
2336 if(!(p2p_data = data))
2337 return ;
2339 yd = purple_connection_get_protocol_data(p2p_data->gc);
2341 /* lets see whats in the packet */
2342 while (l) {
2343 struct yahoo_pair *pair = l->data;
2345 switch (pair->key) {
2346 case 4:
2347 if (g_utf8_validate(pair->value, -1, NULL)) {
2348 who = pair->value;
2349 if(strncmp(who, p2p_data->host_username, strlen(p2p_data->host_username)) != 0) {
2350 /* from whom are we receiving the packets ?? */
2351 purple_debug_warning("yahoo","p2p: received data from wrong user\n");
2352 return;
2354 } else {
2355 purple_debug_warning("yahoo", "yahoo_p2p_process_p2pfilexfer "
2356 "got non-UTF-8 string for key %d\n", pair->key);
2358 break;
2359 case 13:
2360 p2p_data->val_13 = strtol(pair->value, NULL, 10); /* Value should be 5-7 */
2361 break;
2362 /* case 5, 49 look laters, no use right now */
2364 l = l->next;
2367 account = purple_connection_get_account(p2p_data->gc);
2369 /* key_13: sort of a counter.
2370 * WHEN WE ARE CLIENT: yahoo server sends val_13 = 0, we send to peer val_13 = 1, receive back val_13 = 5,
2371 * we send val_13=6, receive val_13=7, we send val_13=7, HALT. Keep sending val_13 = 7 as keep alive.
2372 * WHEN WE ARE SERVER: we send val_13 = 0 to yahoo server, peer sends us val_13 = 1, we send val_13 = 5,
2373 * receive val_13 = 6, send val_13 = 7, receive val_13 = 7. HALT. Keep sending val_13 = 7 as keep alive. */
2375 switch(p2p_data->val_13) {
2376 case 1 : val_13_to_send = 5; break;
2377 case 5 : val_13_to_send = 6; break;
2378 case 6 : val_13_to_send = 7; break;
2379 case 7 : if( g_hash_table_lookup(yd->peers, p2p_data->host_username) )
2380 return;
2381 val_13_to_send = 7; break;
2382 default: purple_debug_warning("yahoo","p2p:Unknown value for key 13\n");
2383 return;
2386 /* Build the yahoo packet */
2387 pkt_to_send = yahoo_packet_new(YAHOO_SERVICE_P2PFILEXFER, YAHOO_STATUS_AVAILABLE, yd->session_id);
2388 yahoo_packet_hash(pkt_to_send, "ssisi",
2389 4, purple_normalize(account, purple_account_get_username(account)),
2390 5, p2p_data->host_username,
2391 241, 0, /* Protocol identifier */
2392 49, "PEERTOPEER",
2393 13, val_13_to_send);
2395 /* build the raw packet and send it to the host */
2396 yahoo_p2p_write_pkt(source, pkt_to_send);
2397 yahoo_packet_free(pkt_to_send);
2399 if( val_13_to_send == 7 )
2400 if( !g_hash_table_lookup(yd->peers, p2p_data->host_username) ) {
2401 g_hash_table_insert(yd->peers, g_strdup(p2p_data->host_username), p2p_data);
2402 /* If the peer is a friend, set him connected */
2403 f = yahoo_friend_find(p2p_data->gc, p2p_data->host_username);
2404 if (f) {
2405 if(p2p_data->connection_type == YAHOO_P2P_WE_ARE_SERVER) {
2406 p2p_data->session_id = f->session_id;
2407 yahoo_friend_set_p2p_status(f, YAHOO_P2PSTATUS_WE_ARE_SERVER);
2409 else
2410 yahoo_friend_set_p2p_status(f, YAHOO_P2PSTATUS_WE_ARE_CLIENT);
2415 /* callback function associated with receiving of data, not considering receipt of multiple YMSG packets in a single TCP packet */
2416 static void yahoo_p2p_read_pkt_cb(gpointer data, gint source, PurpleInputCondition cond)
2418 guchar buf[1024]; /* is it safe to assume a fixed array length of 1024 ?? */
2419 int len;
2420 int pos = 0;
2421 int pktlen;
2422 struct yahoo_packet *pkt;
2423 guchar *start;
2424 struct yahoo_p2p_data *p2p_data;
2425 YahooData *yd;
2427 if(!(p2p_data = data))
2428 return ;
2429 yd = purple_connection_get_protocol_data(p2p_data->gc);
2431 len = read(source, buf, sizeof(buf));
2432 if ((len < 0) && ((errno == EAGAIN) || (errno == EWOULDBLOCK)))
2433 return ; /* No Worries*/
2434 else if (len <= 0)
2436 purple_debug_warning("yahoo","p2p: Error in connection, or host disconnected\n");
2437 /* remove from p2p connection lists, also calls yahoo_p2p_disconnect_destroy_data */
2438 if( g_hash_table_lookup(yd->peers, p2p_data->host_username) )
2439 g_hash_table_remove(yd->peers,p2p_data->host_username);
2440 else
2441 yahoo_p2p_disconnect_destroy_data(data);
2442 return;
2445 /* TODO: It looks like there's a bug here (and above) where an incorrect
2446 * assumtion is being made that the buffer will be added to when this
2447 * is next called, but that's not really the case! */
2448 if(len < YAHOO_PACKET_HDRLEN)
2449 return;
2451 if(strncmp((char *)buf, "YMSG", 4) != 0) {
2452 /* Not a YMSG packet */
2453 purple_debug_warning("yahoo", "p2p: Got something other than YMSG packet\n");
2455 start = (guchar *) g_strstr_len((char *) buf + 1, len - 1 ,"YMSG");
2456 if (start == NULL) {
2457 /* remove from p2p connection lists, also calls yahoo_p2p_disconnect_destroy_data */
2458 if (g_hash_table_lookup(yd->peers, p2p_data->host_username))
2459 g_hash_table_remove(yd->peers, p2p_data->host_username);
2460 else
2461 yahoo_p2p_disconnect_destroy_data(data);
2462 return;
2464 purple_debug_warning("yahoo","p2p: Got something other than YMSG packet\n");
2466 len -= (start - buf);
2467 g_memmove(buf, start, len);
2470 pos += 4; /* YMSG */
2471 pos += 2;
2472 pos += 2;
2474 pktlen = yahoo_get16(buf + pos); pos += 2;
2475 if (len < (YAHOO_PACKET_HDRLEN + pktlen)) {
2476 purple_debug_error("yahoo", "p2p: packet length(%d) > buffer length(%d)\n",
2477 pktlen, (len - pos));
2478 /* remove from p2p connection lists, also calls yahoo_p2p_disconnect_destroy_data */
2479 if (g_hash_table_lookup(yd->peers, p2p_data->host_username))
2480 g_hash_table_remove(yd->peers, p2p_data->host_username);
2481 else
2482 yahoo_p2p_disconnect_destroy_data(data);
2483 return;
2484 } else
2485 purple_debug_misc("yahoo", "p2p: %d bytes to read\n", pktlen);
2487 pkt = yahoo_packet_new(0, 0, 0);
2488 pkt->service = yahoo_get16(buf + pos); pos += 2;
2489 pkt->status = yahoo_get32(buf + pos); pos += 4;
2490 pkt->id = yahoo_get32(buf + pos); pos += 4;
2492 purple_debug_misc("yahoo", "p2p: Yahoo Service: 0x%02x Status: %d\n",pkt->service, pkt->status);
2493 yahoo_packet_read(pkt, buf + pos, pktlen);
2495 /* packet processing */
2496 switch(pkt->service) {
2497 case YAHOO_SERVICE_P2PFILEXFER:
2498 yahoo_p2p_process_p2pfilexfer(data, source, pkt);
2499 break;
2500 case YAHOO_SERVICE_MESSAGE:
2501 yahoo_process_message(p2p_data->gc, pkt, YAHOO_PKT_TYPE_P2P);
2502 break;
2503 case YAHOO_SERVICE_NOTIFY:
2504 yahoo_process_notify(p2p_data->gc, pkt, YAHOO_PKT_TYPE_P2P);
2505 break;
2506 default:
2507 purple_debug_warning("yahoo","p2p: p2p service %d Unhandled\n",pkt->service);
2510 yahoo_packet_free(pkt);
2513 static void yahoo_p2p_server_send_connected_cb(gpointer data, gint source, PurpleInputCondition cond)
2515 int acceptfd;
2516 struct yahoo_p2p_data *p2p_data;
2517 YahooData *yd;
2519 if(!(p2p_data = data))
2520 return ;
2521 yd = purple_connection_get_protocol_data(p2p_data->gc);
2523 acceptfd = accept(source, NULL, 0);
2524 if(acceptfd == -1 && (errno == EAGAIN || errno == EWOULDBLOCK))
2525 return;
2526 else if(acceptfd == -1) {
2527 purple_debug_warning("yahoo","yahoo_p2p_server_send_connected_cb: accept: %s\n", g_strerror(errno));
2528 yahoo_p2p_disconnect_destroy_data(data);
2529 return;
2532 /* remove timeout */
2533 if (yd->yahoo_p2p_server_timeout_handle) {
2534 purple_timeout_remove(yd->yahoo_p2p_server_timeout_handle);
2535 yd->yahoo_p2p_server_timeout_handle = 0;
2538 /* remove watcher and close p2p server */
2539 if (yd->yahoo_p2p_server_watcher) {
2540 purple_input_remove(yd->yahoo_p2p_server_watcher);
2541 yd->yahoo_p2p_server_watcher = 0;
2543 if (yd->yahoo_local_p2p_server_fd >= 0) {
2544 close(yd->yahoo_local_p2p_server_fd);
2545 yd->yahoo_local_p2p_server_fd = -1;
2548 /* Add an Input Read event to the file descriptor */
2549 p2p_data->input_event = purple_input_add(acceptfd, PURPLE_INPUT_READ, yahoo_p2p_read_pkt_cb, data);
2550 p2p_data->source = acceptfd;
2553 static gboolean yahoo_cancel_p2p_server_listen_cb(gpointer data)
2555 struct yahoo_p2p_data *p2p_data;
2556 YahooData *yd;
2558 if(!(p2p_data = data))
2559 return FALSE;
2561 yd = purple_connection_get_protocol_data(p2p_data->gc);
2563 purple_debug_warning("yahoo","yahoo p2p server timeout, peer failed to connect\n");
2564 yahoo_p2p_disconnect_destroy_data(data);
2565 purple_input_remove(yd->yahoo_p2p_server_watcher);
2566 yd->yahoo_p2p_server_watcher = 0;
2567 close(yd->yahoo_local_p2p_server_fd);
2568 yd->yahoo_local_p2p_server_fd = -1;
2569 yd->yahoo_p2p_server_timeout_handle = 0;
2571 return FALSE;
2574 static void yahoo_p2p_server_listen_cb(int listenfd, gpointer data)
2576 struct yahoo_p2p_data *p2p_data;
2577 YahooData *yd;
2579 if(!(p2p_data = data))
2580 return ;
2582 yd = purple_connection_get_protocol_data(p2p_data->gc);
2583 yd->listen_data = NULL;
2585 if(listenfd == -1) {
2586 purple_debug_warning("yahoo","p2p: error starting p2p server\n");
2587 yahoo_p2p_disconnect_destroy_data(data);
2588 return;
2591 /* Add an Input Read event to the file descriptor */
2592 yd->yahoo_local_p2p_server_fd = listenfd;
2593 yd->yahoo_p2p_server_watcher = purple_input_add(listenfd, PURPLE_INPUT_READ, yahoo_p2p_server_send_connected_cb,data);
2595 /* add timeout */
2596 yd->yahoo_p2p_server_timeout_handle = purple_timeout_add_seconds(YAHOO_P2P_SERVER_TIMEOUT, yahoo_cancel_p2p_server_listen_cb, data);
2599 /* send p2p pkt containing our encoded ip, asking peer to connect to us */
2600 void yahoo_send_p2p_pkt(PurpleConnection *gc, const char *who, int val_13)
2602 const char *public_ip;
2603 guint32 temp[4];
2604 guint32 ip;
2605 char temp_str[100];
2606 gchar *base64_ip = NULL;
2607 YahooFriend *f;
2608 struct yahoo_packet *pkt;
2609 PurpleAccount *account;
2610 YahooData *yd = purple_connection_get_protocol_data(gc);
2611 struct yahoo_p2p_data *p2p_data;
2612 const char *norm_username;
2614 f = yahoo_friend_find(gc, who);
2615 account = purple_connection_get_account(gc);
2617 /* Do not send invitation if already listening for other connection */
2618 if(yd->yahoo_local_p2p_server_fd >= 0)
2619 return;
2621 /* One shouldn't try to connect to self */
2622 if( strcmp(purple_normalize(account, purple_account_get_username(account)), who) == 0)
2623 return;
2625 /* 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 */
2626 if( !( f && (yahoo_friend_get_p2p_status(f) == YAHOO_P2PSTATUS_NOT_CONNECTED) && (f->p2p_packet_sent == 0)) )
2627 return;
2629 /* Finally, don't try to connect to buddies not online or on sms */
2630 if( (f->status == YAHOO_STATUS_OFFLINE) || f->sms )
2631 return;
2633 public_ip = purple_network_get_public_ip();
2634 if( (sscanf(public_ip, "%u.%u.%u.%u", &temp[0], &temp[1], &temp[2], &temp[3])) !=4 )
2635 return ;
2637 ip = (temp[3] << 24) | (temp[2] <<16) | (temp[1] << 8) | temp[0];
2638 sprintf(temp_str, "%d", ip);
2639 base64_ip = purple_base64_encode( (guchar *)temp_str, strlen(temp_str) );
2641 norm_username = purple_normalize(account, purple_account_get_username(account));
2642 pkt = yahoo_packet_new(YAHOO_SERVICE_PEERTOPEER, YAHOO_STATUS_AVAILABLE, 0);
2643 yahoo_packet_hash(pkt, "sssissis",
2644 1, norm_username,
2645 4, norm_username,
2646 12, base64_ip, /* base64 encode ip */
2647 61, 0, /* To-do : figure out what is 61 for?? */
2648 2, "",
2649 5, who,
2650 13, val_13,
2651 49, "PEERTOPEER");
2652 yahoo_packet_send_and_free(pkt, yd);
2654 f->p2p_packet_sent = 1; /* set p2p_packet_sent to sent */
2656 p2p_data = g_new0(struct yahoo_p2p_data, 1);
2658 p2p_data->gc = gc;
2659 p2p_data->host_ip = NULL;
2660 p2p_data->host_username = g_strdup(who);
2661 p2p_data->val_13 = val_13;
2662 p2p_data->connection_type = YAHOO_P2P_WE_ARE_SERVER;
2663 p2p_data->source = -1;
2665 /* FIXME: If the port is already used, purple_network_listener returns NULL and old listener won't be canceled
2666 * in yahoo_close function. */
2667 if (yd->listen_data)
2668 purple_debug_warning("yahoo","p2p: Failed to create p2p server - server already exists\n");
2669 else {
2670 yd->listen_data = purple_network_listen(YAHOO_PAGER_PORT_P2P, AF_UNSPEC, SOCK_STREAM, TRUE, yahoo_p2p_server_listen_cb, p2p_data);
2671 if (yd->listen_data == NULL)
2672 purple_debug_warning("yahoo","p2p: Failed to created p2p server\n");
2675 g_free(base64_ip);
2678 /* function called when connection to p2p host is setup */
2679 static void yahoo_p2p_init_cb(gpointer data, gint source, const gchar *error_message)
2681 struct yahoo_p2p_data *p2p_data;
2682 struct yahoo_packet *pkt_to_send;
2683 PurpleAccount *account;
2684 YahooData *yd;
2686 p2p_data = data;
2687 yd = purple_connection_get_protocol_data(p2p_data->gc);
2689 if(error_message != NULL) {
2690 purple_debug_warning("yahoo","p2p: %s\n",error_message);
2691 yahoo_send_p2p_pkt(p2p_data->gc, p2p_data->host_username, 2);/* send p2p init packet with val_13=2 */
2693 yahoo_p2p_disconnect_destroy_data(p2p_data);
2694 return;
2697 /* Add an Input Read event to the file descriptor */
2698 p2p_data->input_event = purple_input_add(source, PURPLE_INPUT_READ, yahoo_p2p_read_pkt_cb, data);
2699 p2p_data->source = source;
2701 account = purple_connection_get_account(p2p_data->gc);
2703 /* Build the yahoo packet */
2704 pkt_to_send = yahoo_packet_new(YAHOO_SERVICE_P2PFILEXFER, YAHOO_STATUS_AVAILABLE, yd->session_id);
2705 yahoo_packet_hash(pkt_to_send, "ssisi",
2706 4, purple_normalize(account, purple_account_get_username(account)),
2707 5, p2p_data->host_username,
2708 241, 0, /* Protocol identifier */
2709 49, "PEERTOPEER",
2710 13, 1); /* we receive key13= 0 or 2, we send key13=1 */
2712 yahoo_p2p_write_pkt(source, pkt_to_send); /* build raw packet and send */
2713 yahoo_packet_free(pkt_to_send);
2716 static void yahoo_process_p2p(PurpleConnection *gc, struct yahoo_packet *pkt)
2718 GSList *l = pkt->hash;
2719 char *who = NULL;
2720 char *base64 = NULL;
2721 guchar *decoded;
2722 gsize len;
2723 gint val_13 = 0;
2724 gint val_11 = 0;
2725 PurpleAccount *account;
2726 YahooFriend *f;
2728 /* if status is not YAHOO_STATUS_BRB or YAHOO_STATUS_P2P, the packet bounced back,
2729 * so it contains our own ip */
2730 if(pkt->status != YAHOO_STATUS_BRB && pkt->status != YAHOO_STATUS_P2P)
2731 return ;
2733 while (l) {
2734 struct yahoo_pair *pair = l->data;
2736 switch (pair->key) {
2737 case 5:
2738 /* our identity */
2739 break;
2740 case 4:
2741 if (g_utf8_validate(pair->value, -1, NULL)) {
2742 who = pair->value;
2743 } else {
2744 purple_debug_warning("yahoo", "yahoo_process_p2p "
2745 "got non-UTF-8 string for key %d\n", pair->key);
2747 break;
2748 case 1:
2749 /* who again, the master identity this time? */
2750 break;
2751 case 12:
2752 if (g_utf8_validate(pair->value, -1, NULL)) {
2753 base64 = pair->value;
2754 /* so, this is an ip address. in base64. decoded it's in ascii.
2755 after strtol, it's in reversed byte order. Who thought this up?*/
2756 } else {
2757 purple_debug_warning("yahoo", "yahoo_process_p2p "
2758 "got non-UTF-8 string for key %d\n", pair->key);
2760 break;
2761 case 13:
2762 val_13 = strtol(pair->value, NULL, 10);
2763 break;
2764 case 11:
2765 val_11 = strtol(pair->value, NULL, 10); /* session id of peer */
2766 if( (f = yahoo_friend_find(gc, who)) )
2767 f->session_id = val_11;
2768 break;
2770 TODO: figure these out
2771 yahoo: Key: 61 Value: 0
2772 yahoo: Key: 2 Value:
2773 yahoo: Key: 13 Value: 0 packet count ??
2774 yahoo: Key: 49 Value: PEERTOPEER
2775 yahoo: Key: 140 Value: 1
2780 l = l->next;
2783 if (base64) {
2784 guint32 ip;
2785 YahooFriend *f;
2786 char *host_ip, *tmp;
2787 struct yahoo_p2p_data *p2p_data;
2789 decoded = purple_base64_decode(base64, &len);
2790 if (decoded == NULL) {
2791 purple_debug_info("yahoo","p2p: Unable to decode base64 IP (%s) \n", base64);
2792 return;
2794 tmp = purple_str_binary_to_ascii(decoded, len);
2795 purple_debug_info("yahoo", "Got P2P service packet (from server): who = %s, ip = %s\n", who, tmp);
2796 g_free(tmp);
2798 ip = strtol((gchar *)decoded, NULL, 10);
2799 g_free(decoded);
2800 host_ip = g_strdup_printf("%u.%u.%u.%u", ip & 0xff, (ip >> 8) & 0xff, (ip >> 16) & 0xff,
2801 (ip >> 24) & 0xff);
2802 f = yahoo_friend_find(gc, who);
2803 if (f)
2804 yahoo_friend_set_ip(f, host_ip);
2805 purple_debug_info("yahoo", "IP : %s\n", host_ip);
2807 account = purple_connection_get_account(gc);
2809 if(val_11==0) {
2810 if(!f)
2811 return;
2812 else
2813 val_11 = f->session_id;
2816 p2p_data = g_new0(struct yahoo_p2p_data, 1);
2817 p2p_data->host_username = g_strdup(who);
2818 p2p_data->val_13 = val_13;
2819 p2p_data->session_id = val_11;
2820 p2p_data->host_ip = host_ip;
2821 p2p_data->gc = gc;
2822 p2p_data->connection_type = YAHOO_P2P_WE_ARE_CLIENT;
2823 p2p_data->source = -1;
2825 /* connect to host */
2826 if((purple_proxy_connect(gc, account, host_ip, YAHOO_PAGER_PORT_P2P, yahoo_p2p_init_cb, p2p_data))==NULL) {
2827 purple_debug_info("yahoo","p2p: Connection to %s failed\n", host_ip);
2828 g_free(p2p_data->host_ip);
2829 g_free(p2p_data->host_username);
2830 g_free(p2p_data);
2835 static void yahoo_process_audible(PurpleConnection *gc, struct yahoo_packet *pkt)
2837 PurpleAccount *account;
2838 char *who = NULL, *msg = NULL, *id = NULL;
2839 GSList *l = pkt->hash;
2841 account = purple_connection_get_account(gc);
2843 while (l) {
2844 struct yahoo_pair *pair = l->data;
2846 switch (pair->key) {
2847 case 4:
2848 if (g_utf8_validate(pair->value, -1, NULL)) {
2849 who = pair->value;
2850 } else {
2851 purple_debug_warning("yahoo", "yahoo_process_audible "
2852 "got non-UTF-8 string for key %d\n", pair->key);
2854 break;
2855 case 5:
2856 /* us */
2857 break;
2858 case 230:
2859 /* the audible, in foo.locale.bar.baz format
2860 eg: base.tw.smiley.smiley43 */
2861 if (g_utf8_validate(pair->value, -1, NULL)) {
2862 id = pair->value;
2863 } else {
2864 purple_debug_warning("yahoo", "yahoo_process_audible "
2865 "got non-UTF-8 string for key %d\n", pair->key);
2867 break;
2868 case 231:
2869 /* the text of the audible */
2870 if (g_utf8_validate(pair->value, -1, NULL)) {
2871 msg = pair->value;
2872 } else {
2873 purple_debug_warning("yahoo", "yahoo_process_audible "
2874 "got non-UTF-8 string for key %d\n", pair->key);
2876 break;
2877 case 232:
2878 /* SHA-1 hash of audible SWF file (eg: 4e8691499d9c0fb8374478ff9720f4a9ea4a4915) */
2879 break;
2882 l = l->next;
2885 if (!msg)
2886 msg = id;
2887 if (!who || !msg)
2888 return;
2889 if (!g_utf8_validate(msg, -1, NULL)) {
2890 purple_debug_misc("yahoo", "Warning, nonutf8 audible, ignoring!\n");
2891 return;
2893 if (!purple_account_privacy_check(account, who)) {
2894 purple_debug_misc("yahoo", "Audible message from %s for %s dropped!\n",
2895 purple_account_get_username(account), who);
2896 return;
2898 if (id) {
2899 /* "http://l.yimg.com/pu/dl/aud/"+locale+"/"+id+".swf" */
2900 char **audible_locale = g_strsplit(id, ".", 0);
2901 char *buf = g_strdup_printf(_("[ Audible %s/%s/%s.swf ] %s"), YAHOO_AUDIBLE_URL, audible_locale[1], id, msg);
2902 g_strfreev(audible_locale);
2904 purple_serv_got_im(gc, who, buf, 0, time(NULL));
2905 g_free(buf);
2906 } else
2907 purple_serv_got_im(gc, who, msg, 0, time(NULL));
2910 static void yahoo_packet_process(PurpleConnection *gc, struct yahoo_packet *pkt)
2912 switch (pkt->service) {
2913 case YAHOO_SERVICE_LOGON:
2914 case YAHOO_SERVICE_LOGOFF:
2915 case YAHOO_SERVICE_ISAWAY:
2916 case YAHOO_SERVICE_ISBACK:
2917 case YAHOO_SERVICE_GAMELOGON:
2918 case YAHOO_SERVICE_GAMELOGOFF:
2919 case YAHOO_SERVICE_CHATLOGON:
2920 case YAHOO_SERVICE_CHATLOGOFF:
2921 case YAHOO_SERVICE_Y6_STATUS_UPDATE:
2922 case YAHOO_SERVICE_STATUS_15:
2923 yahoo_process_status(gc, pkt);
2924 break;
2925 case YAHOO_SERVICE_NOTIFY:
2926 yahoo_process_notify(gc, pkt, YAHOO_PKT_TYPE_SERVER);
2927 break;
2928 case YAHOO_SERVICE_MESSAGE:
2929 case YAHOO_SERVICE_GAMEMSG:
2930 case YAHOO_SERVICE_CHATMSG:
2931 yahoo_process_message(gc, pkt, YAHOO_PKT_TYPE_SERVER);
2932 break;
2933 case YAHOO_SERVICE_SYSMESSAGE:
2934 yahoo_process_sysmessage(gc, pkt);
2935 break;
2936 case YAHOO_SERVICE_NEWMAIL:
2937 yahoo_process_mail(gc, pkt);
2938 break;
2939 case YAHOO_SERVICE_NEWCONTACT:
2940 yahoo_process_contact(gc, pkt);
2941 break;
2942 case YAHOO_SERVICE_AUTHRESP:
2943 yahoo_process_authresp(gc, pkt);
2944 break;
2945 case YAHOO_SERVICE_LIST:
2946 yahoo_process_list(gc, pkt);
2947 break;
2948 case YAHOO_SERVICE_LIST_15:
2949 yahoo_process_list_15(gc, pkt);
2950 break;
2951 case YAHOO_SERVICE_AUTH:
2952 yahoo_process_auth(gc, pkt);
2953 break;
2954 case YAHOO_SERVICE_AUTH_REQ_15:
2955 yahoo_buddy_auth_req_15(gc, pkt);
2956 break;
2957 case YAHOO_SERVICE_ADDBUDDY:
2958 yahoo_process_addbuddy(gc, pkt);
2959 break;
2960 case YAHOO_SERVICE_IGNORECONTACT:
2961 yahoo_process_ignore(gc, pkt);
2962 break;
2963 case YAHOO_SERVICE_CONFINVITE:
2964 case YAHOO_SERVICE_CONFADDINVITE:
2965 yahoo_process_conference_invite(gc, pkt);
2966 break;
2967 case YAHOO_SERVICE_CONFDECLINE:
2968 yahoo_process_conference_decline(gc, pkt);
2969 break;
2970 case YAHOO_SERVICE_CONFLOGON:
2971 yahoo_process_conference_logon(gc, pkt);
2972 break;
2973 case YAHOO_SERVICE_CONFLOGOFF:
2974 yahoo_process_conference_logoff(gc, pkt);
2975 break;
2976 case YAHOO_SERVICE_CONFMSG:
2977 yahoo_process_conference_message(gc, pkt);
2978 break;
2979 case YAHOO_SERVICE_CHATONLINE:
2980 yahoo_process_chat_online(gc, pkt);
2981 break;
2982 case YAHOO_SERVICE_CHATLOGOUT:
2983 yahoo_process_chat_logout(gc, pkt);
2984 break;
2985 case YAHOO_SERVICE_CHATGOTO:
2986 yahoo_process_chat_goto(gc, pkt);
2987 break;
2988 case YAHOO_SERVICE_CHATJOIN:
2989 yahoo_process_chat_join(gc, pkt);
2990 break;
2991 case YAHOO_SERVICE_CHATLEAVE: /* XXX is this right? */
2992 case YAHOO_SERVICE_CHATEXIT:
2993 yahoo_process_chat_exit(gc, pkt);
2994 break;
2995 case YAHOO_SERVICE_CHATINVITE: /* XXX never seen this one, might not do it right */
2996 case YAHOO_SERVICE_CHATADDINVITE:
2997 yahoo_process_chat_addinvite(gc, pkt);
2998 break;
2999 case YAHOO_SERVICE_COMMENT:
3000 yahoo_process_chat_message(gc, pkt);
3001 break;
3002 case YAHOO_SERVICE_PRESENCE_PERM:
3003 case YAHOO_SERVICE_PRESENCE_SESSION:
3004 yahoo_process_presence(gc, pkt);
3005 break;
3006 case YAHOO_SERVICE_P2PFILEXFER:
3007 /* This case had no break and continued; thus keeping it this way.*/
3008 yahoo_process_p2p(gc, pkt); /* P2PFILEXFER handled the same way as process_p2p */
3009 yahoo_process_p2pfilexfer(gc, pkt); /* redundant ??, need to have a break now */
3010 case YAHOO_SERVICE_FILETRANSFER:
3011 purple_debug_error("yahoo", "Legacy file transfers are not "
3012 "supported anymore.\n");
3013 break;
3014 case YAHOO_SERVICE_PEERTOPEER:
3015 yahoo_process_p2p(gc, pkt);
3016 break;
3017 case YAHOO_SERVICE_PICTURE:
3018 yahoo_process_picture(gc, pkt);
3019 break;
3020 case YAHOO_SERVICE_PICTURE_CHECKSUM:
3021 yahoo_process_picture_checksum(gc, pkt);
3022 break;
3023 case YAHOO_SERVICE_PICTURE_UPLOAD:
3024 yahoo_process_picture_upload(gc, pkt);
3025 break;
3026 case YAHOO_SERVICE_PICTURE_UPDATE:
3027 case YAHOO_SERVICE_AVATAR_UPDATE:
3028 yahoo_process_avatar_update(gc, pkt);
3029 break;
3030 case YAHOO_SERVICE_AUDIBLE:
3031 yahoo_process_audible(gc, pkt);
3032 break;
3033 case YAHOO_SERVICE_CONTACT_DETAILS:
3034 yahoo_process_contact_details(gc, pkt);
3035 break;
3036 case YAHOO_SERVICE_FILETRANS_15:
3037 yahoo_process_filetrans_15(gc, pkt);
3038 break;
3039 case YAHOO_SERVICE_FILETRANS_INFO_15:
3040 yahoo_process_filetrans_info_15(gc, pkt);
3041 break;
3042 case YAHOO_SERVICE_FILETRANS_ACC_15:
3043 yahoo_process_filetrans_acc_15(gc, pkt);
3044 break;
3045 case YAHOO_SERVICE_SMS_MSG:
3046 yahoo_process_sms_message(gc, pkt);
3047 break;
3049 default:
3050 purple_debug_error("yahoo", "Unhandled service 0x%02x\n", pkt->service);
3051 break;
3055 static void yahoo_pending(gpointer data, gint source, PurpleInputCondition cond)
3057 PurpleConnection *gc = data;
3058 YahooData *yd = purple_connection_get_protocol_data(gc);
3059 char buf[1024];
3060 int len;
3062 len = read(yd->fd, buf, sizeof(buf));
3064 if (len < 0) {
3065 gchar *tmp;
3067 if (errno == EAGAIN)
3068 /* No worries */
3069 return;
3071 tmp = g_strdup_printf(_("Lost connection with server: %s"),
3072 g_strerror(errno));
3073 purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp);
3074 g_free(tmp);
3075 return;
3076 } else if (len == 0) {
3077 purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
3078 _("Server closed the connection"));
3079 return;
3081 purple_connection_update_last_received(gc);
3082 yd->rxqueue = g_realloc(yd->rxqueue, len + yd->rxlen);
3083 memcpy(yd->rxqueue + yd->rxlen, buf, len);
3084 yd->rxlen += len;
3086 while (1) {
3087 struct yahoo_packet *pkt;
3088 int pos = 0;
3089 int pktlen;
3091 if (yd->rxlen < YAHOO_PACKET_HDRLEN)
3092 return;
3094 if (strncmp((char *)yd->rxqueue, "YMSG", MIN(4, yd->rxlen)) != 0) {
3095 /* HEY! This isn't even a YMSG packet. What
3096 * are you trying to pull? */
3097 guchar *start;
3099 purple_debug_warning("yahoo", "Error in YMSG stream, got something not a YMSG packet!\n");
3101 start = memchr(yd->rxqueue + 1, 'Y', yd->rxlen - 1);
3102 if (start) {
3103 g_memmove(yd->rxqueue, start, yd->rxlen - (start - yd->rxqueue));
3104 yd->rxlen -= start - yd->rxqueue;
3105 continue;
3106 } else {
3107 g_free(yd->rxqueue);
3108 yd->rxqueue = NULL;
3109 yd->rxlen = 0;
3110 return;
3114 pos += 4; /* YMSG */
3115 pos += 2;
3116 pos += 2;
3118 pktlen = yahoo_get16(yd->rxqueue + pos); pos += 2;
3119 purple_debug_misc("yahoo", "%d bytes to read, rxlen is %d\n", pktlen, yd->rxlen);
3121 if (yd->rxlen < (YAHOO_PACKET_HDRLEN + pktlen))
3122 return;
3124 yahoo_packet_dump(yd->rxqueue, YAHOO_PACKET_HDRLEN + pktlen);
3126 pkt = yahoo_packet_new(0, 0, 0);
3128 pkt->service = yahoo_get16(yd->rxqueue + pos); pos += 2;
3129 pkt->status = yahoo_get32(yd->rxqueue + pos); pos += 4;
3130 purple_debug_misc("yahoo", "Yahoo Service: 0x%02x Status: %d\n",
3131 pkt->service, pkt->status);
3132 pkt->id = yahoo_get32(yd->rxqueue + pos); pos += 4;
3134 yahoo_packet_read(pkt, yd->rxqueue + pos, pktlen);
3136 yd->rxlen -= YAHOO_PACKET_HDRLEN + pktlen;
3137 if (yd->rxlen) {
3138 guchar *tmp = g_memdup(yd->rxqueue + YAHOO_PACKET_HDRLEN + pktlen, yd->rxlen);
3139 g_free(yd->rxqueue);
3140 yd->rxqueue = tmp;
3141 } else {
3142 g_free(yd->rxqueue);
3143 yd->rxqueue = NULL;
3146 yahoo_packet_process(gc, pkt);
3148 yahoo_packet_free(pkt);
3152 static void yahoo_got_connected(gpointer data, gint source, const gchar *error_message)
3154 PurpleConnection *gc = data;
3155 YahooData *yd;
3156 struct yahoo_packet *pkt;
3158 if (source < 0) {
3159 gchar *tmp;
3160 tmp = g_strdup_printf(_("Unable to connect: %s"), error_message);
3161 purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp);
3162 g_free(tmp);
3163 return;
3166 yd = purple_connection_get_protocol_data(gc);
3167 yd->fd = source;
3169 pkt = yahoo_packet_new(YAHOO_SERVICE_AUTH, yd->current_status, yd->session_id);
3171 yahoo_packet_hash_str(pkt, 1, purple_normalize(purple_connection_get_account(gc), purple_account_get_username(purple_connection_get_account(gc))));
3172 yahoo_packet_send_and_free(pkt, yd);
3174 yd->inpa = purple_input_add(yd->fd, PURPLE_INPUT_READ, yahoo_pending, gc);
3177 #if TRY_WEBMESSENGER_LOGIN
3179 static void yahoo_got_web_connected(gpointer data, gint source, const gchar *error_message)
3181 PurpleConnection *gc = data;
3182 YahooData *yd;
3183 struct yahoo_packet *pkt;
3185 if (source < 0) {
3186 gchar *tmp;
3187 tmp = g_strdup_printf(_("Unable to connect: %s"), error_message);
3188 purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp);
3189 g_free(tmp);
3190 return;
3193 yd = purple_connection_get_protocol_data(gc);
3194 yd->fd = source;
3196 pkt = yahoo_packet_new(YAHOO_SERVICE_WEBLOGIN, YAHOO_STATUS_WEBLOGIN, yd->session_id);
3198 yahoo_packet_hash(pkt, "sss", 0,
3199 purple_normalize(purple_connection_get_account(gc), purple_account_get_username(purple_connection_get_account(gc))),
3200 1, purple_normalize(purple_connection_get_account(gc), purple_account_get_username(purple_connection_get_account(gc))),
3201 6, yd->auth);
3202 yahoo_packet_send_and_free(pkt, yd);
3204 g_free(yd->auth);
3205 yd->inpa = purple_input_add(yd->fd, PURPLE_INPUT_READ, yahoo_pending, gc);
3208 static void
3209 yahoo_login_page_got(PurpleHttpConnection *hc, PurpleHttpResponse *resp,
3210 gpointer _unused)
3212 PurpleConnection *gc = purple_http_conn_get_purple_connection(hc);
3213 YahooData *yd = purple_connection_get_protocol_data(gc);
3214 PurpleAccount *account = purple_connection_get_account(gc);
3215 PurpleHttpCookieJar *cjar;
3216 GString *auth_s;
3217 gchar *cookie;
3219 if (purple_http_response_get_code(resp) != 302) {
3220 purple_connection_error(gc,
3221 PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
3222 _("Unable to connect"));
3223 return;
3226 auth_s = g_string_new(NULL);
3227 cjar = purple_http_conn_get_cookie_jar(hc);
3228 cookie = purple_http_cookie_jar_get(cjar, "B");
3229 if (cookie) {
3230 g_string_append_printf(auth_s, "B=%s; ", cookie);
3231 g_free(cookie);
3233 cookie = purple_http_cookie_jar_get(cjar, "T");
3234 if (cookie) {
3235 g_string_append_printf(auth_s, "T=%s; ", cookie);
3236 g_free(cookie);
3238 cookie = purple_http_cookie_jar_get(cjar, "Y");
3239 if (cookie) {
3240 g_string_append_printf(auth_s, "Y=%s; ", cookie);
3241 g_free(cookie);
3244 yd->auth = g_string_free(auth_s, FALSE);
3245 /* Now we have our cookies to login with. I'll go get the milk. */
3247 /* XXX: wcs2.msg.dcn.yahoo.com is down, so I used
3248 * YAHOO_PAGER_HOST_FALLBACK, but I'm not sure, if it is the correct
3249 * host.
3251 if (purple_proxy_connect(gc, account, YAHOO_PAGER_HOST_FALLBACK,
3252 purple_account_get_int(account, "port", YAHOO_PAGER_PORT),
3253 yahoo_got_web_connected, gc) == NULL)
3255 purple_connection_error(gc,
3256 PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
3257 _("Unable to connect"));
3258 return;
3262 static void yahoo_login_page_hash_iter(const char *key, const char *val, GString *url)
3264 if (!strcmp(key, "passwd") || !strcmp(key, "login"))
3265 return;
3266 g_string_append_c(url, '&');
3267 g_string_append(url, key);
3268 g_string_append_c(url, '=');
3269 if (!strcmp(key, ".save") || !strcmp(key, ".js"))
3270 g_string_append_c(url, '1');
3271 else if (!strcmp(key, ".challenge"))
3272 g_string_append(url, val);
3273 else
3274 g_string_append(url, purple_url_encode(val));
3277 static GHashTable *yahoo_login_page_hash(const char *buf, size_t len)
3279 GHashTable *hash = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
3280 const char *c = buf;
3281 char *d;
3282 char name[64], value[64];
3283 int count;
3284 int input_len = strlen("<input ");
3285 int name_len = strlen("name=\"");
3286 int value_len = strlen("value=\"");
3287 while ((len > ((c - buf) + input_len))
3288 && (c = strstr(c, "<input "))) {
3289 if (!(c = g_strstr_len(c, len - (c - buf), "name=\"")))
3290 continue;
3291 c += name_len;
3292 count = sizeof(name)-1;
3293 for (d = name; (len > ((c - buf) + 1)) && *c!='"'
3294 && count; c++, d++, count--)
3295 *d = *c;
3296 *d = '\0';
3297 count = sizeof(value)-1;
3298 if (!(d = g_strstr_len(c, len - (c - buf), "value=\"")))
3299 continue;
3300 d += value_len;
3301 if (strchr(c, '>') < d)
3302 break;
3303 for (c = d, d = value; (len > ((c - buf) + 1))
3304 && *c!='"' && count; c++, d++, count--)
3305 *d = *c;
3306 *d = '\0';
3307 g_hash_table_insert(hash, g_strdup(name), g_strdup(value));
3309 return hash;
3312 static void
3313 yahoo_login_page_cb(PurpleHttpConnection *http_conn,
3314 PurpleHttpResponse *response, gpointer _unused)
3316 PurpleConnection *gc = purple_http_conn_get_purple_connection(http_conn);
3317 PurpleAccount *account = purple_connection_get_account(gc);
3318 YahooData *yd = purple_connection_get_protocol_data(gc);
3319 const char *pass = purple_connection_get_password(gc);
3320 size_t len;
3321 const gchar *got_data;
3322 GHashTable *hash;
3323 GString *url;
3324 char md5[33], *hashp = md5, *chal;
3325 int i;
3326 PurpleCipher *cipher;
3327 guchar digest[16];
3328 PurpleHttpRequest *req;
3330 if (!purple_http_response_is_successful(response))
3332 purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
3333 purple_http_response_get_error(response));
3334 return;
3337 got_data = purple_http_response_get_data(response, &len);
3338 hash = yahoo_login_page_hash(got_data, len);
3340 cipher = purple_md5_cipher_new();
3342 purple_cipher_append(cipher, (const guchar *)pass, strlen(pass));
3343 purple_cipher_digest(cipher, digest, sizeof(digest));
3344 for (i = 0; i < 16; ++i) {
3345 g_snprintf(hashp, 3, "%02x", digest[i]);
3346 hashp += 2;
3349 chal = g_strconcat(md5, g_hash_table_lookup(hash, ".challenge"), NULL);
3350 purple_cipher_reset(cipher);
3351 purple_cipher_append(cipher, (const guchar *)chal, strlen(chal));
3352 purple_cipher_digest(cipher, digest, sizeof(digest));
3353 hashp = md5;
3354 for (i = 0; i < 16; ++i) {
3355 g_snprintf(hashp, 3, "%02x", digest[i]);
3356 hashp += 2;
3359 * I dunno why this is here and commented out.. but in case it's needed
3360 * I updated it..
3362 purple_cipher_reset(cipher);
3363 purple_cipher_append(cipher, md5, strlen(md5));
3364 purple_cipher_digest(cipher, sizeof(digest), digest, NULL);
3365 hashp = md5;
3366 for (i = 0; i < 16; ++i) {
3367 g_snprintf(hashp, 3, "%02x", digest[i]);
3368 hashp += 2;
3371 g_free(chal);
3373 url = g_string_new(NULL);
3374 g_string_printf(url, "http://login.yahoo.com/config/login?login=%s&passwd=%s", purple_account_get_username(account), md5);
3375 g_hash_table_foreach(hash, (GHFunc)yahoo_login_page_hash_iter, url);
3376 url = g_string_append(url, "&.hash=1&.md5=1");
3378 g_hash_table_destroy(hash);
3379 g_object_unref(cipher);
3381 req = purple_http_request_new(g_string_free(url, FALSE));
3382 purple_http_request_set_max_redirects(req, 0);
3383 purple_http_connection_set_add(yd->http_reqs,
3384 purple_http_request(gc, req, yahoo_login_page_got, NULL));
3385 purple_http_request_unref(req);
3388 #endif /* TRY_WEBMESSENGER_LOGIN */
3390 static void yahoo_picture_check(PurpleAccount *account)
3392 PurpleConnection *gc = purple_account_get_connection(account);
3393 PurpleImage *img = purple_buddy_icons_find_account_icon(account);
3395 yahoo_set_buddy_icon(gc, img);
3397 if (img)
3398 g_object_unref(img);
3401 static int get_yahoo_status_from_purple_status(PurpleStatus *status)
3403 PurplePresence *presence;
3404 const char *status_id;
3405 const char *msg;
3407 presence = purple_status_get_presence(status);
3408 status_id = purple_status_get_id(status);
3409 msg = purple_status_get_attr_string(status, "message");
3411 if ((msg != NULL) && (*msg != '\0')) {
3412 return YAHOO_STATUS_CUSTOM;
3413 } else if (!strcmp(status_id, YAHOO_STATUS_TYPE_AVAILABLE)) {
3414 return YAHOO_STATUS_AVAILABLE;
3415 } else if (!strcmp(status_id, YAHOO_STATUS_TYPE_BRB)) {
3416 return YAHOO_STATUS_BRB;
3417 } else if (!strcmp(status_id, YAHOO_STATUS_TYPE_BUSY)) {
3418 return YAHOO_STATUS_BUSY;
3419 } else if (!strcmp(status_id, YAHOO_STATUS_TYPE_NOTATHOME)) {
3420 return YAHOO_STATUS_NOTATHOME;
3421 } else if (!strcmp(status_id, YAHOO_STATUS_TYPE_NOTATDESK)) {
3422 return YAHOO_STATUS_NOTATDESK;
3423 } else if (!strcmp(status_id, YAHOO_STATUS_TYPE_NOTINOFFICE)) {
3424 return YAHOO_STATUS_NOTINOFFICE;
3425 } else if (!strcmp(status_id, YAHOO_STATUS_TYPE_ONPHONE)) {
3426 return YAHOO_STATUS_ONPHONE;
3427 } else if (!strcmp(status_id, YAHOO_STATUS_TYPE_ONVACATION)) {
3428 return YAHOO_STATUS_ONVACATION;
3429 } else if (!strcmp(status_id, YAHOO_STATUS_TYPE_OUTTOLUNCH)) {
3430 return YAHOO_STATUS_OUTTOLUNCH;
3431 } else if (!strcmp(status_id, YAHOO_STATUS_TYPE_STEPPEDOUT)) {
3432 return YAHOO_STATUS_STEPPEDOUT;
3433 } else if (!strcmp(status_id, YAHOO_STATUS_TYPE_INVISIBLE)) {
3434 return YAHOO_STATUS_INVISIBLE;
3435 } else if (!strcmp(status_id, YAHOO_STATUS_TYPE_AWAY)) {
3436 return YAHOO_STATUS_CUSTOM;
3437 } else if (purple_presence_is_idle(presence)) {
3438 return YAHOO_STATUS_IDLE;
3439 } else {
3440 purple_debug_error("yahoo", "Unexpected PurpleStatus!\n");
3441 return YAHOO_STATUS_AVAILABLE;
3445 static void yahoo_got_pager_server(PurpleHttpConnection *http_conn,
3446 PurpleHttpResponse *response, gpointer _yd)
3448 YahooData *yd = _yd;
3449 PurpleConnection *gc = yd->gc;
3450 PurpleAccount *a = purple_connection_get_account(gc);
3451 gchar **strings = NULL, *cs_server = NULL;
3452 int port = purple_account_get_int(a, "port", YAHOO_PAGER_PORT);
3453 int stringslen = 0;
3454 const gchar *got_data;
3456 if (!purple_http_response_is_successful(response)) {
3457 purple_debug_error("yahoo", "Unable to retrieve server info: %s\n",
3458 purple_http_response_get_error(response));
3460 if(purple_proxy_connect(gc, a, YAHOO_PAGER_HOST_FALLBACK, port,
3461 yahoo_got_connected, gc) == NULL) {
3462 purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
3463 _("Unable to connect"));
3465 } else {
3466 got_data = purple_http_response_get_data(response, NULL);
3467 strings = g_strsplit(got_data, "\r\n", -1);
3469 if((stringslen = g_strv_length(strings)) > 1) {
3470 int i;
3472 for(i = 0; i < stringslen; i++) {
3473 if(g_ascii_strncasecmp(strings[i], "COLO_CAPACITY=", 14) == 0) {
3474 purple_debug_info("yahoo", "Got COLO Capacity: %s\n", &(strings[i][14]));
3475 } else if(g_ascii_strncasecmp(strings[i], "CS_IP_ADDRESS=", 14) == 0) {
3476 cs_server = g_strdup(&strings[i][14]);
3477 purple_debug_info("yahoo", "Got CS IP address: %s\n", cs_server);
3482 if(cs_server) { /* got an address; get on with connecting */
3483 if(purple_proxy_connect(gc, a, cs_server, port, yahoo_got_connected, gc) == NULL)
3484 purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
3485 _("Unable to connect"));
3486 } else {
3487 purple_debug_error("yahoo", "No CS address retrieved! Server "
3488 "response:\n%s\n", got_data);
3490 if(purple_proxy_connect(gc, a, YAHOO_PAGER_HOST_FALLBACK, port,
3491 yahoo_got_connected, gc) == NULL) {
3492 purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
3493 _("Unable to connect"));
3498 g_strfreev(strings);
3499 g_free(cs_server);
3502 void yahoo_login(PurpleAccount *account) {
3503 PurpleConnection *gc = purple_account_get_connection(account);
3504 PurpleHttpRequest *req;
3505 YahooData *yd = g_new0(YahooData, 1);
3506 PurpleStatus *status = purple_account_get_active_status(account);
3508 purple_connection_set_protocol_data(gc, yd);
3509 purple_connection_set_flags(gc, PURPLE_CONNECTION_FLAG_HTML |
3510 PURPLE_CONNECTION_FLAG_NO_BGCOLOR |
3511 PURPLE_CONNECTION_FLAG_NO_URLDESC |
3512 PURPLE_CONNECTION_FLAG_NO_IMAGES);
3514 purple_connection_update_progress(gc, _("Connecting"), 1, 2);
3516 purple_connection_set_display_name(gc, purple_account_get_username(account));
3518 yd->gc = gc;
3519 yd->yahoo_local_p2p_server_fd = -1;
3520 yd->fd = -1;
3521 yd->txhandler = 0;
3522 /* TODO: Is there a good grow size for the buffer? */
3523 yd->txbuf = purple_circular_buffer_new(0);
3524 yd->http_reqs = purple_http_connection_set_new();
3525 yd->friends = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, yahoo_friend_free);
3526 yd->imvironments = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
3527 yd->xfer_peer_idstring_map = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, NULL);
3528 yd->peers = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
3529 yahoo_p2p_disconnect_destroy_data);
3530 yd->sms_carrier = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
3531 yd->yahoo_p2p_timer = purple_timeout_add_seconds(YAHOO_P2P_KEEPALIVE_SECS,
3532 yahoo_p2p_keepalive, gc);
3533 yd->confs = NULL;
3534 yd->conf_id = 2;
3535 yd->last_keepalive = yd->last_ping = time(NULL);
3537 yd->current_status = get_yahoo_status_from_purple_status(status);
3539 yahoo_picture_check(account);
3541 /* Get the pager server. Actually start connecting in the callback since we
3542 * must have the contents of the HTTP response to proceed. */
3543 req = purple_http_request_new(YAHOO_PAGER_HOST_REQ_URL);
3544 purple_http_request_header_set(req, "User-Agent", YAHOO_CLIENT_USERAGENT);
3545 purple_http_connection_set_add(yd->http_reqs, purple_http_request(gc,
3546 req, yahoo_got_pager_server, yd));
3547 purple_http_request_unref(req);
3549 return;
3552 void yahoo_close(PurpleConnection *gc) {
3553 YahooData *yd = purple_connection_get_protocol_data(gc);
3554 GSList *l;
3556 if (yd->inpa) {
3557 purple_input_remove(yd->inpa);
3558 yd->inpa = 0;
3561 purple_http_connection_set_destroy(yd->http_reqs);
3562 yd->http_reqs = NULL;
3564 for (l = yd->confs; l; l = l->next) {
3565 PurpleChatConversation *conv = l->data;
3566 GList *users;
3568 users = purple_chat_conversation_get_users(conv);
3569 yahoo_conf_leave(yd,
3570 purple_conversation_get_name(PURPLE_CONVERSATION(conv)),
3571 purple_connection_get_display_name(gc), users);
3572 g_list_free(users);
3574 g_slist_free(yd->confs);
3576 g_slist_free_full(yd->cookies, g_free);
3578 yd->chat_online = FALSE;
3579 if (yd->in_chat)
3580 yahoo_c_leave(gc, 1); /* 1 = YAHOO_CHAT_ID */
3582 purple_timeout_remove(yd->yahoo_p2p_timer);
3583 if(yd->yahoo_p2p_server_timeout_handle != 0) {
3584 purple_timeout_remove(yd->yahoo_p2p_server_timeout_handle);
3585 yd->yahoo_p2p_server_timeout_handle = 0;
3588 /* close p2p server if it is waiting for a peer to connect */
3589 if (yd->yahoo_p2p_server_watcher) {
3590 purple_input_remove(yd->yahoo_p2p_server_watcher);
3591 yd->yahoo_p2p_server_watcher = 0;
3593 if (yd->yahoo_local_p2p_server_fd >= 0) {
3594 close(yd->yahoo_local_p2p_server_fd);
3595 yd->yahoo_local_p2p_server_fd = -1;
3598 g_hash_table_destroy(yd->sms_carrier);
3599 g_hash_table_destroy(yd->peers);
3600 g_hash_table_destroy(yd->friends);
3601 g_hash_table_destroy(yd->imvironments);
3602 g_hash_table_destroy(yd->xfer_peer_idstring_map);
3603 g_free(yd->chat_name);
3605 g_free(yd->cookie_y);
3606 g_free(yd->cookie_t);
3607 g_free(yd->cookie_b);
3609 if (yd->txhandler)
3610 purple_input_remove(yd->txhandler);
3612 g_object_unref(G_OBJECT(yd->txbuf));
3614 if (yd->fd >= 0)
3615 close(yd->fd);
3617 g_free(yd->rxqueue);
3618 yd->rxlen = 0;
3619 g_free(yd->picture_url);
3621 purple_http_conn_cancel(yd->picture_upload_hc);
3622 if (yd->picture_upload_todo)
3623 yahoo_buddy_icon_upload_data_free(yd->picture_upload_todo);
3624 if (yd->ycht)
3625 ycht_connection_close(yd->ycht);
3626 if (yd->listen_data != NULL)
3627 purple_network_listen_cancel(yd->listen_data);
3629 g_free(yd->pending_chat_room);
3630 g_free(yd->pending_chat_id);
3631 g_free(yd->pending_chat_topic);
3632 g_free(yd->pending_chat_goto);
3633 g_strfreev(yd->profiles);
3635 yahoo_personal_details_reset(&yd->ypd, TRUE);
3637 g_free(yd->current_list15_grp);
3639 g_free(yd);
3640 purple_connection_set_protocol_data(gc, NULL);
3643 const char *yahoo_list_icon(PurpleAccount *a, PurpleBuddy *b)
3645 return "yahoo";
3648 const char *yahoo_list_emblem(PurpleBuddy *b)
3650 PurpleAccount *account;
3651 PurpleConnection *gc;
3652 YahooFriend *f;
3653 PurplePresence *presence;
3655 if (!b || !(account = purple_buddy_get_account(b)) ||
3656 !(gc = purple_account_get_connection(account)) ||
3657 !purple_connection_get_protocol_data(gc))
3658 return NULL;
3660 f = yahoo_friend_find(gc, purple_buddy_get_name(b));
3661 if (!f) {
3662 return "not-authorized";
3665 presence = purple_buddy_get_presence(b);
3667 if (purple_presence_is_online(presence)) {
3668 if (yahoo_friend_get_game(f))
3669 return "game";
3671 return NULL;
3674 static const char *yahoo_get_status_string(enum yahoo_status a)
3676 switch (a) {
3677 case YAHOO_STATUS_BRB:
3678 return _("Be Right Back");
3679 case YAHOO_STATUS_BUSY:
3680 return _("Busy");
3681 case YAHOO_STATUS_NOTATHOME:
3682 return _("Not at Home");
3683 case YAHOO_STATUS_NOTATDESK:
3684 return _("Not at Desk");
3685 case YAHOO_STATUS_NOTINOFFICE:
3686 return _("Not in Office");
3687 case YAHOO_STATUS_ONPHONE:
3688 return _("On the Phone");
3689 case YAHOO_STATUS_ONVACATION:
3690 return _("On Vacation");
3691 case YAHOO_STATUS_OUTTOLUNCH:
3692 return _("Out to Lunch");
3693 case YAHOO_STATUS_STEPPEDOUT:
3694 return _("Stepped Out");
3695 case YAHOO_STATUS_INVISIBLE:
3696 return _("Invisible");
3697 case YAHOO_STATUS_IDLE:
3698 return _("Idle");
3699 case YAHOO_STATUS_OFFLINE:
3700 return _("Offline");
3701 default:
3702 return _("Available");
3706 static void yahoo_initiate_conference(PurpleBlistNode *node, gpointer data) {
3708 PurpleBuddy *buddy;
3709 PurpleConnection *gc;
3711 GHashTable *components;
3712 YahooData *yd;
3713 int id;
3715 g_return_if_fail(PURPLE_IS_BUDDY(node));
3717 buddy = (PurpleBuddy *) node;
3718 gc = purple_account_get_connection(purple_buddy_get_account(buddy));
3719 yd = purple_connection_get_protocol_data(gc);
3720 id = yd->conf_id;
3722 components = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
3723 g_hash_table_replace(components, g_strdup("room"),
3724 g_strdup_printf("%s-%d", purple_connection_get_display_name(gc), id));
3725 g_hash_table_replace(components, g_strdup("topic"), g_strdup("Join my conference..."));
3726 g_hash_table_replace(components, g_strdup("type"), g_strdup("Conference"));
3727 yahoo_c_join(gc, components);
3728 g_hash_table_destroy(components);
3730 yahoo_c_invite(gc, id, "Join my conference...", purple_buddy_get_name(buddy));
3733 static void yahoo_presence_settings(PurpleBlistNode *node, gpointer data) {
3734 PurpleBuddy *buddy;
3735 PurpleConnection *gc;
3736 int presence_val = GPOINTER_TO_INT(data);
3738 buddy = (PurpleBuddy *) node;
3739 gc = purple_account_get_connection(purple_buddy_get_account(buddy));
3741 yahoo_friend_update_presence(gc, purple_buddy_get_name(buddy), presence_val);
3744 static void yahoo_game(PurpleBlistNode *node, gpointer data) {
3746 PurpleBuddy *buddy;
3747 PurpleConnection *gc;
3749 const char *game;
3750 char *game2;
3751 char *t;
3752 char url[256];
3753 YahooFriend *f;
3755 g_return_if_fail(PURPLE_IS_BUDDY(node));
3757 buddy = (PurpleBuddy *) node;
3758 gc = purple_account_get_connection(purple_buddy_get_account(buddy));
3760 f = yahoo_friend_find(gc, purple_buddy_get_name(buddy));
3761 if (!f)
3762 return;
3764 game = yahoo_friend_get_game(f);
3765 if (!game)
3766 return;
3768 t = game2 = g_strdup(strstr(game, "ante?room="));
3769 while (*t && *t != '\t')
3770 t++;
3771 *t = 0;
3772 g_snprintf(url, sizeof url, "http://games.yahoo.com/games/%s", game2);
3773 purple_notify_uri(gc, url);
3774 g_free(game2);
3777 char *yahoo_status_text(PurpleBuddy *b)
3779 YahooFriend *f = NULL;
3780 const char *msg;
3781 char *msg2;
3782 PurpleAccount *account;
3783 PurpleConnection *gc;
3785 account = purple_buddy_get_account(b);
3786 gc = purple_account_get_connection(account);
3787 if (!gc || !purple_connection_get_protocol_data(gc))
3788 return NULL;
3790 f = yahoo_friend_find(gc, purple_buddy_get_name(b));
3791 if (!f)
3792 return g_strdup(_("Not on server list"));
3794 switch (f->status) {
3795 case YAHOO_STATUS_AVAILABLE:
3796 return NULL;
3797 case YAHOO_STATUS_IDLE:
3798 if (f->idle == -1)
3799 return g_strdup(yahoo_get_status_string(f->status));
3800 return NULL;
3801 case YAHOO_STATUS_CUSTOM:
3802 if (!(msg = yahoo_friend_get_status_message(f)))
3803 return NULL;
3804 msg2 = g_markup_escape_text(msg, strlen(msg));
3805 purple_util_chrreplace(msg2, '\n', ' ');
3806 return msg2;
3808 default:
3809 return g_strdup(yahoo_get_status_string(f->status));
3813 void yahoo_tooltip_text(PurpleBuddy *b, PurpleNotifyUserInfo *user_info, gboolean full)
3815 YahooFriend *f;
3816 char *status = NULL;
3817 const char *presence = NULL;
3818 PurpleAccount *account;
3820 account = purple_buddy_get_account(b);
3821 f = yahoo_friend_find(purple_account_get_connection(account), purple_buddy_get_name(b));
3822 if (!f)
3823 status = g_strdup_printf("\n%s", _("Not on server list"));
3824 else {
3825 switch (f->status) {
3826 case YAHOO_STATUS_CUSTOM:
3827 if (!yahoo_friend_get_status_message(f))
3828 return;
3829 status = g_strdup(yahoo_friend_get_status_message(f));
3830 break;
3831 case YAHOO_STATUS_OFFLINE:
3832 break;
3833 default:
3834 status = g_strdup(yahoo_get_status_string(f->status));
3835 break;
3838 switch (f->presence) {
3839 case YAHOO_PRESENCE_ONLINE:
3840 presence = _("Appear Online");
3841 break;
3842 case YAHOO_PRESENCE_PERM_OFFLINE:
3843 presence = _("Appear Permanently Offline");
3844 break;
3845 case YAHOO_PRESENCE_DEFAULT:
3846 break;
3847 default:
3848 purple_debug_error("yahoo", "Unknown presence in yahoo_tooltip_text\n");
3849 break;
3853 if (status != NULL) {
3854 purple_notify_user_info_add_pair_plaintext(user_info, _("Status"), status);
3855 g_free(status);
3858 if (presence != NULL)
3859 purple_notify_user_info_add_pair_plaintext(user_info, _("Presence"), presence);
3861 if (f && full) {
3862 YahooPersonalDetails *ypd = &f->ypd;
3863 if (ypd->phone.home && *ypd->phone.home)
3864 purple_notify_user_info_add_pair_plaintext(user_info, _("Home Phone Number"), ypd->phone.home);
3865 if (ypd->phone.work && *ypd->phone.work)
3866 purple_notify_user_info_add_pair_plaintext(user_info, _("Work Phone Number"), ypd->phone.work);
3867 if (ypd->phone.mobile && *ypd->phone.mobile)
3868 purple_notify_user_info_add_pair_plaintext(user_info, _("Mobile Phone Number"), ypd->phone.mobile);
3872 static void yahoo_addbuddyfrommenu_cb(PurpleBlistNode *node, gpointer data)
3874 PurpleBuddy *buddy;
3875 PurpleConnection *gc;
3877 g_return_if_fail(PURPLE_IS_BUDDY(node));
3879 buddy = (PurpleBuddy *) node;
3880 gc = purple_account_get_connection(purple_buddy_get_account(buddy));
3882 yahoo_add_buddy(gc, buddy, NULL, NULL);
3886 static void yahoo_chat_goto_menu(PurpleBlistNode *node, gpointer data)
3888 PurpleBuddy *buddy;
3889 PurpleConnection *gc;
3891 g_return_if_fail(PURPLE_IS_BUDDY(node));
3893 buddy = (PurpleBuddy *) node;
3894 gc = purple_account_get_connection(purple_buddy_get_account(buddy));
3896 yahoo_chat_goto(gc, purple_buddy_get_name(buddy));
3899 static GList *build_presence_submenu(YahooFriend *f, PurpleConnection *gc) {
3900 GList *m = NULL;
3901 PurpleMenuAction *act;
3902 YahooData *yd = purple_connection_get_protocol_data(gc);
3904 if (yd->current_status == YAHOO_STATUS_INVISIBLE) {
3905 if (f->presence != YAHOO_PRESENCE_ONLINE) {
3906 act = purple_menu_action_new(_("Appear Online"),
3907 PURPLE_CALLBACK(yahoo_presence_settings),
3908 GINT_TO_POINTER(YAHOO_PRESENCE_ONLINE),
3909 NULL);
3910 m = g_list_append(m, act);
3911 } else if (f->presence != YAHOO_PRESENCE_DEFAULT) {
3912 act = purple_menu_action_new(_("Appear Offline"),
3913 PURPLE_CALLBACK(yahoo_presence_settings),
3914 GINT_TO_POINTER(YAHOO_PRESENCE_DEFAULT),
3915 NULL);
3916 m = g_list_append(m, act);
3920 if (f->presence == YAHOO_PRESENCE_PERM_OFFLINE) {
3921 act = purple_menu_action_new(_("Don't Appear Permanently Offline"),
3922 PURPLE_CALLBACK(yahoo_presence_settings),
3923 GINT_TO_POINTER(YAHOO_PRESENCE_DEFAULT),
3924 NULL);
3925 m = g_list_append(m, act);
3926 } else {
3927 act = purple_menu_action_new(_("Appear Permanently Offline"),
3928 PURPLE_CALLBACK(yahoo_presence_settings),
3929 GINT_TO_POINTER(YAHOO_PRESENCE_PERM_OFFLINE),
3930 NULL);
3931 m = g_list_append(m, act);
3934 return m;
3937 static void yahoo_doodle_blist_node(PurpleBlistNode *node, gpointer data)
3939 PurpleBuddy *b = (PurpleBuddy *)node;
3940 PurpleAccount *account = purple_buddy_get_account(b);
3941 PurpleConnection *gc = purple_account_get_connection(account);
3943 yahoo_doodle_initiate(gc, purple_buddy_get_name(b));
3946 #if 0
3947 /* XXX: it doesn't seems to work */
3948 static void
3949 yahoo_userinfo_blist_node(PurpleBlistNode *node, gpointer data)
3951 PurpleBuddy *b = (PurpleBuddy *)node;
3952 PurpleAccount *account = purple_buddy_get_account(b);
3953 PurpleConnection *gc = purple_account_get_connection(account);
3955 yahoo_set_userinfo_for_buddy(gc, b);
3957 #endif
3959 static GList *yahoo_buddy_menu(PurpleBuddy *buddy)
3961 GList *m = NULL;
3962 PurpleMenuAction *act;
3964 PurpleConnection *gc = purple_account_get_connection(purple_buddy_get_account(buddy));
3965 YahooData *yd = purple_connection_get_protocol_data(gc);
3966 static char buf2[1024];
3967 YahooFriend *f;
3969 f = yahoo_friend_find(gc, purple_buddy_get_name(buddy));
3971 if (!f && !yd->wm) {
3972 act = purple_menu_action_new(_("Add Buddy"),
3973 PURPLE_CALLBACK(yahoo_addbuddyfrommenu_cb),
3974 NULL, NULL);
3975 m = g_list_append(m, act);
3977 return m;
3981 if (f && f->status != YAHOO_STATUS_OFFLINE) {
3982 if (!yd->wm) {
3983 act = purple_menu_action_new(_("Join in Chat"),
3984 PURPLE_CALLBACK(yahoo_chat_goto_menu),
3985 NULL, NULL);
3986 m = g_list_append(m, act);
3989 act = purple_menu_action_new(_("Initiate Conference"),
3990 PURPLE_CALLBACK(yahoo_initiate_conference),
3991 NULL, NULL);
3992 m = g_list_append(m, act);
3994 if (yahoo_friend_get_game(f)) {
3995 const char *game = yahoo_friend_get_game(f);
3996 char *room;
3997 char *t;
3999 if ((room = strstr(game, "&follow="))) {/* skip ahead to the url */
4000 while (*room && *room != '\t') /* skip to the tab */
4001 room++;
4002 t = room++; /* room as now at the name */
4003 while (*t != '\n')
4004 t++; /* replace the \n with a space */
4005 *t = ' ';
4006 g_snprintf(buf2, sizeof buf2, "%s", room);
4008 act = purple_menu_action_new(buf2,
4009 PURPLE_CALLBACK(yahoo_game),
4010 NULL, NULL);
4011 m = g_list_append(m, act);
4016 if (f) {
4017 act = purple_menu_action_new(_("Presence Settings"), NULL, NULL,
4018 build_presence_submenu(f, gc));
4019 m = g_list_append(m, act);
4021 act = purple_menu_action_new(_("Start Doodling"),
4022 PURPLE_CALLBACK(yahoo_doodle_blist_node),
4023 NULL, NULL);
4024 m = g_list_append(m, act);
4026 #if 0
4027 /* XXX: it doesn't seems to work */
4028 act = purple_menu_action_new(_("Set User Info..."),
4029 PURPLE_CALLBACK(yahoo_userinfo_blist_node),
4030 NULL, NULL);
4031 m = g_list_append(m, act);
4032 #endif
4035 return m;
4038 GList *yahoo_blist_node_menu(PurpleBlistNode *node)
4040 if(PURPLE_IS_BUDDY(node)) {
4041 return yahoo_buddy_menu((PurpleBuddy *) node);
4042 } else {
4043 return NULL;
4047 static void yahoo_act_id(PurpleConnection *gc, PurpleRequestFields *fields)
4049 YahooData *yd = purple_connection_get_protocol_data(gc);
4050 const char *name = yd->profiles[GPOINTER_TO_INT(purple_request_fields_get_choice(fields, "id"))];
4052 struct yahoo_packet *pkt = yahoo_packet_new(YAHOO_SERVICE_IDACT, YAHOO_STATUS_AVAILABLE, yd->session_id);
4053 yahoo_packet_hash_str(pkt, 3, name);
4054 yahoo_packet_send_and_free(pkt, yd);
4056 purple_connection_set_display_name(gc, name);
4059 static void
4060 yahoo_get_inbox_token_cb(PurpleHttpConnection *http_conn,
4061 PurpleHttpResponse *response, gpointer _unused)
4063 PurpleConnection *gc =
4064 purple_http_conn_get_purple_connection(http_conn);
4065 gchar *url;
4067 PURPLE_ASSERT_CONNECTION_IS_VALID(gc);
4069 if (!purple_http_response_is_successful(response)) {
4070 purple_debug_error("yahoo",
4071 "Requesting mail login token failed: %s\n",
4072 purple_http_response_get_error(response));
4073 url = g_strdup(YAHOO_MAIL_URL);
4074 } else {
4075 /* Should we not be hardcoding the rd url? */
4076 gchar *token;
4077 token = g_strdup(purple_http_response_get_data(response, NULL));
4078 g_strstrip(token);
4079 url = g_strdup_printf(
4080 "http://login.yahoo.com/config/reset_cookies_token?"
4081 ".token=%s"
4082 "&.done=http://us.rd.yahoo.com/messenger/client/%%3f"
4083 "http://mail.yahoo.com/", token);
4084 purple_str_wipe(token);
4087 /* Open the mailbox with the parsed url data */
4088 purple_notify_uri(gc, url);
4090 g_free(url);
4094 static void yahoo_show_inbox(PurpleProtocolAction *action)
4096 /* Setup a cookie that can be used by the browser */
4098 PurpleConnection *gc = action->connection;
4099 YahooData *yd = purple_connection_get_protocol_data(gc);
4100 PurpleHttpRequest *req;
4101 PurpleHttpCookieJar *cookiejar;
4103 req = purple_http_request_new(
4104 "https://login.yahoo.com/config/cookie_token");
4105 purple_http_request_set_method(req, "POST");
4106 purple_http_request_header_set(req, "User-Agent",
4107 YAHOO_CLIENT_USERAGENT);
4108 cookiejar = purple_http_request_get_cookie_jar(req);
4109 purple_http_cookie_jar_set(cookiejar, "T", yd->cookie_t);
4110 purple_http_cookie_jar_set(cookiejar, "Y", yd->cookie_y);
4111 purple_http_connection_set_add(yd->http_reqs, purple_http_request(gc,
4112 req, yahoo_get_inbox_token_cb, NULL));
4113 purple_http_request_unref(req);
4116 #if 0
4117 /* XXX: it doesn't seems to work */
4118 static void
4119 yahoo_set_userinfo_fn(PurpleProtocolAction *action)
4121 yahoo_set_userinfo(action->connection);
4123 #endif
4125 static void yahoo_show_act_id(PurpleProtocolAction *action)
4127 PurpleRequestFields *fields;
4128 PurpleRequestFieldGroup *group;
4129 PurpleRequestField *field;
4130 PurpleConnection *gc = (PurpleConnection *) action->connection;
4131 YahooData *yd = purple_connection_get_protocol_data(gc);
4132 const char *name = purple_connection_get_display_name(gc);
4133 int iter;
4135 fields = purple_request_fields_new();
4136 group = purple_request_field_group_new(NULL);
4137 purple_request_fields_add_group(fields, group);
4138 field = purple_request_field_choice_new("id", _("Activate which ID?"), 0);
4139 purple_request_field_group_add_field(group, field);
4141 for (iter = 0; yd->profiles[iter]; iter++) {
4142 purple_request_field_choice_add(field, yd->profiles[iter], GINT_TO_POINTER(iter));
4143 if (purple_strequal(yd->profiles[iter], name))
4144 purple_request_field_choice_set_default_value(field, GINT_TO_POINTER(iter));
4147 purple_request_fields(gc, NULL, _("Select the ID you want to activate"), NULL,
4148 fields,
4149 _("OK"), G_CALLBACK(yahoo_act_id),
4150 _("Cancel"), NULL,
4151 purple_request_cpar_from_connection(gc), gc);
4154 static void yahoo_show_chat_goto(PurpleProtocolAction *action)
4156 PurpleConnection *gc = action->connection;
4157 purple_request_input(gc, NULL, _("Join whom in chat?"), NULL,
4158 "", FALSE, FALSE, NULL,
4159 _("OK"), G_CALLBACK(yahoo_chat_goto),
4160 _("Cancel"), NULL,
4161 purple_request_cpar_from_connection(gc),
4162 gc);
4165 GList *yahoo_get_actions(PurpleConnection *gc) {
4166 GList *m = NULL;
4167 PurpleProtocolAction *act;
4169 #if 0
4170 /* XXX: it doesn't seems to work */
4171 act = purple_protocol_action_new(_("Set User Info..."),
4172 yahoo_set_userinfo_fn);
4173 m = g_list_append(m, act);
4174 #endif
4176 act = purple_protocol_action_new(_("Activate ID..."),
4177 yahoo_show_act_id);
4178 m = g_list_append(m, act);
4180 act = purple_protocol_action_new(_("Join User in Chat..."),
4181 yahoo_show_chat_goto);
4182 m = g_list_append(m, act);
4184 m = g_list_append(m, NULL);
4185 act = purple_protocol_action_new(_("Open Inbox"),
4186 yahoo_show_inbox);
4187 m = g_list_append(m, act);
4189 return m;
4192 struct yahoo_sms_carrier_cb_data {
4193 PurpleConnection *gc;
4194 char *who;
4195 char *what;
4198 static void yahoo_get_sms_carrier_cb(PurpleHttpConnection *http_conn,
4199 PurpleHttpResponse *response, gpointer _sms_cb_data)
4201 struct yahoo_sms_carrier_cb_data *sms_cb_data = _sms_cb_data;
4202 PurpleConnection *gc = sms_cb_data->gc;
4203 YahooData *yd = purple_connection_get_protocol_data(gc);
4204 char *status = NULL;
4205 char *carrier = NULL;
4206 PurpleAccount *account = purple_connection_get_account(gc);
4207 PurpleIMConversation *im = purple_conversations_find_im_with_account(sms_cb_data->who, account);
4209 if (!purple_http_response_is_successful(response)) {
4210 purple_conversation_write_system_message(PURPLE_CONVERSATION(im),
4211 _("Can't send SMS. Unable to obtain mobile carrier."), 0);
4213 g_free(sms_cb_data->who);
4214 g_free(sms_cb_data->what);
4215 g_free(sms_cb_data);
4216 return ;
4217 } else {
4218 const gchar *got_data = purple_http_response_get_data(response, NULL);
4219 PurpleXmlNode *validate_data_root = purple_xmlnode_from_str(got_data, -1);
4220 PurpleXmlNode *validate_data_child = purple_xmlnode_get_child(validate_data_root, "mobile_no");
4221 const char *mobile_no = purple_xmlnode_get_attrib(validate_data_child, "msisdn");
4223 validate_data_root = purple_xmlnode_copy(validate_data_child);
4224 validate_data_child = purple_xmlnode_get_child(validate_data_root, "status");
4225 status = purple_xmlnode_get_data(validate_data_child);
4227 validate_data_child = purple_xmlnode_get_child(validate_data_root, "carrier");
4228 carrier = purple_xmlnode_get_data(validate_data_child);
4230 purple_debug_info("yahoo", "SMS validate data: %s\n", got_data);
4232 if (status && g_str_equal(status, "Valid")) {
4233 g_hash_table_insert(yd->sms_carrier,
4234 g_strdup_printf("+%s", mobile_no), g_strdup(carrier));
4235 yahoo_send_im(sms_cb_data->gc, purple_message_new_outgoing(
4236 sms_cb_data->who, sms_cb_data->what, 0));
4237 } else {
4238 g_hash_table_insert(yd->sms_carrier,
4239 g_strdup_printf("+%s", mobile_no), g_strdup("Unknown"));
4240 purple_conversation_write_system_message(PURPLE_CONVERSATION(im),
4241 _("Can't send SMS. Unknown mobile carrier."), 0);
4244 purple_xmlnode_free(validate_data_child);
4245 purple_xmlnode_free(validate_data_root);
4246 g_free(sms_cb_data->who);
4247 g_free(sms_cb_data->what);
4248 g_free(sms_cb_data);
4249 g_free(status);
4250 g_free(carrier);
4254 static void yahoo_get_sms_carrier(PurpleConnection *gc, gpointer data)
4256 YahooData *yd = purple_connection_get_protocol_data(gc);
4257 PurpleHttpRequest *req;
4258 PurpleHttpCookieJar *cookiejar;
4259 struct yahoo_sms_carrier_cb_data *sms_cb_data;
4260 char *validate_request_str = NULL;
4261 PurpleXmlNode *validate_request_root = NULL;
4262 PurpleXmlNode *validate_request_child = NULL;
4264 if(!(sms_cb_data = data))
4265 return;
4267 validate_request_root = purple_xmlnode_new("validate");
4268 purple_xmlnode_set_attrib(validate_request_root, "intl", "us");
4269 purple_xmlnode_set_attrib(validate_request_root, "version", YAHOO_CLIENT_VERSION);
4270 purple_xmlnode_set_attrib(validate_request_root, "qos", "0");
4272 validate_request_child = purple_xmlnode_new_child(validate_request_root, "mobile_no");
4273 purple_xmlnode_set_attrib(validate_request_child, "msisdn", sms_cb_data->who + 1);
4275 validate_request_str = purple_xmlnode_to_str(validate_request_root, NULL);
4277 purple_xmlnode_free(validate_request_child);
4278 purple_xmlnode_free(validate_request_root);
4280 req = purple_http_request_new(NULL);
4281 purple_http_request_set_url_printf(req, "http://validate.msg.yahoo.com"
4282 "/mobileno?intl=us&version=%s", YAHOO_CLIENT_VERSION);
4283 purple_http_request_set_method(req, "POST");
4284 purple_http_request_header_set(req, "User-Agent",
4285 YAHOO_CLIENT_USERAGENT);
4286 cookiejar = purple_http_request_get_cookie_jar(req);
4287 purple_http_cookie_jar_set(cookiejar, "T", yd->cookie_t);
4288 purple_http_cookie_jar_set(cookiejar, "Y", yd->cookie_y);
4289 purple_http_request_set_contents(req, validate_request_str, -1);
4290 purple_http_connection_set_add(yd->http_reqs, purple_http_request(gc,
4291 req, yahoo_get_sms_carrier_cb, data));
4292 purple_http_request_unref(req);
4294 g_free(validate_request_str);
4297 int yahoo_send_im(PurpleConnection *gc, PurpleMessage *pmsg)
4299 YahooData *yd = purple_connection_get_protocol_data(gc);
4300 struct yahoo_packet *pkt = NULL;
4301 char *msg = yahoo_html_to_codes(purple_message_get_contents(pmsg));
4302 char *msg2;
4303 PurpleWhiteboard *wb;
4304 int ret = 1;
4305 gsize lenb = 0;
4306 glong lenc = 0;
4307 struct yahoo_p2p_data *p2p_data;
4308 const gchar *rcpt = purple_message_get_recipient(pmsg);
4310 msg2 = yahoo_string_encode(gc, msg, TRUE);
4312 if(msg2) {
4313 lenb = strlen(msg2);
4314 lenc = g_utf8_strlen(msg2, -1);
4316 if(lenb > YAHOO_MAX_MESSAGE_LENGTH_BYTES || lenc > YAHOO_MAX_MESSAGE_LENGTH_CHARS) {
4317 purple_debug_info("yahoo", "Message too big. Length is %" G_GSIZE_FORMAT
4318 " bytes, %ld characters. Max is %d bytes, %d chars."
4319 " Message is '%s'.\n", lenb, lenc, YAHOO_MAX_MESSAGE_LENGTH_BYTES,
4320 YAHOO_MAX_MESSAGE_LENGTH_CHARS, msg2);
4321 g_free(msg);
4322 g_free(msg2);
4323 return -E2BIG;
4327 if (rcpt[0] == '+') {
4328 /* we have an sms to be sent */
4329 gchar *carrier = NULL;
4330 const char *alias = NULL;
4331 PurpleAccount *account = purple_connection_get_account(gc);
4332 PurpleIMConversation *im = purple_conversations_find_im_with_account(rcpt, account);
4334 carrier = g_hash_table_lookup(yd->sms_carrier, rcpt);
4335 if (!carrier) {
4336 struct yahoo_sms_carrier_cb_data *sms_cb_data;
4337 sms_cb_data = g_malloc(sizeof(struct yahoo_sms_carrier_cb_data));
4338 sms_cb_data->gc = gc;
4339 sms_cb_data->who = g_strdup(rcpt);
4340 sms_cb_data->what = g_strdup(purple_message_get_contents(pmsg));
4342 purple_conversation_write_system_message(PURPLE_CONVERSATION(im),
4343 _("Getting mobile carrier to send the SMS."), 0);
4345 yahoo_get_sms_carrier(gc, sms_cb_data);
4347 g_free(msg);
4348 g_free(msg2);
4349 return ret;
4351 else if( strcmp(carrier,"Unknown") == 0 ) {
4352 purple_conversation_write_system_message(PURPLE_CONVERSATION(im),
4353 _("Can't send SMS. Unknown mobile carrier."), 0);
4355 g_free(msg);
4356 g_free(msg2);
4357 return -1;
4360 alias = purple_account_get_private_alias(account);
4361 pkt = yahoo_packet_new(YAHOO_SERVICE_SMS_MSG, YAHOO_STATUS_AVAILABLE, yd->session_id);
4362 yahoo_packet_hash(pkt, "sssss",
4363 1, purple_connection_get_display_name(gc),
4364 69, alias,
4365 5, rcpt + 1,
4366 68, carrier,
4367 14, msg2);
4368 yahoo_packet_send_and_free(pkt, yd);
4370 g_free(msg);
4371 g_free(msg2);
4373 return ret;
4376 pkt = yahoo_packet_new(YAHOO_SERVICE_MESSAGE, YAHOO_STATUS_OFFLINE, yd->session_id);
4377 yahoo_packet_hash(pkt, "ss", 1, purple_connection_get_display_name(gc), 5, rcpt);
4379 yahoo_packet_hash_str(pkt, 97, "1"); /* UTF-8 */
4380 yahoo_packet_hash_str(pkt, 14, msg2);
4383 * IMVironment.
4385 * If this message is to a user who is also Doodling with the local user,
4386 * format the chat packet with the correct IMV information (thanks Yahoo!)
4388 * Otherwise attempt to use the same IMVironment as the remote user,
4389 * just so that we don't inadvertantly reset their IMVironment back
4390 * to nothing.
4392 * If they have not set an IMVironment, then use the default.
4394 wb = purple_whiteboard_get_session(purple_connection_get_account(gc), rcpt);
4395 if (wb)
4396 yahoo_packet_hash_str(pkt, 63, DOODLE_IMV_KEY);
4397 else
4399 const char *imv;
4400 imv = g_hash_table_lookup(yd->imvironments, rcpt);
4401 if (imv != NULL)
4402 yahoo_packet_hash_str(pkt, 63, imv);
4403 else
4404 yahoo_packet_hash_str(pkt, 63, ";0");
4407 yahoo_packet_hash_str(pkt, 64, "0"); /* no idea */
4408 yahoo_packet_hash_str(pkt, 1002, "1"); /* no idea, Yahoo 6 or later only it seems */
4409 if (!yd->picture_url)
4410 yahoo_packet_hash_str(pkt, 206, "0"); /* 0 = no picture, 2 = picture, maybe 1 = avatar? */
4411 else
4412 yahoo_packet_hash_str(pkt, 206, "2");
4414 /* We may need to not send any packets over 2000 bytes, but I'm not sure yet. */
4415 if ((YAHOO_PACKET_HDRLEN + yahoo_packet_length(pkt)) <= 2000) {
4416 /* if p2p link exists, send through it. To-do: key 15, time value to be sent in case of p2p */
4417 if( (p2p_data = g_hash_table_lookup(yd->peers, rcpt))) {
4418 yahoo_packet_hash_int(pkt, 11, p2p_data->session_id);
4419 yahoo_p2p_write_pkt(p2p_data->source, pkt);
4421 else {
4422 yahoo_packet_send(pkt, yd);
4423 yahoo_send_p2p_pkt(gc, rcpt, 0); /* send p2p packet, with val_13=0 */
4426 else
4427 ret = -E2BIG;
4429 yahoo_packet_free(pkt);
4431 g_free(msg);
4432 g_free(msg2);
4434 return ret;
4437 unsigned int yahoo_send_typing(PurpleConnection *gc, const char *who, PurpleIMTypingState state)
4439 YahooData *yd = purple_connection_get_protocol_data(gc);
4440 struct yahoo_p2p_data *p2p_data;
4441 struct yahoo_packet *pkt = NULL;
4443 /* Don't do anything if sms is being typed */
4444 if( strncmp(who, "+", 1) == 0 )
4445 return 0;
4447 pkt = yahoo_packet_new(YAHOO_SERVICE_NOTIFY, YAHOO_STATUS_TYPING, yd->session_id);
4449 /* check to see if p2p link exists, send through it */
4450 if( (p2p_data = g_hash_table_lookup(yd->peers, who))) {
4451 yahoo_packet_hash(pkt, "sssssis", 49, "TYPING", 1, purple_connection_get_display_name(gc),
4452 14, " ", 13, state == PURPLE_IM_TYPING ? "1" : "0",
4453 5, who, 11, p2p_data->session_id, 1002, "1"); /* To-do: key 15 to be sent in case of p2p */
4454 yahoo_p2p_write_pkt(p2p_data->source, pkt);
4455 yahoo_packet_free(pkt);
4457 else { /* send through yahoo server */
4459 yahoo_packet_hash(pkt, "ssssss", 49, "TYPING", 1, purple_connection_get_display_name(gc),
4460 14, " ", 13, state == PURPLE_IM_TYPING ? "1" : "0",
4461 5, who, 1002, "1");
4462 yahoo_packet_send_and_free(pkt, yd);
4465 return 0;
4468 static void yahoo_session_presence_remove(gpointer key, gpointer value, gpointer data)
4470 YahooFriend *f = value;
4471 if (f && f->presence == YAHOO_PRESENCE_ONLINE)
4472 f->presence = YAHOO_PRESENCE_DEFAULT;
4475 void yahoo_set_status(PurpleAccount *account, PurpleStatus *status)
4477 PurpleConnection *gc;
4478 PurplePresence *presence;
4479 YahooData *yd;
4480 struct yahoo_packet *pkt;
4481 int old_status;
4482 const char *msg = NULL;
4483 char *tmp = NULL;
4484 char *conv_msg = NULL;
4486 if (!purple_status_is_active(status))
4487 return;
4489 gc = purple_account_get_connection(account);
4490 presence = purple_status_get_presence(status);
4491 yd = purple_connection_get_protocol_data(gc);
4492 old_status = yd->current_status;
4494 yd->current_status = get_yahoo_status_from_purple_status(status);
4496 if (yd->current_status == YAHOO_STATUS_CUSTOM)
4498 msg = purple_status_get_attr_string(status, "message");
4500 if (purple_status_is_available(status)) {
4501 tmp = yahoo_string_encode(gc, msg, TRUE);
4502 conv_msg = purple_markup_strip_html(tmp);
4503 g_free(tmp);
4504 } else {
4505 if ((msg == NULL) || (*msg == '\0'))
4506 msg = _("Away");
4507 tmp = yahoo_string_encode(gc, msg, TRUE);
4508 conv_msg = purple_markup_strip_html(tmp);
4509 g_free(tmp);
4513 if (yd->current_status == YAHOO_STATUS_INVISIBLE) {
4514 pkt = yahoo_packet_new(YAHOO_SERVICE_Y6_VISIBLE_TOGGLE, YAHOO_STATUS_AVAILABLE, yd->session_id);
4515 yahoo_packet_hash_str(pkt, 13, "2");
4516 yahoo_packet_send_and_free(pkt, yd);
4518 return;
4521 pkt = yahoo_packet_new(YAHOO_SERVICE_Y6_STATUS_UPDATE, YAHOO_STATUS_AVAILABLE, yd->session_id);
4522 yahoo_packet_hash_int(pkt, 10, yd->current_status);
4524 if (yd->current_status == YAHOO_STATUS_CUSTOM) {
4525 yahoo_packet_hash_str(pkt, 97, "1"); /* UTF-8 */
4526 yahoo_packet_hash_str(pkt, 19, conv_msg);
4527 } else {
4528 yahoo_packet_hash_str(pkt, 19, "");
4531 g_free(conv_msg);
4533 if (purple_presence_is_idle(presence))
4534 yahoo_packet_hash_str(pkt, 47, "2");
4535 else {
4536 if (!purple_status_is_available(status))
4537 yahoo_packet_hash_str(pkt, 47, "1");
4538 else
4539 yahoo_packet_hash_str(pkt, 47, "0");
4542 yahoo_packet_send_and_free(pkt, yd);
4544 if (old_status == YAHOO_STATUS_INVISIBLE) {
4545 pkt = yahoo_packet_new(YAHOO_SERVICE_Y6_VISIBLE_TOGGLE, YAHOO_STATUS_AVAILABLE, yd->session_id);
4546 yahoo_packet_hash_str(pkt, 13, "1");
4547 yahoo_packet_send_and_free(pkt, yd);
4549 /* Any per-session presence settings are removed */
4550 g_hash_table_foreach(yd->friends, yahoo_session_presence_remove, NULL);
4555 void yahoo_set_idle(PurpleConnection *gc, int idle)
4557 YahooData *yd = purple_connection_get_protocol_data(gc);
4558 struct yahoo_packet *pkt = NULL;
4559 char *msg = NULL, *msg2 = NULL;
4560 PurpleStatus *status = NULL;
4561 gboolean invisible = FALSE;
4563 if (idle && yd->current_status != YAHOO_STATUS_CUSTOM)
4564 yd->current_status = YAHOO_STATUS_IDLE;
4565 else if (!idle && yd->current_status == YAHOO_STATUS_IDLE) {
4566 status = purple_presence_get_active_status(purple_account_get_presence(purple_connection_get_account(gc)));
4567 yd->current_status = get_yahoo_status_from_purple_status(status);
4570 invisible = (yd->current_status == YAHOO_STATUS_INVISIBLE);
4572 pkt = yahoo_packet_new(YAHOO_SERVICE_Y6_STATUS_UPDATE, YAHOO_STATUS_AVAILABLE, yd->session_id);
4574 if (!idle && invisible)
4575 yahoo_packet_hash_int(pkt, 10, YAHOO_STATUS_AVAILABLE);
4576 else
4577 yahoo_packet_hash_int(pkt, 10, yd->current_status);
4579 if (yd->current_status == YAHOO_STATUS_CUSTOM) {
4580 const char *tmp;
4581 if (status == NULL)
4582 status = purple_presence_get_active_status(purple_account_get_presence(purple_connection_get_account(gc)));
4583 tmp = purple_status_get_attr_string(status, "message");
4584 if (tmp != NULL) {
4585 msg = yahoo_string_encode(gc, tmp, TRUE);
4586 msg2 = purple_markup_strip_html(msg);
4587 yahoo_packet_hash_str(pkt, 97, "1"); /* UTF-8 */
4588 yahoo_packet_hash_str(pkt, 19, msg2);
4589 } else {
4590 /* get_yahoo_status_from_purple_status() returns YAHOO_STATUS_CUSTOM for
4591 * the generic away state (YAHOO_STATUS_TYPE_AWAY) with no message */
4592 yahoo_packet_hash_str(pkt, 19, _("Away"));
4594 } else {
4595 yahoo_packet_hash_str(pkt, 19, "");
4598 if (idle)
4599 yahoo_packet_hash_str(pkt, 47, "2");
4600 else if (yd->current_status == YAHOO_STATUS_CUSTOM &&
4601 !purple_status_is_available(status))
4602 /* We are still unavailable in this case.
4603 * Make sure Yahoo knows that */
4604 yahoo_packet_hash_str(pkt, 47, "1");
4606 yahoo_packet_send_and_free(pkt, yd);
4608 g_free(msg);
4609 g_free(msg2);
4612 GList *yahoo_status_types(PurpleAccount *account)
4614 PurpleStatusType *type;
4615 GList *types = NULL;
4617 type = purple_status_type_new_with_attrs(PURPLE_STATUS_AVAILABLE, YAHOO_STATUS_TYPE_AVAILABLE,
4618 NULL, TRUE, TRUE, FALSE,
4619 "message", _("Message"),
4620 purple_value_new(G_TYPE_STRING), NULL);
4621 types = g_list_append(types, type);
4623 type = purple_status_type_new_with_attrs(PURPLE_STATUS_AWAY, YAHOO_STATUS_TYPE_AWAY,
4624 NULL, TRUE, TRUE, FALSE,
4625 "message", _("Message"),
4626 purple_value_new(G_TYPE_STRING), NULL);
4627 types = g_list_append(types, type);
4629 type = purple_status_type_new(PURPLE_STATUS_AWAY, YAHOO_STATUS_TYPE_BRB, _("Be Right Back"), TRUE);
4630 types = g_list_append(types, type);
4632 type = purple_status_type_new_with_attrs(PURPLE_STATUS_UNAVAILABLE, YAHOO_STATUS_TYPE_BUSY,
4633 _("Busy"), TRUE, TRUE, FALSE,
4634 "message", _("Message"),
4635 purple_value_new(G_TYPE_STRING), NULL);
4636 types = g_list_append(types, type);
4638 type = purple_status_type_new(PURPLE_STATUS_AWAY, YAHOO_STATUS_TYPE_NOTATHOME, _("Not at Home"), TRUE);
4639 types = g_list_append(types, type);
4641 type = purple_status_type_new(PURPLE_STATUS_AWAY, YAHOO_STATUS_TYPE_NOTATDESK, _("Not at Desk"), TRUE);
4642 types = g_list_append(types, type);
4644 type = purple_status_type_new(PURPLE_STATUS_AWAY, YAHOO_STATUS_TYPE_NOTINOFFICE, _("Not in Office"), TRUE);
4645 types = g_list_append(types, type);
4647 type = purple_status_type_new(PURPLE_STATUS_UNAVAILABLE, YAHOO_STATUS_TYPE_ONPHONE, _("On the Phone"), TRUE);
4648 types = g_list_append(types, type);
4650 type = purple_status_type_new(PURPLE_STATUS_EXTENDED_AWAY, YAHOO_STATUS_TYPE_ONVACATION, _("On Vacation"), TRUE);
4651 types = g_list_append(types, type);
4653 type = purple_status_type_new(PURPLE_STATUS_AWAY, YAHOO_STATUS_TYPE_OUTTOLUNCH, _("Out to Lunch"), TRUE);
4654 types = g_list_append(types, type);
4656 type = purple_status_type_new(PURPLE_STATUS_AWAY, YAHOO_STATUS_TYPE_STEPPEDOUT, _("Stepped Out"), TRUE);
4657 types = g_list_append(types, type);
4660 type = purple_status_type_new(PURPLE_STATUS_INVISIBLE, YAHOO_STATUS_TYPE_INVISIBLE, NULL, TRUE);
4661 types = g_list_append(types, type);
4663 type = purple_status_type_new(PURPLE_STATUS_OFFLINE, YAHOO_STATUS_TYPE_OFFLINE, NULL, TRUE);
4664 types = g_list_append(types, type);
4666 type = purple_status_type_new_full(PURPLE_STATUS_MOBILE, YAHOO_STATUS_TYPE_MOBILE, NULL, FALSE, FALSE, TRUE);
4667 types = g_list_append(types, type);
4669 return types;
4672 void yahoo_keepalive(PurpleConnection *gc)
4674 struct yahoo_packet *pkt;
4675 YahooData *yd = purple_connection_get_protocol_data(gc);
4676 time_t now = time(NULL);
4678 /* We're only allowed to send a ping once an hour or the servers will boot us */
4679 if ((now - yd->last_ping) >= PING_TIMEOUT) {
4680 yd->last_ping = now;
4682 /* The native client will only send PING or CHATPING */
4683 if (yd->chat_online) {
4684 if (yd->wm) {
4685 ycht_chat_send_keepalive(yd->ycht);
4686 } else {
4687 pkt = yahoo_packet_new(YAHOO_SERVICE_CHATPING, YAHOO_STATUS_AVAILABLE, yd->session_id);
4688 yahoo_packet_hash_str(pkt, 109, purple_connection_get_display_name(gc));
4689 yahoo_packet_send_and_free(pkt, yd);
4691 } else {
4692 pkt = yahoo_packet_new(YAHOO_SERVICE_PING, YAHOO_STATUS_AVAILABLE, yd->session_id);
4693 yahoo_packet_send_and_free(pkt, yd);
4697 if ((now - yd->last_keepalive) >= KEEPALIVE_TIMEOUT) {
4698 yd->last_keepalive = now;
4699 pkt = yahoo_packet_new(YAHOO_SERVICE_KEEPALIVE, YAHOO_STATUS_AVAILABLE, yd->session_id);
4700 yahoo_packet_hash_str(pkt, 0, purple_connection_get_display_name(gc));
4701 yahoo_packet_send_and_free(pkt, yd);
4706 void yahoo_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *g, const char *message)
4708 YahooData *yd = purple_connection_get_protocol_data(gc);
4709 struct yahoo_packet *pkt;
4710 const char *group = NULL;
4711 char *group2;
4712 const char *bname = purple_buddy_get_name(buddy);
4714 if (!yd->logged_in)
4715 return;
4717 if (!purple_account_privacy_check(purple_connection_get_account(gc), bname))
4718 return;
4720 group = purple_group_get_name(purple_buddy_get_group(buddy));
4722 group2 = yahoo_string_encode(gc, group, FALSE);
4723 pkt = yahoo_packet_new(YAHOO_SERVICE_ADDBUDDY, YAHOO_STATUS_AVAILABLE, yd->session_id);
4724 yahoo_packet_hash(pkt, "ssssssssss",
4725 14, "",
4726 65, group2,
4727 97, "1", /* UTF-8 */
4728 1, purple_connection_get_display_name(gc),
4729 302, "319",
4730 300, "319",
4731 7, bname,
4732 334, "0",
4733 301, "319",
4734 303, "319");
4736 yahoo_packet_send_and_free(pkt, yd);
4737 g_free(group2);
4740 void yahoo_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group)
4742 YahooData *yd = purple_connection_get_protocol_data(gc);
4743 struct yahoo_packet *pkt;
4744 GSList *buddies, *l;
4745 PurpleGroup *g;
4746 gboolean remove = TRUE;
4747 char *cg;
4748 const char *bname, *gname;
4749 YahooFriend *f = NULL;
4751 bname = purple_buddy_get_name(buddy);
4752 f = yahoo_friend_find(gc, bname);
4753 if (!f)
4754 return;
4756 gname = purple_group_get_name(group);
4757 buddies = purple_blist_find_buddies(purple_connection_get_account(gc), bname);
4758 for (l = buddies; l; l = l->next) {
4759 g = purple_buddy_get_group(l->data);
4760 if (purple_utf8_strcasecmp(gname, purple_group_get_name(g))) {
4761 remove = FALSE;
4762 break;
4766 g_slist_free(buddies);
4768 if (remove) {
4769 g_hash_table_remove(yd->friends, bname);
4770 f = NULL; /* f no longer valid - Just making it clear */
4773 cg = yahoo_string_encode(gc, gname, FALSE);
4774 pkt = yahoo_packet_new(YAHOO_SERVICE_REMBUDDY, YAHOO_STATUS_AVAILABLE, yd->session_id);
4776 yahoo_packet_hash(pkt, "sss", 1, purple_connection_get_display_name(gc),
4777 7, bname, 65, cg);
4778 yahoo_packet_send_and_free(pkt, yd);
4779 g_free(cg);
4782 void yahoo_add_deny(PurpleConnection *gc, const char *who) {
4783 YahooData *yd = purple_connection_get_protocol_data(gc);
4784 struct yahoo_packet *pkt;
4786 if (!yd->logged_in)
4787 return;
4789 if (!who || who[0] == '\0')
4790 return;
4792 pkt = yahoo_packet_new(YAHOO_SERVICE_IGNORECONTACT, YAHOO_STATUS_AVAILABLE, yd->session_id);
4794 yahoo_packet_hash(pkt, "sss", 1, purple_connection_get_display_name(gc), 7, who, 13, "1");
4796 yahoo_packet_send_and_free(pkt, yd);
4799 void yahoo_rem_deny(PurpleConnection *gc, const char *who) {
4800 YahooData *yd = purple_connection_get_protocol_data(gc);
4801 struct yahoo_packet *pkt;
4803 if (!yd->logged_in)
4804 return;
4806 if (!who || who[0] == '\0')
4807 return;
4809 pkt = yahoo_packet_new(YAHOO_SERVICE_IGNORECONTACT, YAHOO_STATUS_AVAILABLE, yd->session_id);
4811 yahoo_packet_hash(pkt, "sss", 1, purple_connection_get_display_name(gc), 7, who, 13, "2");
4813 yahoo_packet_send_and_free(pkt, yd);
4816 void yahoo_set_permit_deny(PurpleConnection *gc)
4818 PurpleAccount *account;
4819 GSList *deny;
4821 account = purple_connection_get_account(gc);
4823 switch (purple_account_get_privacy_type(account))
4825 case PURPLE_ACCOUNT_PRIVACY_ALLOW_ALL:
4826 for (deny = purple_account_privacy_get_denied(account); deny; deny = deny->next)
4827 yahoo_rem_deny(gc, deny->data);
4828 break;
4830 case PURPLE_ACCOUNT_PRIVACY_ALLOW_BUDDYLIST:
4831 case PURPLE_ACCOUNT_PRIVACY_ALLOW_USERS:
4832 case PURPLE_ACCOUNT_PRIVACY_DENY_USERS:
4833 case PURPLE_ACCOUNT_PRIVACY_DENY_ALL:
4834 for (deny = purple_account_privacy_get_denied(account); deny; deny = deny->next)
4835 yahoo_add_deny(gc, deny->data);
4836 break;
4840 void yahoo_change_buddys_group(PurpleConnection *gc, const char *who,
4841 const char *old_group, const char *new_group)
4843 YahooData *yd = purple_connection_get_protocol_data(gc);
4844 struct yahoo_packet *pkt;
4845 char *gpn, *gpo;
4846 YahooFriend *f = yahoo_friend_find(gc, who);
4847 const char *temp = NULL;
4849 /* Step 0: If they aren't on the server list anyway,
4850 * don't bother letting the server know.
4852 if (!f)
4853 return;
4855 temp = who;
4857 /* If old and new are the same, we would probably
4858 * end up deleting the buddy, which would be bad.
4859 * This might happen because of the charset conversation.
4861 gpn = yahoo_string_encode(gc, new_group, FALSE);
4862 gpo = yahoo_string_encode(gc, old_group, FALSE);
4863 if (!strcmp(gpn, gpo)) {
4864 g_free(gpn);
4865 g_free(gpo);
4866 return;
4869 pkt = yahoo_packet_new(YAHOO_SERVICE_CHGRP_15, YAHOO_STATUS_AVAILABLE, yd->session_id);
4870 yahoo_packet_hash(pkt, "ssssssss", 1, purple_connection_get_display_name(gc),
4871 302, "240", 300, "240", 7, temp, 224, gpo, 264, gpn, 301,
4872 "240", 303, "240");
4873 yahoo_packet_send_and_free(pkt, yd);
4875 g_free(gpn);
4876 g_free(gpo);
4879 void yahoo_rename_group(PurpleConnection *gc, const char *old_name,
4880 PurpleGroup *group, GList *moved_buddies)
4882 YahooData *yd = purple_connection_get_protocol_data(gc);
4883 struct yahoo_packet *pkt;
4884 char *gpn, *gpo;
4886 gpn = yahoo_string_encode(gc, purple_group_get_name(group), FALSE);
4887 gpo = yahoo_string_encode(gc, old_name, FALSE);
4888 if (!strcmp(gpn, gpo)) {
4889 g_free(gpn);
4890 g_free(gpo);
4891 return;
4894 pkt = yahoo_packet_new(YAHOO_SERVICE_GROUPRENAME, YAHOO_STATUS_AVAILABLE, yd->session_id);
4895 yahoo_packet_hash(pkt, "sss", 1, purple_connection_get_display_name(gc),
4896 65, gpo, 67, gpn);
4897 yahoo_packet_send_and_free(pkt, yd);
4898 g_free(gpn);
4899 g_free(gpo);
4902 /********************************* Commands **********************************/
4904 PurpleCmdRet
4905 yahoopurple_cmd_buzz(PurpleConversation *c, const gchar *cmd, gchar **args, gchar **error, void *data) {
4906 PurpleAccount *account = purple_conversation_get_account(c);
4908 if (*args && args[0])
4909 return PURPLE_CMD_RET_FAILED;
4911 purple_protocol_send_attention(purple_account_get_connection(account), purple_conversation_get_name(c), YAHOO_BUZZ);
4913 return PURPLE_CMD_RET_OK;
4916 PurpleCmdRet
4917 yahoopurple_cmd_chat_join(PurpleConversation *conv, const char *cmd,
4918 char **args, char **error, void *data)
4920 GHashTable *comp;
4921 PurpleConnection *gc;
4923 if (!args || !args[0])
4924 return PURPLE_CMD_RET_FAILED;
4926 gc = purple_conversation_get_connection(conv);
4927 purple_debug_info("yahoo", "Trying to join %s \n", args[0]);
4929 comp = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
4930 g_hash_table_replace(comp, g_strdup("room"), g_ascii_strdown(args[0], -1));
4931 g_hash_table_replace(comp, g_strdup("type"), g_strdup("Chat"));
4933 yahoo_c_join(gc, comp);
4935 g_hash_table_destroy(comp);
4936 return PURPLE_CMD_RET_OK;
4939 PurpleCmdRet
4940 yahoopurple_cmd_chat_list(PurpleConversation *conv, const char *cmd,
4941 char **args, char **error, void *data)
4943 PurpleAccount *account = purple_conversation_get_account(conv);
4944 if (*args && args[0])
4945 return PURPLE_CMD_RET_FAILED;
4946 purple_roomlist_show_with_account(account);
4947 return PURPLE_CMD_RET_OK;
4950 gboolean yahoo_offline_message(const PurpleBuddy *buddy)
4952 return TRUE;
4955 gboolean yahoo_send_attention(PurpleConnection *gc, const char *username, guint type)
4957 PurpleIMConversation *im;
4959 im = purple_conversations_find_im_with_account(username,
4960 purple_connection_get_account(gc));
4962 g_return_val_if_fail(im != NULL, FALSE);
4964 purple_debug_info("yahoo", "Sending <ding> on account %s to buddy %s.\n",
4965 username, purple_conversation_get_name(PURPLE_CONVERSATION(im)));
4966 purple_conversation_send_with_flags(PURPLE_CONVERSATION(im), "<ding>", PURPLE_MESSAGE_INVISIBLE);
4968 return TRUE;
4971 GList *yahoo_attention_types(PurpleAccount *account)
4973 static GList *list = NULL;
4975 if (!list) {
4976 /* Yahoo only supports one attention command: the 'buzz'. */
4977 /* This is index number YAHOO_BUZZ. */
4978 list = g_list_append(list, purple_attention_type_new("Buzz", _("Buzz"),
4979 _("%s has buzzed you!"), _("Buzzing %s...")));
4982 return list;
4985 gssize
4986 yahoo_get_max_message_size(PurpleConversation *conv)
4988 return YAHOO_MAX_MESSAGE_LENGTH_CHARS;