comparison libpurple/protocols/msnp9/notification.c @ 21312:a07cfce78345

Add MSNP9 back as an alternative alongside the existing MSN prpl. Cowardly old fools like me who prefer the stability of our MSNP9 code over the features of MSNP14 can enable this using the --disable-msnp14 ./configure option. If we want to release from i.p.p and MSN stability is the only blocker, we can trivially flick the default to use MSNP9 in configure.ac
author Stu Tomlinson <stu@nosnilmot.com>
date Sun, 11 Nov 2007 12:57:52 +0000
parents
children af5de2b08fe0
comparison
equal deleted inserted replaced
21311:7d031cec5ba2 21312:a07cfce78345
1 /**
2 * @file notification.c Notification server functions
3 *
4 * purple
5 *
6 * Purple is the legal property of its developers, whose names are too numerous
7 * to list here. Please refer to the COPYRIGHT file distributed with this
8 * source distribution.
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
23 */
24 #include "msn.h"
25 #include "notification.h"
26 #include "state.h"
27 #include "error.h"
28 #include "msn-utils.h"
29 #include "page.h"
30
31 #include "userlist.h"
32 #include "sync.h"
33 #include "slplink.h"
34
35 static MsnTable *cbs_table;
36
37 /**************************************************************************
38 * Main
39 **************************************************************************/
40
41 static void
42 destroy_cb(MsnServConn *servconn)
43 {
44 MsnNotification *notification;
45
46 notification = servconn->cmdproc->data;
47 g_return_if_fail(notification != NULL);
48
49 msn_notification_destroy(notification);
50 }
51
52 MsnNotification *
53 msn_notification_new(MsnSession *session)
54 {
55 MsnNotification *notification;
56 MsnServConn *servconn;
57
58 g_return_val_if_fail(session != NULL, NULL);
59
60 notification = g_new0(MsnNotification, 1);
61
62 notification->session = session;
63 notification->servconn = servconn = msn_servconn_new(session, MSN_SERVCONN_NS);
64 msn_servconn_set_destroy_cb(servconn, destroy_cb);
65
66 notification->cmdproc = servconn->cmdproc;
67 notification->cmdproc->data = notification;
68 notification->cmdproc->cbs_table = cbs_table;
69
70 return notification;
71 }
72
73 void
74 msn_notification_destroy(MsnNotification *notification)
75 {
76 notification->cmdproc->data = NULL;
77
78 msn_servconn_set_destroy_cb(notification->servconn, NULL);
79
80 msn_servconn_destroy(notification->servconn);
81
82 g_free(notification);
83 }
84
85 /**************************************************************************
86 * Connect
87 **************************************************************************/
88
89 static void
90 connect_cb(MsnServConn *servconn)
91 {
92 MsnCmdProc *cmdproc;
93 MsnSession *session;
94 PurpleAccount *account;
95 char **a, **c, *vers;
96 int i;
97
98 g_return_if_fail(servconn != NULL);
99
100 cmdproc = servconn->cmdproc;
101 session = servconn->session;
102 account = session->account;
103
104 /* Allocate an array for CVR0, NULL, and all the versions */
105 a = c = g_new0(char *, session->protocol_ver - 8 + 3);
106
107 for (i = session->protocol_ver; i >= 8; i--)
108 *c++ = g_strdup_printf("MSNP%d", i);
109
110 *c++ = g_strdup("CVR0");
111
112 vers = g_strjoinv(" ", a);
113
114 if (session->login_step == MSN_LOGIN_STEP_START)
115 msn_session_set_login_step(session, MSN_LOGIN_STEP_HANDSHAKE);
116 else
117 msn_session_set_login_step(session, MSN_LOGIN_STEP_HANDSHAKE2);
118
119 msn_cmdproc_send(cmdproc, "VER", "%s", vers);
120
121 g_strfreev(a);
122 g_free(vers);
123 }
124
125 gboolean
126 msn_notification_connect(MsnNotification *notification, const char *host, int port)
127 {
128 MsnServConn *servconn;
129
130 g_return_val_if_fail(notification != NULL, FALSE);
131
132 servconn = notification->servconn;
133
134 msn_servconn_set_connect_cb(servconn, connect_cb);
135 notification->in_use = msn_servconn_connect(servconn, host, port);
136
137 return notification->in_use;
138 }
139
140 void
141 msn_notification_disconnect(MsnNotification *notification)
142 {
143 g_return_if_fail(notification != NULL);
144 g_return_if_fail(notification->in_use);
145
146 msn_servconn_disconnect(notification->servconn);
147
148 notification->in_use = FALSE;
149 }
150
151 /**************************************************************************
152 * Util
153 **************************************************************************/
154
155 static void
156 group_error_helper(MsnSession *session, const char *msg, int group_id, int error)
157 {
158 PurpleAccount *account;
159 PurpleConnection *gc;
160 char *reason = NULL;
161 char *title = NULL;
162
163 account = session->account;
164 gc = purple_account_get_connection(account);
165
166 if (error == 224)
167 {
168 if (group_id == 0)
169 {
170 return;
171 }
172 else
173 {
174 const char *group_name;
175 group_name =
176 msn_userlist_find_group_name(session->userlist,
177 group_id);
178 reason = g_strdup_printf(_("%s is not a valid group."),
179 group_name);
180 }
181 }
182 else
183 {
184 reason = g_strdup(_("Unknown error."));
185 }
186
187 title = g_strdup_printf(_("%s on %s (%s)"), msg,
188 purple_account_get_username(account),
189 purple_account_get_protocol_name(account));
190 purple_notify_error(gc, NULL, title, reason);
191 g_free(title);
192 g_free(reason);
193 }
194
195 /**************************************************************************
196 * Login
197 **************************************************************************/
198
199 void
200 msn_got_login_params(MsnSession *session, const char *login_params)
201 {
202 MsnCmdProc *cmdproc;
203
204 cmdproc = session->notification->cmdproc;
205
206 msn_session_set_login_step(session, MSN_LOGIN_STEP_AUTH_END);
207
208 msn_cmdproc_send(cmdproc, "USR", "TWN S %s", login_params);
209 }
210
211 static void
212 cvr_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
213 {
214 PurpleAccount *account;
215
216 account = cmdproc->session->account;
217
218 msn_cmdproc_send(cmdproc, "USR", "TWN I %s",
219 purple_account_get_username(account));
220 }
221
222 static void
223 usr_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
224 {
225 MsnSession *session;
226 PurpleAccount *account;
227 PurpleConnection *gc;
228
229 session = cmdproc->session;
230 account = session->account;
231 gc = purple_account_get_connection(account);
232
233 if (!g_ascii_strcasecmp(cmd->params[1], "OK"))
234 {
235 /* OK */
236 const char *friendly = purple_url_decode(cmd->params[3]);
237
238 purple_connection_set_display_name(gc, friendly);
239
240 msn_session_set_login_step(session, MSN_LOGIN_STEP_SYN);
241
242 msn_cmdproc_send(cmdproc, "SYN", "%s", "0");
243 }
244 else if (!g_ascii_strcasecmp(cmd->params[1], "TWN"))
245 {
246 /* Passport authentication */
247 char **elems, **cur, **tokens;
248
249 session->nexus = msn_nexus_new(session);
250
251 /* Parse the challenge data. */
252
253 elems = g_strsplit(cmd->params[3], ",", 0);
254
255 for (cur = elems; *cur != NULL; cur++)
256 {
257 tokens = g_strsplit(*cur, "=", 2);
258 g_hash_table_insert(session->nexus->challenge_data, tokens[0], tokens[1]);
259 /* Don't free each of the tokens, only the array. */
260 g_free(tokens);
261 }
262
263 g_strfreev(elems);
264
265 msn_session_set_login_step(session, MSN_LOGIN_STEP_AUTH_START);
266
267 msn_nexus_connect(session->nexus);
268 }
269 }
270
271 static void
272 usr_error(MsnCmdProc *cmdproc, MsnTransaction *trans, int error)
273 {
274 MsnErrorType msnerr = 0;
275
276 switch (error)
277 {
278 case 500:
279 case 601:
280 case 910:
281 case 921:
282 msnerr = MSN_ERROR_SERV_UNAVAILABLE;
283 break;
284 case 911:
285 msnerr = MSN_ERROR_AUTH;
286 break;
287 default:
288 return;
289 break;
290 }
291
292 msn_session_set_error(cmdproc->session, msnerr, NULL);
293 }
294
295 static void
296 ver_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
297 {
298 MsnSession *session;
299 PurpleAccount *account;
300 gboolean protocol_supported = FALSE;
301 char proto_str[8];
302 size_t i;
303
304 session = cmdproc->session;
305 account = session->account;
306
307 g_snprintf(proto_str, sizeof(proto_str), "MSNP%d", session->protocol_ver);
308
309 for (i = 1; i < cmd->param_count; i++)
310 {
311 if (!strcmp(cmd->params[i], proto_str))
312 {
313 protocol_supported = TRUE;
314 break;
315 }
316 }
317
318 if (!protocol_supported)
319 {
320 msn_session_set_error(session, MSN_ERROR_UNSUPPORTED_PROTOCOL,
321 NULL);
322 return;
323 }
324
325 msn_cmdproc_send(cmdproc, "CVR",
326 "0x0409 winnt 5.1 i386 MSNMSGR 6.0.0602 MSMSGS %s",
327 purple_account_get_username(account));
328 }
329
330 /**************************************************************************
331 * Log out
332 **************************************************************************/
333
334 static void
335 out_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
336 {
337 if (!g_ascii_strcasecmp(cmd->params[0], "OTH"))
338 msn_session_set_error(cmdproc->session, MSN_ERROR_SIGN_OTHER,
339 NULL);
340 else if (!g_ascii_strcasecmp(cmd->params[0], "SSD"))
341 msn_session_set_error(cmdproc->session, MSN_ERROR_SERV_DOWN, NULL);
342 }
343
344 void
345 msn_notification_close(MsnNotification *notification)
346 {
347 g_return_if_fail(notification != NULL);
348
349 if (!notification->in_use)
350 return;
351
352 msn_cmdproc_send_quick(notification->cmdproc, "OUT", NULL, NULL);
353
354 msn_notification_disconnect(notification);
355 }
356
357 /**************************************************************************
358 * Messages
359 **************************************************************************/
360
361 static void
362 msg_cmd_post(MsnCmdProc *cmdproc, MsnCommand *cmd, char *payload,
363 size_t len)
364 {
365 MsnMessage *msg;
366
367 msg = msn_message_new_from_cmd(cmdproc->session, cmd);
368
369 msn_message_parse_payload(msg, payload, len);
370 #ifdef MSN_DEBUG_NS
371 msn_message_show_readable(msg, "Notification", TRUE);
372 #endif
373
374 msn_cmdproc_process_msg(cmdproc, msg);
375
376 msn_message_destroy(msg);
377 }
378
379 static void
380 msg_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
381 {
382 /* NOTE: cmd is not always cmdproc->last_cmd, sometimes cmd is a queued
383 * command and we are processing it */
384
385 if (cmd->payload == NULL)
386 {
387 cmdproc->last_cmd->payload_cb = msg_cmd_post;
388 cmdproc->servconn->payload_len = atoi(cmd->params[2]);
389 }
390 else
391 {
392 g_return_if_fail(cmd->payload_cb != NULL);
393
394 cmd->payload_cb(cmdproc, cmd, cmd->payload, cmd->payload_len);
395 }
396 }
397
398 /**************************************************************************
399 * Challenges
400 **************************************************************************/
401
402 static void
403 chl_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
404 {
405 MsnTransaction *trans;
406 char buf[33];
407 const char *challenge_resp;
408 PurpleCipher *cipher;
409 PurpleCipherContext *context;
410 guchar digest[16];
411 int i;
412
413 cipher = purple_ciphers_find_cipher("md5");
414 context = purple_cipher_context_new(cipher, NULL);
415
416 purple_cipher_context_append(context, (const guchar *)cmd->params[1],
417 strlen(cmd->params[1]));
418
419 challenge_resp = "VT6PX?UQTM4WM%YR";
420
421 purple_cipher_context_append(context, (const guchar *)challenge_resp,
422 strlen(challenge_resp));
423 purple_cipher_context_digest(context, sizeof(digest), digest, NULL);
424 purple_cipher_context_destroy(context);
425
426 for (i = 0; i < 16; i++)
427 g_snprintf(buf + (i*2), 3, "%02x", digest[i]);
428
429 trans = msn_transaction_new(cmdproc, "QRY", "%s 32", "PROD0038W!61ZTF9");
430
431 msn_transaction_set_payload(trans, buf, 32);
432
433 msn_cmdproc_send_trans(cmdproc, trans);
434 }
435
436 /**************************************************************************
437 * Buddy Lists
438 **************************************************************************/
439
440 static void
441 add_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
442 {
443 MsnSession *session;
444 MsnUser *user;
445 const char *list;
446 const char *passport;
447 const char *friendly;
448 MsnListId list_id;
449 int group_id;
450
451 list = cmd->params[1];
452 passport = cmd->params[3];
453 friendly = purple_url_decode(cmd->params[4]);
454
455 session = cmdproc->session;
456
457 user = msn_userlist_find_user(session->userlist, passport);
458
459 if (user == NULL)
460 {
461 user = msn_user_new(session->userlist, passport, friendly);
462 msn_userlist_add_user(session->userlist, user);
463 }
464 else
465 msn_user_set_friendly_name(user, friendly);
466
467 list_id = msn_get_list_id(list);
468
469 if (cmd->param_count >= 6)
470 group_id = atoi(cmd->params[5]);
471 else
472 group_id = -1;
473
474 msn_got_add_user(session, user, list_id, group_id);
475 msn_user_update(user);
476 }
477
478 static void
479 add_error(MsnCmdProc *cmdproc, MsnTransaction *trans, int error)
480 {
481 MsnSession *session;
482 PurpleAccount *account;
483 PurpleConnection *gc;
484 const char *list, *passport;
485 char *reason = NULL;
486 char *msg = NULL;
487 char **params;
488
489 session = cmdproc->session;
490 account = session->account;
491 gc = purple_account_get_connection(account);
492 params = g_strsplit(trans->params, " ", 0);
493
494 list = params[0];
495 passport = params[1];
496
497 if (!strcmp(list, "FL"))
498 msg = g_strdup_printf(_("Unable to add user on %s (%s)"),
499 purple_account_get_username(account),
500 purple_account_get_protocol_name(account));
501 else if (!strcmp(list, "BL"))
502 msg = g_strdup_printf(_("Unable to block user on %s (%s)"),
503 purple_account_get_username(account),
504 purple_account_get_protocol_name(account));
505 else if (!strcmp(list, "AL"))
506 msg = g_strdup_printf(_("Unable to permit user on %s (%s)"),
507 purple_account_get_username(account),
508 purple_account_get_protocol_name(account));
509
510 if (!strcmp(list, "FL"))
511 {
512 if (error == 210)
513 {
514 reason = g_strdup_printf(_("%s could not be added because "
515 "your buddy list is full."), passport);
516 }
517 }
518
519 if (reason == NULL)
520 {
521 if (error == 208)
522 {
523 reason = g_strdup_printf(_("%s is not a valid passport account."),
524 passport);
525 }
526 else if (error == 500)
527 {
528 reason = g_strdup(_("Service Temporarily Unavailable."));
529 }
530 else
531 {
532 reason = g_strdup(_("Unknown error."));
533 }
534 }
535
536 if (msg != NULL)
537 {
538 purple_notify_error(gc, NULL, msg, reason);
539 g_free(msg);
540 }
541
542 if (!strcmp(list, "FL"))
543 {
544 PurpleBuddy *buddy;
545
546 buddy = purple_find_buddy(account, passport);
547
548 if (buddy != NULL)
549 purple_blist_remove_buddy(buddy);
550 }
551
552 g_free(reason);
553
554 g_strfreev(params);
555 }
556
557 static void
558 adg_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
559 {
560 MsnSession *session;
561 gint group_id;
562 const char *group_name;
563
564 session = cmdproc->session;
565
566 group_id = atoi(cmd->params[3]);
567
568 group_name = purple_url_decode(cmd->params[2]);
569
570 msn_group_new(session->userlist, group_id, group_name);
571
572 /* There is a user that must me moved to this group */
573 if (cmd->trans->data)
574 {
575 /* msn_userlist_move_buddy(); */
576 MsnUserList *userlist = cmdproc->session->userlist;
577 MsnMoveBuddy *data = cmd->trans->data;
578
579 if (data->old_group_name != NULL)
580 {
581 msn_userlist_rem_buddy(userlist, data->who, MSN_LIST_FL, data->old_group_name);
582 g_free(data->old_group_name);
583 }
584
585 msn_userlist_add_buddy(userlist, data->who, MSN_LIST_FL, group_name);
586 g_free(data->who);
587
588 }
589 }
590
591 static void
592 qng_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
593 {
594 MsnSession *session;
595 static int count = 0;
596 const char *passport;
597 PurpleAccount *account;
598
599 session = cmdproc->session;
600 account = session->account;
601
602 if (session->passport_info.file == NULL)
603 return;
604
605 passport = purple_normalize(account, purple_account_get_username(account));
606
607 if ((strstr(passport, "@hotmail.") != NULL) ||
608 (strstr(passport, "@msn.com") != NULL))
609 return;
610
611 if (count++ < 26)
612 return;
613
614 count = 0;
615 msn_cmdproc_send(cmdproc, "URL", "%s", "INBOX");
616 }
617
618
619 static void
620 fln_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
621 {
622 MsnSlpLink *slplink;
623 MsnUser *user;
624
625 user = msn_userlist_find_user(cmdproc->session->userlist, cmd->params[0]);
626
627 user->status = "offline";
628 msn_user_update(user);
629
630 slplink = msn_session_find_slplink(cmdproc->session, cmd->params[0]);
631
632 if (slplink != NULL)
633 msn_slplink_destroy(slplink);
634
635 }
636
637 static void
638 iln_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
639 {
640 MsnSession *session;
641 PurpleAccount *account;
642 PurpleConnection *gc;
643 MsnUser *user;
644 MsnObject *msnobj;
645 const char *state, *passport, *friendly;
646
647 session = cmdproc->session;
648 account = session->account;
649 gc = purple_account_get_connection(account);
650
651 state = cmd->params[1];
652 passport = cmd->params[2];
653 friendly = purple_url_decode(cmd->params[3]);
654
655 user = msn_userlist_find_user(session->userlist, passport);
656
657 serv_got_alias(gc, passport, friendly);
658
659 msn_user_set_friendly_name(user, friendly);
660
661 if (session->protocol_ver >= 9 && cmd->param_count == 6)
662 {
663 msnobj = msn_object_new_from_string(purple_url_decode(cmd->params[5]));
664 msn_user_set_object(user, msnobj);
665 }
666
667 msn_user_set_state(user, state);
668 msn_user_update(user);
669 }
670
671 static void
672 ipg_cmd_post(MsnCmdProc *cmdproc, MsnCommand *cmd, char *payload, size_t len)
673 {
674 #if 0
675 purple_debug_misc("msn", "Incoming Page: {%s}\n", payload);
676 #endif
677 }
678
679 static void
680 ipg_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
681 {
682 cmdproc->servconn->payload_len = atoi(cmd->params[0]);
683 cmdproc->last_cmd->payload_cb = ipg_cmd_post;
684 }
685
686 static void
687 nln_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
688 {
689 MsnSession *session;
690 PurpleAccount *account;
691 PurpleConnection *gc;
692 MsnUser *user;
693 MsnObject *msnobj;
694 int clientid;
695 const char *state, *passport, *friendly, *old_friendly;
696
697 session = cmdproc->session;
698 account = session->account;
699 gc = purple_account_get_connection(account);
700
701 state = cmd->params[0];
702 passport = cmd->params[1];
703 friendly = purple_url_decode(cmd->params[2]);
704
705 user = msn_userlist_find_user(session->userlist, passport);
706
707 old_friendly = msn_user_get_friendly_name(user);
708 if (!old_friendly || (old_friendly && (!friendly || strcmp(old_friendly, friendly))))
709 {
710 serv_got_alias(gc, passport, friendly);
711 msn_user_set_friendly_name(user, friendly);
712 }
713
714 if (session->protocol_ver >= 9)
715 {
716 if (cmd->param_count == 5)
717 {
718 msnobj =
719 msn_object_new_from_string(purple_url_decode(cmd->params[4]));
720 msn_user_set_object(user, msnobj);
721 }
722 else
723 {
724 msn_user_set_object(user, NULL);
725 }
726 }
727
728 clientid = atoi(cmd->params[3]);
729 user->mobile = (clientid & MSN_CLIENT_CAP_MSNMOBILE);
730
731 msn_user_set_state(user, state);
732 msn_user_update(user);
733 }
734
735 #if 0
736 static void
737 chg_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
738 {
739 char *state = cmd->params[1];
740 int state_id = 0;
741
742 if (!strcmp(state, "NLN"))
743 state_id = MSN_ONLINE;
744 else if (!strcmp(state, "BSY"))
745 state_id = MSN_BUSY;
746 else if (!strcmp(state, "IDL"))
747 state_id = MSN_IDLE;
748 else if (!strcmp(state, "BRB"))
749 state_id = MSN_BRB;
750 else if (!strcmp(state, "AWY"))
751 state_id = MSN_AWAY;
752 else if (!strcmp(state, "PHN"))
753 state_id = MSN_PHONE;
754 else if (!strcmp(state, "LUN"))
755 state_id = MSN_LUNCH;
756 else if (!strcmp(state, "HDN"))
757 state_id = MSN_HIDDEN;
758
759 cmdproc->session->state = state_id;
760 }
761 #endif
762
763
764 static void
765 not_cmd_post(MsnCmdProc *cmdproc, MsnCommand *cmd, char *payload, size_t len)
766 {
767 #if 0
768 MSN_SET_PARAMS("NOT %d\r\n%s", cmdproc->servconn->payload, payload);
769 purple_debug_misc("msn", "Notification: {%s}\n", payload);
770 #endif
771 }
772
773 static void
774 not_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
775 {
776 cmdproc->servconn->payload_len = atoi(cmd->params[0]);
777 cmdproc->last_cmd->payload_cb = not_cmd_post;
778 }
779
780 static void
781 rea_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
782 {
783 /* TODO: This might be for us too */
784
785 MsnSession *session;
786 PurpleConnection *gc;
787 const char *friendly;
788
789 session = cmdproc->session;
790 gc = session->account->gc;
791 friendly = purple_url_decode(cmd->params[3]);
792
793 purple_connection_set_display_name(gc, friendly);
794 }
795
796 static void
797 prp_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
798 {
799 MsnSession *session = cmdproc->session;
800 const char *type, *value;
801
802 g_return_if_fail(cmd->param_count >= 3);
803
804 type = cmd->params[2];
805
806 if (cmd->param_count == 4)
807 {
808 value = cmd->params[3];
809 if (!strcmp(type, "PHH"))
810 msn_user_set_home_phone(session->user, purple_url_decode(value));
811 else if (!strcmp(type, "PHW"))
812 msn_user_set_work_phone(session->user, purple_url_decode(value));
813 else if (!strcmp(type, "PHM"))
814 msn_user_set_mobile_phone(session->user, purple_url_decode(value));
815 }
816 else
817 {
818 if (!strcmp(type, "PHH"))
819 msn_user_set_home_phone(session->user, NULL);
820 else if (!strcmp(type, "PHW"))
821 msn_user_set_work_phone(session->user, NULL);
822 else if (!strcmp(type, "PHM"))
823 msn_user_set_mobile_phone(session->user, NULL);
824 }
825 }
826
827 static void
828 reg_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
829 {
830 MsnSession *session;
831 int group_id;
832 const char *group_name;
833
834 session = cmdproc->session;
835 group_id = atoi(cmd->params[2]);
836 group_name = purple_url_decode(cmd->params[3]);
837
838 msn_userlist_rename_group_id(session->userlist, group_id, group_name);
839 }
840
841 static void
842 reg_error(MsnCmdProc *cmdproc, MsnTransaction *trans, int error)
843 {
844 int group_id;
845 char **params;
846
847 params = g_strsplit(trans->params, " ", 0);
848
849 group_id = atoi(params[0]);
850
851 group_error_helper(cmdproc->session, _("Unable to rename group"), group_id, error);
852
853 g_strfreev(params);
854 }
855
856 static void
857 rem_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
858 {
859 MsnSession *session;
860 MsnUser *user;
861 const char *list;
862 const char *passport;
863 MsnListId list_id;
864 int group_id;
865
866 session = cmdproc->session;
867 list = cmd->params[1];
868 passport = cmd->params[3];
869 user = msn_userlist_find_user(session->userlist, passport);
870
871 g_return_if_fail(user != NULL);
872
873 list_id = msn_get_list_id(list);
874
875 if (cmd->param_count == 5)
876 group_id = atoi(cmd->params[4]);
877 else
878 group_id = -1;
879
880 msn_got_rem_user(session, user, list_id, group_id);
881 msn_user_update(user);
882 }
883
884 static void
885 rmg_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
886 {
887 MsnSession *session;
888 int group_id;
889
890 session = cmdproc->session;
891 group_id = atoi(cmd->params[2]);
892
893 msn_userlist_remove_group_id(session->userlist, group_id);
894 }
895
896 static void
897 rmg_error(MsnCmdProc *cmdproc, MsnTransaction *trans, int error)
898 {
899 int group_id;
900 char **params;
901
902 params = g_strsplit(trans->params, " ", 0);
903
904 group_id = atoi(params[0]);
905
906 group_error_helper(cmdproc->session, _("Unable to delete group"), group_id, error);
907
908 g_strfreev(params);
909 }
910
911 static void
912 syn_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
913 {
914 MsnSession *session;
915 MsnSync *sync;
916 int total_users;
917
918 session = cmdproc->session;
919
920 if (cmd->param_count == 2)
921 {
922 /*
923 * This can happen if we sent a SYN with an up-to-date
924 * buddy list revision, but we send 0 to get a full list.
925 * So, error out.
926 */
927
928 msn_session_set_error(cmdproc->session, MSN_ERROR_BAD_BLIST, NULL);
929 return;
930 }
931
932 total_users = atoi(cmd->params[2]);
933
934 sync = msn_sync_new(session);
935 sync->total_users = total_users;
936 sync->old_cbs_table = cmdproc->cbs_table;
937
938 session->sync = sync;
939 cmdproc->cbs_table = sync->cbs_table;
940 }
941
942 /**************************************************************************
943 * Misc commands
944 **************************************************************************/
945
946 static void
947 url_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
948 {
949 MsnSession *session;
950 PurpleAccount *account;
951 const char *rru;
952 const char *url;
953 PurpleCipher *cipher;
954 PurpleCipherContext *context;
955 guchar digest[16];
956 FILE *fd;
957 char *buf;
958 char buf2[3];
959 char sendbuf[64];
960 int i;
961
962 session = cmdproc->session;
963 account = session->account;
964
965 rru = cmd->params[1];
966 url = cmd->params[2];
967
968 buf = g_strdup_printf("%s%lu%s",
969 session->passport_info.mspauth ? session->passport_info.mspauth : "BOGUS",
970 time(NULL) - session->passport_info.sl,
971 purple_connection_get_password(account->gc));
972
973 cipher = purple_ciphers_find_cipher("md5");
974 context = purple_cipher_context_new(cipher, NULL);
975
976 purple_cipher_context_append(context, (const guchar *)buf, strlen(buf));
977 purple_cipher_context_digest(context, sizeof(digest), digest, NULL);
978 purple_cipher_context_destroy(context);
979
980 g_free(buf);
981
982 memset(sendbuf, 0, sizeof(sendbuf));
983
984 for (i = 0; i < 16; i++)
985 {
986 g_snprintf(buf2, sizeof(buf2), "%02x", digest[i]);
987 strcat(sendbuf, buf2);
988 }
989
990 if (session->passport_info.file != NULL)
991 {
992 g_unlink(session->passport_info.file);
993 g_free(session->passport_info.file);
994 }
995
996 if ((fd = purple_mkstemp(&session->passport_info.file, FALSE)) == NULL)
997 {
998 purple_debug_error("msn",
999 "Error opening temp passport file: %s\n",
1000 strerror(errno));
1001 }
1002 else
1003 {
1004 #ifdef _WIN32
1005 fputs("<!-- saved from url=(0013)about:internet -->\n", fd);
1006 #endif
1007 fputs("<html>\n"
1008 "<head>\n"
1009 "<noscript>\n"
1010 "<meta http-equiv=\"Refresh\" content=\"0; "
1011 "url=http://www.hotmail.com\">\n"
1012 "</noscript>\n"
1013 "</head>\n\n",
1014 fd);
1015
1016 fprintf(fd, "<body onload=\"document.pform.submit(); \">\n");
1017 fprintf(fd, "<form name=\"pform\" action=\"%s\" method=\"POST\">\n\n",
1018 url);
1019 fprintf(fd, "<input type=\"hidden\" name=\"mode\" value=\"ttl\">\n");
1020 fprintf(fd, "<input type=\"hidden\" name=\"login\" value=\"%s\">\n",
1021 purple_account_get_username(account));
1022 fprintf(fd, "<input type=\"hidden\" name=\"username\" value=\"%s\">\n",
1023 purple_account_get_username(account));
1024 if (session->passport_info.sid != NULL)
1025 fprintf(fd, "<input type=\"hidden\" name=\"sid\" value=\"%s\">\n",
1026 session->passport_info.sid);
1027 if (session->passport_info.kv != NULL)
1028 fprintf(fd, "<input type=\"hidden\" name=\"kv\" value=\"%s\">\n",
1029 session->passport_info.kv);
1030 fprintf(fd, "<input type=\"hidden\" name=\"id\" value=\"2\">\n");
1031 fprintf(fd, "<input type=\"hidden\" name=\"sl\" value=\"%ld\">\n",
1032 time(NULL) - session->passport_info.sl);
1033 fprintf(fd, "<input type=\"hidden\" name=\"rru\" value=\"%s\">\n",
1034 rru);
1035 if (session->passport_info.mspauth != NULL)
1036 fprintf(fd, "<input type=\"hidden\" name=\"auth\" value=\"%s\">\n",
1037 session->passport_info.mspauth);
1038 fprintf(fd, "<input type=\"hidden\" name=\"creds\" value=\"%s\">\n",
1039 sendbuf); /* TODO Digest me (huh? -- ChipX86) */
1040 fprintf(fd, "<input type=\"hidden\" name=\"svc\" value=\"mail\">\n");
1041 fprintf(fd, "<input type=\"hidden\" name=\"js\" value=\"yes\">\n");
1042 fprintf(fd, "</form></body>\n");
1043 fprintf(fd, "</html>\n");
1044
1045 if (fclose(fd))
1046 {
1047 purple_debug_error("msn",
1048 "Error closing temp passport file: %s\n",
1049 strerror(errno));
1050
1051 g_unlink(session->passport_info.file);
1052 g_free(session->passport_info.file);
1053 session->passport_info.file = NULL;
1054 }
1055 #ifdef _WIN32
1056 else
1057 {
1058 /*
1059 * Renaming file with .html extension, so that the
1060 * win32 open_url will work.
1061 */
1062 char *tmp;
1063
1064 if ((tmp =
1065 g_strdup_printf("%s.html",
1066 session->passport_info.file)) != NULL)
1067 {
1068 if (g_rename(session->passport_info.file,
1069 tmp) == 0)
1070 {
1071 g_free(session->passport_info.file);
1072 session->passport_info.file = tmp;
1073 }
1074 else
1075 g_free(tmp);
1076 }
1077 }
1078 #endif
1079 }
1080 }
1081 /**************************************************************************
1082 * Switchboards
1083 **************************************************************************/
1084
1085 static void
1086 rng_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
1087 {
1088 MsnSession *session;
1089 MsnSwitchBoard *swboard;
1090 const char *session_id;
1091 char *host;
1092 int port;
1093
1094 session = cmdproc->session;
1095 session_id = cmd->params[0];
1096
1097 msn_parse_socket(cmd->params[1], &host, &port);
1098
1099 if (session->http_method)
1100 port = 80;
1101
1102 swboard = msn_switchboard_new(session);
1103
1104 msn_switchboard_set_invited(swboard, TRUE);
1105 msn_switchboard_set_session_id(swboard, cmd->params[0]);
1106 msn_switchboard_set_auth_key(swboard, cmd->params[3]);
1107 swboard->im_user = g_strdup(cmd->params[4]);
1108 /* msn_switchboard_add_user(swboard, cmd->params[4]); */
1109
1110 if (!msn_switchboard_connect(swboard, host, port))
1111 msn_switchboard_destroy(swboard);
1112
1113 g_free(host);
1114 }
1115
1116 static void
1117 xfr_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
1118 {
1119 char *host;
1120 int port;
1121
1122 if (strcmp(cmd->params[1], "SB") && strcmp(cmd->params[1], "NS"))
1123 {
1124 /* Maybe we can have a generic bad command error. */
1125 purple_debug_error("msn", "Bad XFR command (%s)\n", cmd->params[1]);
1126 return;
1127 }
1128
1129 msn_parse_socket(cmd->params[2], &host, &port);
1130
1131 if (!strcmp(cmd->params[1], "SB"))
1132 {
1133 purple_debug_error("msn", "This shouldn't be handled here.\n");
1134 }
1135 else if (!strcmp(cmd->params[1], "NS"))
1136 {
1137 MsnSession *session;
1138
1139 session = cmdproc->session;
1140
1141 msn_session_set_login_step(session, MSN_LOGIN_STEP_TRANSFER);
1142
1143 msn_notification_connect(session->notification, host, port);
1144 }
1145
1146 g_free(host);
1147 }
1148
1149 /**************************************************************************
1150 * Message Types
1151 **************************************************************************/
1152
1153 static void
1154 profile_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
1155 {
1156 MsnSession *session;
1157 const char *value;
1158
1159 session = cmdproc->session;
1160
1161 if (strcmp(msg->remote_user, "Hotmail"))
1162 /* This isn't an official message. */
1163 return;
1164
1165 if ((value = msn_message_get_attr(msg, "kv")) != NULL)
1166 {
1167 g_free(session->passport_info.kv);
1168 session->passport_info.kv = g_strdup(value);
1169 }
1170
1171 if ((value = msn_message_get_attr(msg, "sid")) != NULL)
1172 {
1173 g_free(session->passport_info.sid);
1174 session->passport_info.sid = g_strdup(value);
1175 }
1176
1177 if ((value = msn_message_get_attr(msg, "MSPAuth")) != NULL)
1178 {
1179 g_free(session->passport_info.mspauth);
1180 session->passport_info.mspauth = g_strdup(value);
1181 }
1182
1183 if ((value = msn_message_get_attr(msg, "ClientIP")) != NULL)
1184 {
1185 g_free(session->passport_info.client_ip);
1186 session->passport_info.client_ip = g_strdup(value);
1187 }
1188
1189 if ((value = msn_message_get_attr(msg, "ClientPort")) != NULL)
1190 session->passport_info.client_port = ntohs(atoi(value));
1191
1192 if ((value = msn_message_get_attr(msg, "LoginTime")) != NULL)
1193 session->passport_info.sl = atol(value);
1194 }
1195
1196 static void
1197 initial_email_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
1198 {
1199 MsnSession *session;
1200 PurpleConnection *gc;
1201 GHashTable *table;
1202 const char *unread;
1203
1204 session = cmdproc->session;
1205 gc = session->account->gc;
1206
1207 if (strcmp(msg->remote_user, "Hotmail"))
1208 /* This isn't an official message. */
1209 return;
1210
1211 if (session->passport_info.file == NULL)
1212 {
1213 MsnTransaction *trans;
1214 trans = msn_transaction_new(cmdproc, "URL", "%s", "INBOX");
1215 msn_transaction_queue_cmd(trans, msg->cmd);
1216
1217 msn_cmdproc_send_trans(cmdproc, trans);
1218
1219 return;
1220 }
1221
1222 if (!purple_account_get_check_mail(session->account))
1223 return;
1224
1225 table = msn_message_get_hashtable_from_body(msg);
1226
1227 unread = g_hash_table_lookup(table, "Inbox-Unread");
1228
1229 if (unread != NULL)
1230 {
1231 int count = atoi(unread);
1232
1233 if (count > 0)
1234 {
1235 const char *passport;
1236 const char *url;
1237
1238 passport = msn_user_get_passport(session->user);
1239 url = session->passport_info.file;
1240
1241 purple_notify_emails(gc, atoi(unread), FALSE, NULL, NULL,
1242 &passport, &url, NULL, NULL);
1243 }
1244 }
1245
1246 g_hash_table_destroy(table);
1247 }
1248
1249 static void
1250 email_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
1251 {
1252 MsnSession *session;
1253 PurpleConnection *gc;
1254 GHashTable *table;
1255 char *from, *subject, *tmp;
1256
1257 session = cmdproc->session;
1258 gc = session->account->gc;
1259
1260 if (strcmp(msg->remote_user, "Hotmail"))
1261 /* This isn't an official message. */
1262 return;
1263
1264 if (session->passport_info.file == NULL)
1265 {
1266 MsnTransaction *trans;
1267 trans = msn_transaction_new(cmdproc, "URL", "%s", "INBOX");
1268 msn_transaction_queue_cmd(trans, msg->cmd);
1269
1270 msn_cmdproc_send_trans(cmdproc, trans);
1271
1272 return;
1273 }
1274
1275 if (!purple_account_get_check_mail(session->account))
1276 return;
1277
1278 table = msn_message_get_hashtable_from_body(msg);
1279
1280 from = subject = NULL;
1281
1282 tmp = g_hash_table_lookup(table, "From");
1283 if (tmp != NULL)
1284 from = purple_mime_decode_field(tmp);
1285
1286 tmp = g_hash_table_lookup(table, "Subject");
1287 if (tmp != NULL)
1288 subject = purple_mime_decode_field(tmp);
1289
1290 purple_notify_email(gc,
1291 (subject != NULL ? subject : ""),
1292 (from != NULL ? from : ""),
1293 msn_user_get_passport(session->user),
1294 session->passport_info.file, NULL, NULL);
1295
1296 g_free(from);
1297 g_free(subject);
1298
1299 g_hash_table_destroy(table);
1300 }
1301
1302 static void
1303 system_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
1304 {
1305 GHashTable *table;
1306 const char *type_s;
1307
1308 if (strcmp(msg->remote_user, "Hotmail"))
1309 /* This isn't an official message. */
1310 return;
1311
1312 table = msn_message_get_hashtable_from_body(msg);
1313
1314 if ((type_s = g_hash_table_lookup(table, "Type")) != NULL)
1315 {
1316 int type = atoi(type_s);
1317 char buf[MSN_BUF_LEN];
1318 int minutes;
1319
1320 switch (type)
1321 {
1322 case 1:
1323 minutes = atoi(g_hash_table_lookup(table, "Arg1"));
1324 g_snprintf(buf, sizeof(buf), dngettext(PACKAGE,
1325 "The MSN server will shut down for maintenance "
1326 "in %d minute. You will automatically be "
1327 "signed out at that time. Please finish any "
1328 "conversations in progress.\n\nAfter the "
1329 "maintenance has been completed, you will be "
1330 "able to successfully sign in.",
1331 "The MSN server will shut down for maintenance "
1332 "in %d minutes. You will automatically be "
1333 "signed out at that time. Please finish any "
1334 "conversations in progress.\n\nAfter the "
1335 "maintenance has been completed, you will be "
1336 "able to successfully sign in.", minutes),
1337 minutes);
1338 default:
1339 break;
1340 }
1341
1342 if (*buf != '\0')
1343 purple_notify_info(cmdproc->session->account->gc, NULL, buf, NULL);
1344 }
1345
1346 g_hash_table_destroy(table);
1347 }
1348
1349 void
1350 msn_notification_add_buddy(MsnNotification *notification, const char *list,
1351 const char *who, const char *store_name,
1352 int group_id)
1353 {
1354 MsnCmdProc *cmdproc;
1355 cmdproc = notification->servconn->cmdproc;
1356
1357 if (group_id < 0 && !strcmp(list, "FL"))
1358 group_id = 0;
1359
1360 if (group_id >= 0)
1361 {
1362 msn_cmdproc_send(cmdproc, "ADD", "%s %s %s %d",
1363 list, who, store_name, group_id);
1364 }
1365 else
1366 {
1367 msn_cmdproc_send(cmdproc, "ADD", "%s %s %s", list, who, store_name);
1368 }
1369 }
1370
1371 void
1372 msn_notification_rem_buddy(MsnNotification *notification, const char *list,
1373 const char *who, int group_id)
1374 {
1375 MsnCmdProc *cmdproc;
1376 cmdproc = notification->servconn->cmdproc;
1377
1378 if (group_id >= 0)
1379 {
1380 msn_cmdproc_send(cmdproc, "REM", "%s %s %d", list, who, group_id);
1381 }
1382 else
1383 {
1384 msn_cmdproc_send(cmdproc, "REM", "%s %s", list, who);
1385 }
1386 }
1387
1388 /**************************************************************************
1389 * Init
1390 **************************************************************************/
1391
1392 void
1393 msn_notification_init(void)
1394 {
1395 /* TODO: check prp, blp */
1396
1397 cbs_table = msn_table_new();
1398
1399 /* Synchronous */
1400 msn_table_add_cmd(cbs_table, "CHG", "CHG", NULL);
1401 msn_table_add_cmd(cbs_table, "CHG", "ILN", iln_cmd);
1402 msn_table_add_cmd(cbs_table, "ADD", "ADD", add_cmd);
1403 msn_table_add_cmd(cbs_table, "ADD", "ILN", iln_cmd);
1404 msn_table_add_cmd(cbs_table, "REM", "REM", rem_cmd);
1405 msn_table_add_cmd(cbs_table, "USR", "USR", usr_cmd);
1406 msn_table_add_cmd(cbs_table, "USR", "XFR", xfr_cmd);
1407 msn_table_add_cmd(cbs_table, "SYN", "SYN", syn_cmd);
1408 msn_table_add_cmd(cbs_table, "CVR", "CVR", cvr_cmd);
1409 msn_table_add_cmd(cbs_table, "VER", "VER", ver_cmd);
1410 msn_table_add_cmd(cbs_table, "REA", "REA", rea_cmd);
1411 msn_table_add_cmd(cbs_table, "PRP", "PRP", prp_cmd);
1412 /* msn_table_add_cmd(cbs_table, "BLP", "BLP", blp_cmd); */
1413 msn_table_add_cmd(cbs_table, "BLP", "BLP", NULL);
1414 msn_table_add_cmd(cbs_table, "REG", "REG", reg_cmd);
1415 msn_table_add_cmd(cbs_table, "ADG", "ADG", adg_cmd);
1416 msn_table_add_cmd(cbs_table, "RMG", "RMG", rmg_cmd);
1417 msn_table_add_cmd(cbs_table, "XFR", "XFR", xfr_cmd);
1418
1419 /* Asynchronous */
1420 msn_table_add_cmd(cbs_table, NULL, "IPG", ipg_cmd);
1421 msn_table_add_cmd(cbs_table, NULL, "MSG", msg_cmd);
1422 msn_table_add_cmd(cbs_table, NULL, "NOT", not_cmd);
1423
1424 msn_table_add_cmd(cbs_table, NULL, "CHL", chl_cmd);
1425 msn_table_add_cmd(cbs_table, NULL, "REM", rem_cmd);
1426 msn_table_add_cmd(cbs_table, NULL, "ADD", add_cmd);
1427
1428 msn_table_add_cmd(cbs_table, NULL, "QRY", NULL);
1429 msn_table_add_cmd(cbs_table, NULL, "QNG", qng_cmd);
1430 msn_table_add_cmd(cbs_table, NULL, "FLN", fln_cmd);
1431 msn_table_add_cmd(cbs_table, NULL, "NLN", nln_cmd);
1432 msn_table_add_cmd(cbs_table, NULL, "ILN", iln_cmd);
1433 msn_table_add_cmd(cbs_table, NULL, "OUT", out_cmd);
1434 msn_table_add_cmd(cbs_table, NULL, "RNG", rng_cmd);
1435
1436 msn_table_add_cmd(cbs_table, NULL, "URL", url_cmd);
1437
1438 msn_table_add_cmd(cbs_table, "fallback", "XFR", xfr_cmd);
1439
1440 msn_table_add_error(cbs_table, "ADD", add_error);
1441 msn_table_add_error(cbs_table, "REG", reg_error);
1442 msn_table_add_error(cbs_table, "RMG", rmg_error);
1443 /* msn_table_add_error(cbs_table, "REA", rea_error); */
1444 msn_table_add_error(cbs_table, "USR", usr_error);
1445
1446 msn_table_add_msg_type(cbs_table,
1447 "text/x-msmsgsprofile",
1448 profile_msg);
1449 msn_table_add_msg_type(cbs_table,
1450 "text/x-msmsgsinitialemailnotification",
1451 initial_email_msg);
1452 msn_table_add_msg_type(cbs_table,
1453 "text/x-msmsgsemailnotification",
1454 email_msg);
1455 msn_table_add_msg_type(cbs_table,
1456 "application/x-msmsgssystemmessage",
1457 system_msg);
1458 }
1459
1460 void
1461 msn_notification_end(void)
1462 {
1463 msn_table_destroy(cbs_table);
1464 }