comparison libpurple/protocols/msnp9/msn.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 73788974b883
comparison
equal deleted inserted replaced
21311:7d031cec5ba2 21312:a07cfce78345
1 /**
2 * @file msn.c The MSN protocol plugin
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 #define PHOTO_SUPPORT 1
25
26 #include <glib.h>
27
28 #include "msn.h"
29 #include "accountopt.h"
30 #include "msg.h"
31 #include "page.h"
32 #include "pluginpref.h"
33 #include "prefs.h"
34 #include "session.h"
35 #include "state.h"
36 #include "util.h"
37 #include "cmds.h"
38 #include "core.h"
39 #include "prpl.h"
40 #include "msn-utils.h"
41 #include "version.h"
42
43 #include "switchboard.h"
44 #include "notification.h"
45 #include "sync.h"
46 #include "slplink.h"
47
48 #if PHOTO_SUPPORT
49 #include "imgstore.h"
50 #endif
51
52 typedef struct
53 {
54 PurpleConnection *gc;
55 const char *passport;
56
57 } MsnMobileData;
58
59 typedef struct
60 {
61 PurpleConnection *gc;
62 char *name;
63
64 } MsnGetInfoData;
65
66 typedef struct
67 {
68 MsnGetInfoData *info_data;
69 char *stripped;
70 char *url_buffer;
71 PurpleNotifyUserInfo *user_info;
72 char *photo_url_text;
73
74 } MsnGetInfoStepTwoData;
75
76 typedef struct
77 {
78 PurpleConnection *gc;
79 const char *who;
80 char *msg;
81 PurpleMessageFlags flags;
82 time_t when;
83 } MsnIMData;
84
85 static const char *
86 msn_normalize(const PurpleAccount *account, const char *str)
87 {
88 static char buf[BUF_LEN];
89 char *tmp;
90
91 g_return_val_if_fail(str != NULL, NULL);
92
93 g_snprintf(buf, sizeof(buf), "%s%s", str,
94 (strchr(str, '@') ? "" : "@hotmail.com"));
95
96 tmp = g_utf8_strdown(buf, -1);
97 strncpy(buf, tmp, sizeof(buf));
98 g_free(tmp);
99
100 return buf;
101 }
102
103 static gboolean
104 msn_send_attention(PurpleConnection *gc, const char *username, guint type)
105 {
106 MsnMessage *msg;
107 MsnSession *session;
108 MsnSwitchBoard *swboard;
109
110 msg = msn_message_new_nudge();
111 session = gc->proto_data;
112 swboard = msn_session_get_swboard(session, username, MSN_SB_FLAG_IM);
113
114 if (swboard == NULL)
115 return FALSE;
116
117 msn_switchboard_send_msg(swboard, msg, TRUE);
118
119 return TRUE;
120 }
121
122 static GList *
123 msn_attention_types(PurpleAccount *account)
124 {
125 PurpleAttentionType *attn;
126 static GList *list = NULL;
127
128 if (!list) {
129 attn = g_new0(PurpleAttentionType, 1);
130 attn->name = _("Nudge");
131 attn->incoming_description = _("%s has nudged you!");
132 attn->outgoing_description = _("Nudging %s...");
133 list = g_list_append(list, attn);
134 }
135
136 return list;
137 }
138
139
140 static PurpleCmdRet
141 msn_cmd_nudge(PurpleConversation *conv, const gchar *cmd, gchar **args, gchar **error, void *data)
142 {
143 PurpleAccount *account = purple_conversation_get_account(conv);
144 PurpleConnection *gc = purple_account_get_connection(account);
145 const gchar *username;
146
147 username = purple_conversation_get_name(conv);
148
149 serv_send_attention(gc, username, MSN_NUDGE);
150
151 return PURPLE_CMD_RET_OK;
152 }
153
154 static void
155 msn_act_id(PurpleConnection *gc, const char *entry)
156 {
157 MsnCmdProc *cmdproc;
158 MsnSession *session;
159 PurpleAccount *account;
160 const char *alias;
161
162 session = gc->proto_data;
163 cmdproc = session->notification->cmdproc;
164 account = purple_connection_get_account(gc);
165
166 if(entry && strlen(entry))
167 alias = purple_url_encode(entry);
168 else
169 alias = "";
170
171 if (strlen(alias) > BUDDY_ALIAS_MAXLEN)
172 {
173 purple_notify_error(gc, NULL,
174 _("Your new MSN friendly name is too long."), NULL);
175 return;
176 }
177
178 msn_cmdproc_send(cmdproc, "REA", "%s %s",
179 purple_account_get_username(account),
180 alias);
181 }
182
183 static void
184 msn_set_prp(PurpleConnection *gc, const char *type, const char *entry)
185 {
186 MsnCmdProc *cmdproc;
187 MsnSession *session;
188
189 session = gc->proto_data;
190 cmdproc = session->notification->cmdproc;
191
192 if (entry == NULL || *entry == '\0')
193 {
194 msn_cmdproc_send(cmdproc, "PRP", "%s", type);
195 }
196 else
197 {
198 msn_cmdproc_send(cmdproc, "PRP", "%s %s", type,
199 purple_url_encode(entry));
200 }
201 }
202
203 static void
204 msn_set_home_phone_cb(PurpleConnection *gc, const char *entry)
205 {
206 msn_set_prp(gc, "PHH", entry);
207 }
208
209 static void
210 msn_set_work_phone_cb(PurpleConnection *gc, const char *entry)
211 {
212 msn_set_prp(gc, "PHW", entry);
213 }
214
215 static void
216 msn_set_mobile_phone_cb(PurpleConnection *gc, const char *entry)
217 {
218 msn_set_prp(gc, "PHM", entry);
219 }
220
221 static void
222 enable_msn_pages_cb(PurpleConnection *gc)
223 {
224 msn_set_prp(gc, "MOB", "Y");
225 }
226
227 static void
228 disable_msn_pages_cb(PurpleConnection *gc)
229 {
230 msn_set_prp(gc, "MOB", "N");
231 }
232
233 static void
234 send_to_mobile(PurpleConnection *gc, const char *who, const char *entry)
235 {
236 MsnTransaction *trans;
237 MsnSession *session;
238 MsnCmdProc *cmdproc;
239 MsnPage *page;
240 char *payload;
241 size_t payload_len;
242
243 session = gc->proto_data;
244 cmdproc = session->notification->cmdproc;
245
246 page = msn_page_new();
247 msn_page_set_body(page, entry);
248
249 payload = msn_page_gen_payload(page, &payload_len);
250
251 trans = msn_transaction_new(cmdproc, "PGD", "%s 1 %d", who, payload_len);
252
253 msn_transaction_set_payload(trans, payload, payload_len);
254
255 msn_page_destroy(page);
256
257 msn_cmdproc_send_trans(cmdproc, trans);
258 }
259
260 static void
261 send_to_mobile_cb(MsnMobileData *data, const char *entry)
262 {
263 send_to_mobile(data->gc, data->passport, entry);
264 g_free(data);
265 }
266
267 static void
268 close_mobile_page_cb(MsnMobileData *data, const char *entry)
269 {
270 g_free(data);
271 }
272
273 /* -- */
274
275 static void
276 msn_show_set_friendly_name(PurplePluginAction *action)
277 {
278 PurpleConnection *gc;
279
280 gc = (PurpleConnection *) action->context;
281
282 purple_request_input(gc, NULL, _("Set your friendly name."),
283 _("This is the name that other MSN buddies will "
284 "see you as."),
285 purple_connection_get_display_name(gc), FALSE, FALSE, NULL,
286 _("OK"), G_CALLBACK(msn_act_id),
287 _("Cancel"), NULL,
288 purple_connection_get_account(gc), NULL, NULL,
289 gc);
290 }
291
292 static void
293 msn_show_set_home_phone(PurplePluginAction *action)
294 {
295 PurpleConnection *gc;
296 MsnSession *session;
297
298 gc = (PurpleConnection *) action->context;
299 session = gc->proto_data;
300
301 purple_request_input(gc, NULL, _("Set your home phone number."), NULL,
302 msn_user_get_home_phone(session->user), FALSE, FALSE, NULL,
303 _("OK"), G_CALLBACK(msn_set_home_phone_cb),
304 _("Cancel"), NULL,
305 purple_connection_get_account(gc), NULL, NULL,
306 gc);
307 }
308
309 static void
310 msn_show_set_work_phone(PurplePluginAction *action)
311 {
312 PurpleConnection *gc;
313 MsnSession *session;
314
315 gc = (PurpleConnection *) action->context;
316 session = gc->proto_data;
317
318 purple_request_input(gc, NULL, _("Set your work phone number."), NULL,
319 msn_user_get_work_phone(session->user), FALSE, FALSE, NULL,
320 _("OK"), G_CALLBACK(msn_set_work_phone_cb),
321 _("Cancel"), NULL,
322 purple_connection_get_account(gc), NULL, NULL,
323 gc);
324 }
325
326 static void
327 msn_show_set_mobile_phone(PurplePluginAction *action)
328 {
329 PurpleConnection *gc;
330 MsnSession *session;
331
332 gc = (PurpleConnection *) action->context;
333 session = gc->proto_data;
334
335 purple_request_input(gc, NULL, _("Set your mobile phone number."), NULL,
336 msn_user_get_mobile_phone(session->user), FALSE, FALSE, NULL,
337 _("OK"), G_CALLBACK(msn_set_mobile_phone_cb),
338 _("Cancel"), NULL,
339 purple_connection_get_account(gc), NULL, NULL,
340 gc);
341 }
342
343 static void
344 msn_show_set_mobile_pages(PurplePluginAction *action)
345 {
346 PurpleConnection *gc;
347
348 gc = (PurpleConnection *) action->context;
349
350 purple_request_action(gc, NULL, _("Allow MSN Mobile pages?"),
351 _("Do you want to allow or disallow people on "
352 "your buddy list to send you MSN Mobile pages "
353 "to your cell phone or other mobile device?"),
354 -1,
355 purple_connection_get_account(gc), NULL, NULL,
356 gc, 3,
357 _("Allow"), G_CALLBACK(enable_msn_pages_cb),
358 _("Disallow"), G_CALLBACK(disable_msn_pages_cb),
359 _("Cancel"), NULL);
360 }
361
362 static void
363 msn_show_hotmail_inbox(PurplePluginAction *action)
364 {
365 PurpleConnection *gc;
366 MsnSession *session;
367
368 gc = (PurpleConnection *) action->context;
369 session = gc->proto_data;
370
371 if (session->passport_info.file == NULL)
372 {
373 purple_notify_error(gc, NULL,
374 _("This Hotmail account may not be active."), NULL);
375 return;
376 }
377
378 purple_notify_uri(gc, session->passport_info.file);
379 }
380
381 static void
382 show_send_to_mobile_cb(PurpleBlistNode *node, gpointer ignored)
383 {
384 PurpleBuddy *buddy;
385 PurpleConnection *gc;
386 MsnSession *session;
387 MsnMobileData *data;
388
389 g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
390
391 buddy = (PurpleBuddy *) node;
392 gc = purple_account_get_connection(buddy->account);
393
394 session = gc->proto_data;
395
396 data = g_new0(MsnMobileData, 1);
397 data->gc = gc;
398 data->passport = buddy->name;
399
400 purple_request_input(gc, NULL, _("Send a mobile message."), NULL,
401 NULL, TRUE, FALSE, NULL,
402 _("Page"), G_CALLBACK(send_to_mobile_cb),
403 _("Close"), G_CALLBACK(close_mobile_page_cb),
404 purple_connection_get_account(gc), purple_buddy_get_name(buddy), NULL,
405 data);
406 }
407
408 static gboolean
409 msn_offline_message(const PurpleBuddy *buddy) {
410 MsnUser *user;
411 if (buddy == NULL)
412 return FALSE;
413 user = buddy->proto_data;
414 return user && user->mobile;
415 }
416
417 static void
418 initiate_chat_cb(PurpleBlistNode *node, gpointer data)
419 {
420 PurpleBuddy *buddy;
421 PurpleConnection *gc;
422
423 MsnSession *session;
424 MsnSwitchBoard *swboard;
425
426 g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
427
428 buddy = (PurpleBuddy *) node;
429 gc = purple_account_get_connection(buddy->account);
430
431 session = gc->proto_data;
432
433 swboard = msn_switchboard_new(session);
434 msn_switchboard_request(swboard);
435 msn_switchboard_request_add_user(swboard, buddy->name);
436
437 /* TODO: This might move somewhere else, after USR might be */
438 swboard->chat_id = session->conv_seq++;
439 swboard->conv = serv_got_joined_chat(gc, swboard->chat_id, "MSN Chat");
440 swboard->flag = MSN_SB_FLAG_IM;
441
442 purple_conv_chat_add_user(PURPLE_CONV_CHAT(swboard->conv),
443 purple_account_get_username(buddy->account), NULL, PURPLE_CBFLAGS_NONE, TRUE);
444 }
445
446 static void
447 t_msn_xfer_init(PurpleXfer *xfer)
448 {
449 MsnSlpLink *slplink = xfer->data;
450 msn_slplink_request_ft(slplink, xfer);
451 }
452
453 static PurpleXfer*
454 msn_new_xfer(PurpleConnection *gc, const char *who)
455 {
456 MsnSession *session;
457 MsnSlpLink *slplink;
458 PurpleXfer *xfer;
459
460 session = gc->proto_data;
461
462 xfer = purple_xfer_new(gc->account, PURPLE_XFER_SEND, who);
463 if (xfer)
464 {
465 slplink = msn_session_get_slplink(session, who);
466
467 xfer->data = slplink;
468
469 purple_xfer_set_init_fnc(xfer, t_msn_xfer_init);
470 }
471
472 return xfer;
473 }
474
475 static void
476 msn_send_file(PurpleConnection *gc, const char *who, const char *file)
477 {
478 PurpleXfer *xfer = msn_new_xfer(gc, who);
479
480 if (file)
481 purple_xfer_request_accepted(xfer, file);
482 else
483 purple_xfer_request(xfer);
484 }
485
486 static gboolean
487 msn_can_receive_file(PurpleConnection *gc, const char *who)
488 {
489 PurpleAccount *account;
490 char *normal;
491 gboolean ret;
492
493 account = purple_connection_get_account(gc);
494
495 normal = g_strdup(msn_normalize(account, purple_account_get_username(account)));
496
497 ret = strcmp(normal, msn_normalize(account, who));
498
499 g_free(normal);
500
501 return ret;
502 }
503
504 /**************************************************************************
505 * Protocol Plugin ops
506 **************************************************************************/
507
508 static const char *
509 msn_list_icon(PurpleAccount *a, PurpleBuddy *b)
510 {
511 return "msn";
512 }
513
514 static char *
515 msn_status_text(PurpleBuddy *buddy)
516 {
517 PurplePresence *presence;
518 PurpleStatus *status;
519
520 presence = purple_buddy_get_presence(buddy);
521 status = purple_presence_get_active_status(presence);
522
523 if (!purple_presence_is_available(presence) && !purple_presence_is_idle(presence))
524 {
525 return g_strdup(purple_status_get_name(status));
526 }
527
528 return NULL;
529 }
530
531 static void
532 msn_tooltip_text(PurpleBuddy *buddy, PurpleNotifyUserInfo *user_info, gboolean full)
533 {
534 MsnUser *user;
535 PurplePresence *presence = purple_buddy_get_presence(buddy);
536 PurpleStatus *status = purple_presence_get_active_status(presence);
537
538 user = buddy->proto_data;
539
540
541 if (purple_presence_is_online(presence))
542 {
543 purple_notify_user_info_add_pair(user_info, _("Status"),
544 (purple_presence_is_idle(presence) ? _("Idle") : purple_status_get_name(status)));
545 }
546
547 if (full && user)
548 {
549 purple_notify_user_info_add_pair(user_info, _("Has you"),
550 ((user->list_op & (1 << MSN_LIST_RL)) ? _("Yes") : _("No")));
551 }
552
553 /* XXX: This is being shown in non-full tooltips because the
554 * XXX: blocked icon overlay isn't always accurate for MSN.
555 * XXX: This can die as soon as purple_privacy_check() knows that
556 * XXX: this prpl always honors both the allow and deny lists. */
557 /* While the above comment may be strictly correct (the privacy API needs
558 * rewriteing), purple_privacy_check() is going to be more accurate at
559 * indicating whether a particular buddy is going to be able to message
560 * you, which is the important information that this is trying to convey. */
561 if (full && user)
562 {
563 purple_notify_user_info_add_pair(user_info, _("Blocked"),
564 ((user->list_op & (1 << MSN_LIST_BL)) ? _("Yes") : _("No")));
565 }
566 }
567
568 static GList *
569 msn_status_types(PurpleAccount *account)
570 {
571 PurpleStatusType *status;
572 GList *types = NULL;
573
574 status = purple_status_type_new_full(PURPLE_STATUS_AVAILABLE,
575 NULL, NULL, FALSE, TRUE, FALSE);
576 types = g_list_append(types, status);
577
578 status = purple_status_type_new_full(PURPLE_STATUS_AWAY,
579 NULL, NULL, FALSE, TRUE, FALSE);
580 types = g_list_append(types, status);
581
582 status = purple_status_type_new_full(PURPLE_STATUS_AWAY,
583 "brb", _("Be Right Back"), FALSE, TRUE, FALSE);
584 types = g_list_append(types, status);
585
586 status = purple_status_type_new_full(PURPLE_STATUS_UNAVAILABLE,
587 "busy", _("Busy"), FALSE, TRUE, FALSE);
588 types = g_list_append(types, status);
589
590 status = purple_status_type_new_full(PURPLE_STATUS_UNAVAILABLE,
591 "phone", _("On the Phone"), FALSE, TRUE, FALSE);
592 types = g_list_append(types, status);
593
594 status = purple_status_type_new_full(PURPLE_STATUS_AWAY,
595 "lunch", _("Out to Lunch"), FALSE, TRUE, FALSE);
596 types = g_list_append(types, status);
597
598 status = purple_status_type_new_full(PURPLE_STATUS_INVISIBLE,
599 NULL, NULL, FALSE, TRUE, FALSE);
600 types = g_list_append(types, status);
601
602 status = purple_status_type_new_full(PURPLE_STATUS_OFFLINE,
603 NULL, NULL, FALSE, TRUE, FALSE);
604 types = g_list_append(types, status);
605
606 status = purple_status_type_new_full(PURPLE_STATUS_MOBILE,
607 "mobile", NULL, FALSE, FALSE, TRUE);
608 types = g_list_append(types, status);
609
610 return types;
611 }
612
613 static GList *
614 msn_actions(PurplePlugin *plugin, gpointer context)
615 {
616 PurpleConnection *gc = (PurpleConnection *)context;
617 PurpleAccount *account;
618 const char *user;
619
620 GList *m = NULL;
621 PurplePluginAction *act;
622
623 act = purple_plugin_action_new(_("Set Friendly Name..."),
624 msn_show_set_friendly_name);
625 m = g_list_append(m, act);
626 m = g_list_append(m, NULL);
627
628 act = purple_plugin_action_new(_("Set Home Phone Number..."),
629 msn_show_set_home_phone);
630 m = g_list_append(m, act);
631
632 act = purple_plugin_action_new(_("Set Work Phone Number..."),
633 msn_show_set_work_phone);
634 m = g_list_append(m, act);
635
636 act = purple_plugin_action_new(_("Set Mobile Phone Number..."),
637 msn_show_set_mobile_phone);
638 m = g_list_append(m, act);
639 m = g_list_append(m, NULL);
640
641 #if 0
642 act = purple_plugin_action_new(_("Enable/Disable Mobile Devices..."),
643 msn_show_set_mobile_support);
644 m = g_list_append(m, act);
645 #endif
646
647 act = purple_plugin_action_new(_("Allow/Disallow Mobile Pages..."),
648 msn_show_set_mobile_pages);
649 m = g_list_append(m, act);
650
651 account = purple_connection_get_account(gc);
652 user = msn_normalize(account, purple_account_get_username(account));
653
654 if ((strstr(user, "@hotmail.") != NULL) ||
655 (strstr(user, "@msn.com") != NULL))
656 {
657 m = g_list_append(m, NULL);
658 act = purple_plugin_action_new(_("Open Hotmail Inbox"),
659 msn_show_hotmail_inbox);
660 m = g_list_append(m, act);
661 }
662
663 return m;
664 }
665
666 static GList *
667 msn_buddy_menu(PurpleBuddy *buddy)
668 {
669 MsnUser *user;
670
671 GList *m = NULL;
672 PurpleMenuAction *act;
673
674 g_return_val_if_fail(buddy != NULL, NULL);
675
676 user = buddy->proto_data;
677
678 if (user != NULL)
679 {
680 if (user->mobile)
681 {
682 act = purple_menu_action_new(_("Send to Mobile"),
683 PURPLE_CALLBACK(show_send_to_mobile_cb),
684 NULL, NULL);
685 m = g_list_append(m, act);
686 }
687 }
688
689 if (g_ascii_strcasecmp(buddy->name,
690 purple_account_get_username(buddy->account)))
691 {
692 act = purple_menu_action_new(_("Initiate _Chat"),
693 PURPLE_CALLBACK(initiate_chat_cb),
694 NULL, NULL);
695 m = g_list_append(m, act);
696 }
697
698 return m;
699 }
700
701 static GList *
702 msn_blist_node_menu(PurpleBlistNode *node)
703 {
704 if(PURPLE_BLIST_NODE_IS_BUDDY(node))
705 {
706 return msn_buddy_menu((PurpleBuddy *) node);
707 }
708 else
709 {
710 return NULL;
711 }
712 }
713
714 static void
715 msn_login(PurpleAccount *account)
716 {
717 PurpleConnection *gc;
718 MsnSession *session;
719 const char *username;
720 const char *host;
721 gboolean http_method = FALSE;
722 int port;
723
724 gc = purple_account_get_connection(account);
725
726 if (!purple_ssl_is_supported())
727 {
728 gc->wants_to_die = TRUE;
729 purple_connection_error(gc,
730 _("SSL support is needed for MSN. Please install a supported "
731 "SSL library."));
732 return;
733 }
734
735 http_method = purple_account_get_bool(account, "http_method", FALSE);
736
737 if (http_method)
738 host = purple_account_get_string(account, "http_method_server", MSN_HTTPCONN_SERVER);
739 else
740 host = purple_account_get_string(account, "server", MSN_SERVER);
741 port = purple_account_get_int(account, "port", MSN_PORT);
742
743 session = msn_session_new(account);
744
745 gc->proto_data = session;
746 gc->flags |= PURPLE_CONNECTION_HTML | PURPLE_CONNECTION_FORMATTING_WBFO | PURPLE_CONNECTION_NO_BGCOLOR | PURPLE_CONNECTION_NO_FONTSIZE | PURPLE_CONNECTION_NO_URLDESC;
747
748 msn_session_set_login_step(session, MSN_LOGIN_STEP_START);
749
750 /* Hmm, I don't like this. */
751 /* XXX shx: Me neither */
752 username = msn_normalize(account, purple_account_get_username(account));
753
754 if (strcmp(username, purple_account_get_username(account)))
755 purple_account_set_username(account, username);
756
757 if (!msn_session_connect(session, host, port, http_method))
758 purple_connection_error(gc, _("Failed to connect to server."));
759 }
760
761 static void
762 msn_close(PurpleConnection *gc)
763 {
764 MsnSession *session;
765
766 session = gc->proto_data;
767
768 g_return_if_fail(session != NULL);
769
770 msn_session_destroy(session);
771
772 gc->proto_data = NULL;
773 }
774
775 static gboolean
776 msn_send_me_im(gpointer data)
777 {
778 MsnIMData *imdata = data;
779 serv_got_im(imdata->gc, imdata->who, imdata->msg, imdata->flags, imdata->when);
780 g_free(imdata->msg);
781 g_free(imdata);
782 return FALSE;
783 }
784
785 static int
786 msn_send_im(PurpleConnection *gc, const char *who, const char *message,
787 PurpleMessageFlags flags)
788 {
789 PurpleAccount *account;
790 PurpleBuddy *buddy = purple_find_buddy(gc->account, who);
791 MsnMessage *msg;
792 char *msgformat;
793 char *msgtext;
794
795 account = purple_connection_get_account(gc);
796
797 if (buddy) {
798 PurplePresence *p = purple_buddy_get_presence(buddy);
799 if (purple_presence_is_status_primitive_active(p, PURPLE_STATUS_MOBILE)) {
800 char *text = purple_markup_strip_html(message);
801 send_to_mobile(gc, who, text);
802 g_free(text);
803 return 1;
804 }
805 }
806
807 msn_import_html(message, &msgformat, &msgtext);
808
809 if (strlen(msgtext) + strlen(msgformat) + strlen(DISPLAY_VERSION) > 1564)
810 {
811 g_free(msgformat);
812 g_free(msgtext);
813
814 return -E2BIG;
815 }
816
817 msg = msn_message_new_plain(msgtext);
818 msn_message_set_attr(msg, "X-MMS-IM-Format", msgformat);
819
820 g_free(msgformat);
821 g_free(msgtext);
822
823 if (g_ascii_strcasecmp(who, purple_account_get_username(account)))
824 {
825 MsnSession *session;
826 MsnSwitchBoard *swboard;
827
828 session = gc->proto_data;
829 swboard = msn_session_get_swboard(session, who, MSN_SB_FLAG_IM);
830
831 msn_switchboard_send_msg(swboard, msg, TRUE);
832 }
833 else
834 {
835 char *body_str, *body_enc, *pre, *post;
836 const char *format;
837 MsnIMData *imdata = g_new0(MsnIMData, 1);
838 /*
839 * In MSN, you can't send messages to yourself, so
840 * we'll fake like we received it ;)
841 */
842 body_str = msn_message_to_string(msg);
843 body_enc = g_markup_escape_text(body_str, -1);
844 g_free(body_str);
845
846 format = msn_message_get_attr(msg, "X-MMS-IM-Format");
847 msn_parse_format(format, &pre, &post);
848 body_str = g_strdup_printf("%s%s%s", pre ? pre : "",
849 body_enc ? body_enc : "", post ? post : "");
850 g_free(body_enc);
851 g_free(pre);
852 g_free(post);
853
854 serv_got_typing_stopped(gc, who);
855 imdata->gc = gc;
856 imdata->who = who;
857 imdata->msg = body_str;
858 imdata->flags = flags;
859 imdata->when = time(NULL);
860 g_idle_add(msn_send_me_im, imdata);
861 }
862
863 msn_message_destroy(msg);
864
865 return 1;
866 }
867
868 static unsigned int
869 msn_send_typing(PurpleConnection *gc, const char *who, PurpleTypingState state)
870 {
871 PurpleAccount *account;
872 MsnSession *session;
873 MsnSwitchBoard *swboard;
874 MsnMessage *msg;
875
876 account = purple_connection_get_account(gc);
877 session = gc->proto_data;
878
879 /*
880 * TODO: I feel like this should be "if (state != PURPLE_TYPING)"
881 * but this is how it was before, and I don't want to break
882 * anything. --KingAnt
883 */
884 if (state == PURPLE_NOT_TYPING)
885 return 0;
886
887 if (!g_ascii_strcasecmp(who, purple_account_get_username(account)))
888 {
889 /* We'll just fake it, since we're sending to ourself. */
890 serv_got_typing(gc, who, MSN_TYPING_RECV_TIMEOUT, PURPLE_TYPING);
891
892 return MSN_TYPING_SEND_TIMEOUT;
893 }
894
895 swboard = msn_session_find_swboard(session, who);
896
897 if (swboard == NULL || !msn_switchboard_can_send(swboard))
898 return 0;
899
900 swboard->flag |= MSN_SB_FLAG_IM;
901
902 msg = msn_message_new(MSN_MSG_TYPING);
903 msn_message_set_content_type(msg, "text/x-msmsgscontrol");
904 msn_message_set_flag(msg, 'U');
905 msn_message_set_attr(msg, "TypingUser",
906 purple_account_get_username(account));
907 msn_message_set_bin_data(msg, "\r\n", 2);
908
909 msn_switchboard_send_msg(swboard, msg, FALSE);
910
911 msn_message_destroy(msg);
912
913 return MSN_TYPING_SEND_TIMEOUT;
914 }
915
916 static void
917 msn_set_status(PurpleAccount *account, PurpleStatus *status)
918 {
919 PurpleConnection *gc;
920 MsnSession *session;
921
922 gc = purple_account_get_connection(account);
923
924 if (gc != NULL)
925 {
926 session = gc->proto_data;
927 msn_change_status(session);
928 }
929 }
930
931 static void
932 msn_set_idle(PurpleConnection *gc, int idle)
933 {
934 MsnSession *session;
935
936 session = gc->proto_data;
937
938 msn_change_status(session);
939 }
940
941 #if 0
942 static void
943 fake_userlist_add_buddy(MsnUserList *userlist,
944 const char *who, int list_id,
945 const char *group_name)
946 {
947 MsnUser *user;
948 static int group_id_c = 1;
949 int group_id;
950
951 group_id = -1;
952
953 if (group_name != NULL)
954 {
955 MsnGroup *group;
956 group = msn_group_new(userlist, group_id_c, group_name);
957 group_id = group_id_c++;
958 }
959
960 user = msn_userlist_find_user(userlist, who);
961
962 if (user == NULL)
963 {
964 user = msn_user_new(userlist, who, NULL);
965 msn_userlist_add_user(userlist, user);
966 }
967 else
968 if (user->list_op & (1 << list_id))
969 {
970 if (list_id == MSN_LIST_FL)
971 {
972 if (group_id >= 0)
973 if (g_list_find(user->group_ids,
974 GINT_TO_POINTER(group_id)))
975 return;
976 }
977 else
978 return;
979 }
980
981 if (group_id >= 0)
982 {
983 user->group_ids = g_list_append(user->group_ids,
984 GINT_TO_POINTER(group_id));
985 }
986
987 user->list_op |= (1 << list_id);
988 }
989 #endif
990
991 static void
992 msn_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group)
993 {
994 MsnSession *session;
995 MsnUserList *userlist;
996 const char *who;
997
998 session = gc->proto_data;
999 userlist = session->userlist;
1000 who = msn_normalize(gc->account, buddy->name);
1001
1002 if (!session->logged_in)
1003 {
1004 #if 0
1005 fake_userlist_add_buddy(session->sync_userlist, who, MSN_LIST_FL,
1006 group ? group->name : NULL);
1007 #else
1008 purple_debug_error("msn", "msn_add_buddy called before connected\n");
1009 #endif
1010
1011 return;
1012 }
1013
1014 #if 0
1015 if (group != NULL && group->name != NULL)
1016 purple_debug_info("msn", "msn_add_buddy: %s, %s\n", who, group->name);
1017 else
1018 purple_debug_info("msn", "msn_add_buddy: %s\n", who);
1019 #endif
1020
1021 #if 0
1022 /* Which is the max? */
1023 if (session->fl_users_count >= 150)
1024 {
1025 purple_debug_info("msn", "Too many buddies\n");
1026 /* Buddy list full */
1027 /* TODO: purple should be notified of this */
1028 return;
1029 }
1030 #endif
1031
1032 /* XXX - Would group ever be NULL here? I don't think so...
1033 * shx: Yes it should; MSN handles non-grouped buddies, and this is only
1034 * internal. */
1035 msn_userlist_add_buddy(userlist, who, MSN_LIST_FL,
1036 group ? group->name : NULL);
1037 }
1038
1039 static void
1040 msn_rem_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group)
1041 {
1042 MsnSession *session;
1043 MsnUserList *userlist;
1044
1045 session = gc->proto_data;
1046 userlist = session->userlist;
1047
1048 if (!session->logged_in)
1049 return;
1050
1051 /* XXX - Does buddy->name need to be msn_normalize'd here? --KingAnt */
1052 msn_userlist_rem_buddy(userlist, buddy->name, MSN_LIST_FL, group->name);
1053 }
1054
1055 static void
1056 msn_add_permit(PurpleConnection *gc, const char *who)
1057 {
1058 MsnSession *session;
1059 MsnUserList *userlist;
1060 MsnUser *user;
1061
1062 session = gc->proto_data;
1063 userlist = session->userlist;
1064 user = msn_userlist_find_user(userlist, who);
1065
1066 if (!session->logged_in)
1067 return;
1068
1069 if (user != NULL && user->list_op & MSN_LIST_BL_OP)
1070 msn_userlist_rem_buddy(userlist, who, MSN_LIST_BL, NULL);
1071
1072 msn_userlist_add_buddy(userlist, who, MSN_LIST_AL, NULL);
1073 }
1074
1075 static void
1076 msn_add_deny(PurpleConnection *gc, const char *who)
1077 {
1078 MsnSession *session;
1079 MsnUserList *userlist;
1080 MsnUser *user;
1081
1082 session = gc->proto_data;
1083 userlist = session->userlist;
1084 user = msn_userlist_find_user(userlist, who);
1085
1086 if (!session->logged_in)
1087 return;
1088
1089 if (user != NULL && user->list_op & MSN_LIST_AL_OP)
1090 msn_userlist_rem_buddy(userlist, who, MSN_LIST_AL, NULL);
1091
1092 msn_userlist_add_buddy(userlist, who, MSN_LIST_BL, NULL);
1093 }
1094
1095 static void
1096 msn_rem_permit(PurpleConnection *gc, const char *who)
1097 {
1098 MsnSession *session;
1099 MsnUserList *userlist;
1100 MsnUser *user;
1101
1102 session = gc->proto_data;
1103 userlist = session->userlist;
1104
1105 if (!session->logged_in)
1106 return;
1107
1108 user = msn_userlist_find_user(userlist, who);
1109
1110 msn_userlist_rem_buddy(userlist, who, MSN_LIST_AL, NULL);
1111
1112 if (user != NULL && user->list_op & MSN_LIST_RL_OP)
1113 msn_userlist_add_buddy(userlist, who, MSN_LIST_BL, NULL);
1114 }
1115
1116 static void
1117 msn_rem_deny(PurpleConnection *gc, const char *who)
1118 {
1119 MsnSession *session;
1120 MsnUserList *userlist;
1121 MsnUser *user;
1122
1123 session = gc->proto_data;
1124 userlist = session->userlist;
1125
1126 if (!session->logged_in)
1127 return;
1128
1129 user = msn_userlist_find_user(userlist, who);
1130
1131 msn_userlist_rem_buddy(userlist, who, MSN_LIST_BL, NULL);
1132
1133 if (user != NULL && user->list_op & MSN_LIST_RL_OP)
1134 msn_userlist_add_buddy(userlist, who, MSN_LIST_AL, NULL);
1135 }
1136
1137 static void
1138 msn_set_permit_deny(PurpleConnection *gc)
1139 {
1140 PurpleAccount *account;
1141 MsnSession *session;
1142 MsnCmdProc *cmdproc;
1143
1144 account = purple_connection_get_account(gc);
1145 session = gc->proto_data;
1146 cmdproc = session->notification->cmdproc;
1147
1148 if (account->perm_deny == PURPLE_PRIVACY_ALLOW_ALL ||
1149 account->perm_deny == PURPLE_PRIVACY_DENY_USERS)
1150 {
1151 msn_cmdproc_send(cmdproc, "BLP", "%s", "AL");
1152 }
1153 else
1154 {
1155 msn_cmdproc_send(cmdproc, "BLP", "%s", "BL");
1156 }
1157 }
1158
1159 static void
1160 msn_chat_invite(PurpleConnection *gc, int id, const char *msg,
1161 const char *who)
1162 {
1163 MsnSession *session;
1164 MsnSwitchBoard *swboard;
1165
1166 session = gc->proto_data;
1167
1168 swboard = msn_session_find_swboard_with_id(session, id);
1169
1170 if (swboard == NULL)
1171 {
1172 /* if we have no switchboard, everyone else left the chat already */
1173 swboard = msn_switchboard_new(session);
1174 msn_switchboard_request(swboard);
1175 swboard->chat_id = id;
1176 swboard->conv = purple_find_chat(gc, id);
1177 }
1178
1179 swboard->flag |= MSN_SB_FLAG_IM;
1180
1181 msn_switchboard_request_add_user(swboard, who);
1182 }
1183
1184 static void
1185 msn_chat_leave(PurpleConnection *gc, int id)
1186 {
1187 MsnSession *session;
1188 MsnSwitchBoard *swboard;
1189 PurpleConversation *conv;
1190
1191 session = gc->proto_data;
1192
1193 swboard = msn_session_find_swboard_with_id(session, id);
1194
1195 /* if swboard is NULL we were the only person left anyway */
1196 if (swboard == NULL)
1197 return;
1198
1199 conv = swboard->conv;
1200
1201 msn_switchboard_release(swboard, MSN_SB_FLAG_IM);
1202
1203 /* If other switchboards managed to associate themselves with this
1204 * conv, make sure they know it's gone! */
1205 if (conv != NULL)
1206 {
1207 while ((swboard = msn_session_find_swboard_with_conv(session, conv)) != NULL)
1208 swboard->conv = NULL;
1209 }
1210 }
1211
1212 static int
1213 msn_chat_send(PurpleConnection *gc, int id, const char *message, PurpleMessageFlags flags)
1214 {
1215 PurpleAccount *account;
1216 MsnSession *session;
1217 MsnSwitchBoard *swboard;
1218 MsnMessage *msg;
1219 char *msgformat;
1220 char *msgtext;
1221
1222 account = purple_connection_get_account(gc);
1223 session = gc->proto_data;
1224 swboard = msn_session_find_swboard_with_id(session, id);
1225
1226 if (swboard == NULL)
1227 return -EINVAL;
1228
1229 if (!swboard->ready)
1230 return 0;
1231
1232 swboard->flag |= MSN_SB_FLAG_IM;
1233
1234 msn_import_html(message, &msgformat, &msgtext);
1235
1236 if (strlen(msgtext) + strlen(msgformat) + strlen(DISPLAY_VERSION) > 1564)
1237 {
1238 g_free(msgformat);
1239 g_free(msgtext);
1240
1241 return -E2BIG;
1242 }
1243
1244 msg = msn_message_new_plain(msgtext);
1245 msn_message_set_attr(msg, "X-MMS-IM-Format", msgformat);
1246 msn_switchboard_send_msg(swboard, msg, FALSE);
1247 msn_message_destroy(msg);
1248
1249 g_free(msgformat);
1250 g_free(msgtext);
1251
1252 serv_got_chat_in(gc, id, purple_account_get_username(account), 0,
1253 message, time(NULL));
1254
1255 return 0;
1256 }
1257
1258 static void
1259 msn_keepalive(PurpleConnection *gc)
1260 {
1261 MsnSession *session;
1262
1263 session = gc->proto_data;
1264
1265 if (!session->http_method)
1266 {
1267 MsnCmdProc *cmdproc;
1268
1269 cmdproc = session->notification->cmdproc;
1270
1271 msn_cmdproc_send_quick(cmdproc, "PNG", NULL, NULL);
1272 }
1273 }
1274
1275 static void
1276 msn_group_buddy(PurpleConnection *gc, const char *who,
1277 const char *old_group_name, const char *new_group_name)
1278 {
1279 MsnSession *session;
1280 MsnUserList *userlist;
1281
1282 session = gc->proto_data;
1283 userlist = session->userlist;
1284
1285 msn_userlist_move_buddy(userlist, who, old_group_name, new_group_name);
1286 }
1287
1288 static void
1289 msn_rename_group(PurpleConnection *gc, const char *old_name,
1290 PurpleGroup *group, GList *moved_buddies)
1291 {
1292 MsnSession *session;
1293 MsnCmdProc *cmdproc;
1294 int old_gid;
1295 const char *enc_new_group_name;
1296
1297 session = gc->proto_data;
1298 cmdproc = session->notification->cmdproc;
1299 enc_new_group_name = purple_url_encode(group->name);
1300
1301 old_gid = msn_userlist_find_group_id(session->userlist, old_name);
1302
1303 if (old_gid >= 0)
1304 {
1305 msn_cmdproc_send(cmdproc, "REG", "%d %s 0", old_gid,
1306 enc_new_group_name);
1307 }
1308 else
1309 {
1310 msn_cmdproc_send(cmdproc, "ADG", "%s 0", enc_new_group_name);
1311 }
1312 }
1313
1314 static void
1315 msn_convo_closed(PurpleConnection *gc, const char *who)
1316 {
1317 MsnSession *session;
1318 MsnSwitchBoard *swboard;
1319 PurpleConversation *conv;
1320
1321 session = gc->proto_data;
1322
1323 swboard = msn_session_find_swboard(session, who);
1324
1325 /*
1326 * Don't perform an assertion here. If swboard is NULL, then the
1327 * switchboard was either closed by the other party, or the person
1328 * is talking to himself.
1329 */
1330 if (swboard == NULL)
1331 return;
1332
1333 conv = swboard->conv;
1334
1335 /* If we release the switchboard here, it may still have messages
1336 pending ACK which would result in incorrect unsent message errors.
1337 Just let it timeout... This is *so* going to screw with people who
1338 use dumb clients that report "User has closed the conversation window" */
1339 /* msn_switchboard_release(swboard, MSN_SB_FLAG_IM); */
1340 swboard->conv = NULL;
1341
1342 /* If other switchboards managed to associate themselves with this
1343 * conv, make sure they know it's gone! */
1344 if (conv != NULL)
1345 {
1346 while ((swboard = msn_session_find_swboard_with_conv(session, conv)) != NULL)
1347 swboard->conv = NULL;
1348 }
1349 }
1350
1351 static void
1352 msn_set_buddy_icon(PurpleConnection *gc, PurpleStoredImage *img)
1353 {
1354 MsnSession *session;
1355 MsnUser *user;
1356
1357 session = gc->proto_data;
1358 user = session->user;
1359
1360 msn_user_set_buddy_icon(user, img);
1361
1362 msn_change_status(session);
1363 }
1364
1365 static void
1366 msn_remove_group(PurpleConnection *gc, PurpleGroup *group)
1367 {
1368 MsnSession *session;
1369 MsnCmdProc *cmdproc;
1370 int group_id;
1371
1372 session = gc->proto_data;
1373 cmdproc = session->notification->cmdproc;
1374
1375 if ((group_id = msn_userlist_find_group_id(session->userlist, group->name)) >= 0)
1376 {
1377 msn_cmdproc_send(cmdproc, "RMG", "%d", group_id);
1378 }
1379 }
1380
1381 /**
1382 * Extract info text from info_data and add it to user_info
1383 */
1384 static gboolean
1385 msn_tooltip_extract_info_text(PurpleNotifyUserInfo *user_info, MsnGetInfoData *info_data)
1386 {
1387 PurpleBuddy *b;
1388
1389 b = purple_find_buddy(purple_connection_get_account(info_data->gc),
1390 info_data->name);
1391
1392 if (b)
1393 {
1394 char *tmp;
1395
1396 if (b->alias && b->alias[0])
1397 {
1398 char *aliastext = g_markup_escape_text(b->alias, -1);
1399 purple_notify_user_info_add_pair(user_info, _("Alias"), aliastext);
1400 g_free(aliastext);
1401 }
1402
1403 if (b->server_alias)
1404 {
1405 char *nicktext = g_markup_escape_text(b->server_alias, -1);
1406 tmp = g_strdup_printf("<font sml=\"msn\">%s</font><br>", nicktext);
1407 purple_notify_user_info_add_pair(user_info, _("Nickname"), tmp);
1408 g_free(tmp);
1409 g_free(nicktext);
1410 }
1411
1412 /* Add the tooltip information */
1413 msn_tooltip_text(b, user_info, TRUE);
1414
1415 return TRUE;
1416 }
1417
1418 return FALSE;
1419 }
1420
1421 #if PHOTO_SUPPORT
1422
1423 static char *
1424 msn_get_photo_url(const char *url_text)
1425 {
1426 char *p, *q;
1427
1428 if ((p = strstr(url_text, " contactparams:photopreauthurl=\"")) != NULL)
1429 {
1430 p += strlen(" contactparams:photopreauthurl=\"");
1431 }
1432
1433 if (p && (strncmp(p, "http://", 8) == 0) && ((q = strchr(p, '"')) != NULL))
1434 return g_strndup(p, q - p);
1435
1436 return NULL;
1437 }
1438
1439 static void msn_got_photo(PurpleUtilFetchUrlData *url_data, gpointer data,
1440 const gchar *url_text, size_t len, const gchar *error_message);
1441
1442 #endif
1443
1444 #if 0
1445 static char *msn_info_date_reformat(const char *field, size_t len)
1446 {
1447 char *tmp = g_strndup(field, len);
1448 time_t t = purple_str_to_time(tmp, FALSE, NULL, NULL, NULL);
1449
1450 g_free(tmp);
1451 return g_strdup(purple_date_format_short(localtime(&t)));
1452 }
1453 #endif
1454
1455 #define MSN_GOT_INFO_GET_FIELD(a, b) \
1456 found = purple_markup_extract_info_field(stripped, stripped_len, user_info, \
1457 "\n" a ":", 0, "\n", 0, "Undisclosed", b, 0, NULL, NULL); \
1458 if (found) \
1459 sect_info = TRUE;
1460
1461 #define MSN_GOT_INFO_GET_FIELD_NO_SEARCH(a, b) \
1462 found = purple_markup_extract_info_field(stripped, stripped_len, user_info, \
1463 "\n" a ":", 0, "\n", 0, "Undisclosed", b, 0, NULL, msn_info_strip_search_link); \
1464 if (found) \
1465 sect_info = TRUE;
1466
1467 static char *
1468 msn_info_strip_search_link(const char *field, size_t len)
1469 {
1470 const char *c;
1471 if ((c = strstr(field, " (http://spaces.live.com/default.aspx?page=searchresults")) == NULL &&
1472 (c = strstr(field, " (http://spaces.msn.com/default.aspx?page=searchresults")) == NULL)
1473 return g_strndup(field, len);
1474 return g_strndup(field, c - field);
1475 }
1476
1477 static void
1478 msn_got_info(PurpleUtilFetchUrlData *url_data, gpointer data,
1479 const gchar *url_text, size_t len, const gchar *error_message)
1480 {
1481 MsnGetInfoData *info_data = (MsnGetInfoData *)data;
1482 PurpleNotifyUserInfo *user_info;
1483 char *stripped, *p, *q, *tmp;
1484 char *user_url = NULL;
1485 gboolean found;
1486 gboolean has_tooltip_text = FALSE;
1487 gboolean has_info = FALSE;
1488 gboolean sect_info = FALSE;
1489 gboolean has_contact_info = FALSE;
1490 char *url_buffer;
1491 int stripped_len;
1492 #if PHOTO_SUPPORT
1493 char *photo_url_text = NULL;
1494 MsnGetInfoStepTwoData *info2_data = NULL;
1495 #endif
1496
1497 purple_debug_info("msn", "In msn_got_info\n");
1498
1499 /* Make sure the connection is still valid */
1500 if (g_list_find(purple_connections_get_all(), info_data->gc) == NULL)
1501 {
1502 purple_debug_warning("msn", "invalid connection. ignoring buddy info.\n");
1503 g_free(info_data->name);
1504 g_free(info_data);
1505 return;
1506 }
1507
1508 user_info = purple_notify_user_info_new();
1509 has_tooltip_text = msn_tooltip_extract_info_text(user_info, info_data);
1510
1511 if (error_message != NULL || url_text == NULL || strcmp(url_text, "") == 0)
1512 {
1513 tmp = g_strdup_printf("<b>%s</b>", _("Error retrieving profile"));
1514 purple_notify_user_info_add_pair(user_info, NULL, tmp);
1515 g_free(tmp);
1516
1517 purple_notify_userinfo(info_data->gc, info_data->name, user_info, NULL, NULL);
1518 purple_notify_user_info_destroy(user_info);
1519
1520 g_free(info_data->name);
1521 g_free(info_data);
1522 return;
1523 }
1524
1525 url_buffer = g_strdup(url_text);
1526
1527 /* If they have a homepage link, MSN masks it such that we need to
1528 * fetch the url out before purple_markup_strip_html() nukes it */
1529 /* I don't think this works with the new spaces profiles - Stu 3/2/06 */
1530 if ((p = strstr(url_text,
1531 "Take a look at my </font><A class=viewDesc title=\"")) != NULL)
1532 {
1533 p += 50;
1534
1535 if ((q = strchr(p, '"')) != NULL)
1536 user_url = g_strndup(p, q - p);
1537 }
1538
1539 /*
1540 * purple_markup_strip_html() doesn't strip out character entities like &nbsp;
1541 * and &#183;
1542 */
1543 while ((p = strstr(url_buffer, "&nbsp;")) != NULL)
1544 {
1545 *p = ' '; /* Turn &nbsp;'s into ordinary blanks */
1546 p += 1;
1547 memmove(p, p + 5, strlen(p + 5));
1548 url_buffer[strlen(url_buffer) - 5] = '\0';
1549 }
1550
1551 while ((p = strstr(url_buffer, "&#183;")) != NULL)
1552 {
1553 memmove(p, p + 6, strlen(p + 6));
1554 url_buffer[strlen(url_buffer) - 6] = '\0';
1555 }
1556
1557 /* Nuke the nasty \r's that just get in the way */
1558 purple_str_strip_char(url_buffer, '\r');
1559
1560 /* MSN always puts in &#39; for apostrophes...replace them */
1561 while ((p = strstr(url_buffer, "&#39;")) != NULL)
1562 {
1563 *p = '\'';
1564 memmove(p + 1, p + 5, strlen(p + 5));
1565 url_buffer[strlen(url_buffer) - 4] = '\0';
1566 }
1567
1568 /* Nuke the html, it's easier than trying to parse the horrid stuff */
1569 stripped = purple_markup_strip_html(url_buffer);
1570 stripped_len = strlen(stripped);
1571
1572 purple_debug_misc("msn", "stripped = %p\n", stripped);
1573 purple_debug_misc("msn", "url_buffer = %p\n", url_buffer);
1574
1575 /* General section header */
1576 if (has_tooltip_text)
1577 purple_notify_user_info_add_section_break(user_info);
1578
1579 purple_notify_user_info_add_section_header(user_info, _("General"));
1580
1581 /* Extract their Name and put it in */
1582 MSN_GOT_INFO_GET_FIELD("Name", _("Name"));
1583
1584 /* General */
1585 MSN_GOT_INFO_GET_FIELD("Nickname", _("Nickname"));
1586 MSN_GOT_INFO_GET_FIELD_NO_SEARCH("Age", _("Age"));
1587 MSN_GOT_INFO_GET_FIELD_NO_SEARCH("Gender", _("Gender"));
1588 MSN_GOT_INFO_GET_FIELD_NO_SEARCH("Occupation", _("Occupation"));
1589 MSN_GOT_INFO_GET_FIELD_NO_SEARCH("Location", _("Location"));
1590
1591 /* Extract their Interests and put it in */
1592 found = purple_markup_extract_info_field(stripped, stripped_len, user_info,
1593 "\nInterests\t", 0, " (/default.aspx?page=searchresults", 0,
1594 "Undisclosed", _("Hobbies and Interests") /* _("Interests") */,
1595 0, NULL, NULL);
1596
1597 if (found)
1598 sect_info = TRUE;
1599
1600 MSN_GOT_INFO_GET_FIELD("More about me", _("A Little About Me"));
1601
1602 if (sect_info)
1603 {
1604 has_info = TRUE;
1605 sect_info = FALSE;
1606 }
1607 else
1608 {
1609 /* Remove the section header */
1610 purple_notify_user_info_remove_last_item(user_info);
1611 if (has_tooltip_text)
1612 purple_notify_user_info_remove_last_item(user_info);
1613 }
1614
1615 /* Social */
1616 purple_notify_user_info_add_section_break(user_info);
1617 purple_notify_user_info_add_section_header(user_info, _("Social"));
1618
1619 MSN_GOT_INFO_GET_FIELD("Marital status", _("Marital Status"));
1620 MSN_GOT_INFO_GET_FIELD("Interested in", _("Interests"));
1621 MSN_GOT_INFO_GET_FIELD("Pets", _("Pets"));
1622 MSN_GOT_INFO_GET_FIELD("Hometown", _("Hometown"));
1623 MSN_GOT_INFO_GET_FIELD("Places lived", _("Places Lived"));
1624 MSN_GOT_INFO_GET_FIELD("Fashion", _("Fashion"));
1625 MSN_GOT_INFO_GET_FIELD("Humor", _("Humor"));
1626 MSN_GOT_INFO_GET_FIELD("Music", _("Music"));
1627 MSN_GOT_INFO_GET_FIELD("Favorite quote", _("Favorite Quote"));
1628
1629 if (sect_info)
1630 {
1631 has_info = TRUE;
1632 sect_info = FALSE;
1633 }
1634 else
1635 {
1636 /* Remove the section header */
1637 purple_notify_user_info_remove_last_item(user_info);
1638 purple_notify_user_info_remove_last_item(user_info);
1639 }
1640
1641 /* Contact Info */
1642 /* Personal */
1643 purple_notify_user_info_add_section_break(user_info);
1644 purple_notify_user_info_add_section_header(user_info, _("Contact Info"));
1645 purple_notify_user_info_add_section_header(user_info, _("Personal"));
1646
1647 MSN_GOT_INFO_GET_FIELD("Name", _("Name"));
1648 MSN_GOT_INFO_GET_FIELD("Significant other", _("Significant Other"));
1649 MSN_GOT_INFO_GET_FIELD("Home phone", _("Home Phone"));
1650 MSN_GOT_INFO_GET_FIELD("Home phone 2", _("Home Phone 2"));
1651 MSN_GOT_INFO_GET_FIELD("Home address", _("Home Address"));
1652 MSN_GOT_INFO_GET_FIELD("Personal Mobile", _("Personal Mobile"));
1653 MSN_GOT_INFO_GET_FIELD("Home fax", _("Home Fax"));
1654 MSN_GOT_INFO_GET_FIELD("Personal e-mail", _("Personal E-Mail"));
1655 MSN_GOT_INFO_GET_FIELD("Personal IM", _("Personal IM"));
1656 MSN_GOT_INFO_GET_FIELD("Birthday", _("Birthday"));
1657 MSN_GOT_INFO_GET_FIELD("Anniversary", _("Anniversary"));
1658 MSN_GOT_INFO_GET_FIELD("Notes", _("Notes"));
1659
1660 if (sect_info)
1661 {
1662 has_info = TRUE;
1663 sect_info = FALSE;
1664 has_contact_info = TRUE;
1665 }
1666 else
1667 {
1668 /* Remove the section header */
1669 purple_notify_user_info_remove_last_item(user_info);
1670 }
1671
1672 /* Business */
1673 purple_notify_user_info_add_section_header(user_info, _("Work"));
1674 MSN_GOT_INFO_GET_FIELD("Name", _("Name"));
1675 MSN_GOT_INFO_GET_FIELD("Job title", _("Job Title"));
1676 MSN_GOT_INFO_GET_FIELD("Company", _("Company"));
1677 MSN_GOT_INFO_GET_FIELD("Department", _("Department"));
1678 MSN_GOT_INFO_GET_FIELD("Profession", _("Profession"));
1679 MSN_GOT_INFO_GET_FIELD("Work phone 1", _("Work Phone"));
1680 MSN_GOT_INFO_GET_FIELD("Work phone 2", _("Work Phone 2"));
1681 MSN_GOT_INFO_GET_FIELD("Work address", _("Work Address"));
1682 MSN_GOT_INFO_GET_FIELD("Work mobile", _("Work Mobile"));
1683 MSN_GOT_INFO_GET_FIELD("Work pager", _("Work Pager"));
1684 MSN_GOT_INFO_GET_FIELD("Work fax", _("Work Fax"));
1685 MSN_GOT_INFO_GET_FIELD("Work e-mail", _("Work E-Mail"));
1686 MSN_GOT_INFO_GET_FIELD("Work IM", _("Work IM"));
1687 MSN_GOT_INFO_GET_FIELD("Start date", _("Start Date"));
1688 MSN_GOT_INFO_GET_FIELD("Notes", _("Notes"));
1689
1690 if (sect_info)
1691 {
1692 has_info = TRUE;
1693 sect_info = FALSE;
1694 has_contact_info = TRUE;
1695 }
1696 else
1697 {
1698 /* Remove the section header */
1699 purple_notify_user_info_remove_last_item(user_info);
1700 }
1701
1702 if (!has_contact_info)
1703 {
1704 /* Remove the Contact Info section header */
1705 purple_notify_user_info_remove_last_item(user_info);
1706 }
1707
1708 #if 0 /* these probably don't show up any more */
1709 /*
1710 * The fields, 'A Little About Me', 'Favorite Things', 'Hobbies
1711 * and Interests', 'Favorite Quote', and 'My Homepage' may or may
1712 * not appear, in any combination. However, they do appear in
1713 * certain order, so we can successively search to pin down the
1714 * distinct values.
1715 */
1716
1717 /* Check if they have A Little About Me */
1718 found = purple_markup_extract_info_field(stripped, stripped_len, s,
1719 " A Little About Me \n\n", 0, "Favorite Things", '\n', NULL,
1720 _("A Little About Me"), 0, NULL, NULL);
1721
1722 if (!found)
1723 {
1724 found = purple_markup_extract_info_field(stripped, stripped_len, s,
1725 " A Little About Me \n\n", 0, "Hobbies and Interests", '\n',
1726 NULL, _("A Little About Me"), 0, NULL, NULL);
1727 }
1728
1729 if (!found)
1730 {
1731 found = purple_markup_extract_info_field(stripped, stripped_len, s,
1732 " A Little About Me \n\n", 0, "Favorite Quote", '\n', NULL,
1733 _("A Little About Me"), 0, NULL, NULL);
1734 }
1735
1736 if (!found)
1737 {
1738 found = purple_markup_extract_info_field(stripped, stripped_len, s,
1739 " A Little About Me \n\n", 0, "My Homepage \n\nTake a look",
1740 '\n',
1741 NULL, _("A Little About Me"), 0, NULL, NULL);
1742 }
1743
1744 if (!found)
1745 {
1746 purple_markup_extract_info_field(stripped, stripped_len, s,
1747 " A Little About Me \n\n", 0, "last updated", '\n', NULL,
1748 _("A Little About Me"), 0, NULL, NULL);
1749 }
1750
1751 if (found)
1752 has_info = TRUE;
1753
1754 /* Check if they have Favorite Things */
1755 found = purple_markup_extract_info_field(stripped, stripped_len, s,
1756 " Favorite Things \n\n", 0, "Hobbies and Interests", '\n', NULL,
1757 _("Favorite Things"), 0, NULL, NULL);
1758
1759 if (!found)
1760 {
1761 found = purple_markup_extract_info_field(stripped, stripped_len, s,
1762 " Favorite Things \n\n", 0, "Favorite Quote", '\n', NULL,
1763 _("Favorite Things"), 0, NULL, NULL);
1764 }
1765
1766 if (!found)
1767 {
1768 found = purple_markup_extract_info_field(stripped, stripped_len, s,
1769 " Favorite Things \n\n", 0, "My Homepage \n\nTake a look", '\n',
1770 NULL, _("Favorite Things"), 0, NULL, NULL);
1771 }
1772
1773 if (!found)
1774 {
1775 purple_markup_extract_info_field(stripped, stripped_len, s,
1776 " Favorite Things \n\n", 0, "last updated", '\n', NULL,
1777 _("Favorite Things"), 0, NULL, NULL);
1778 }
1779
1780 if (found)
1781 has_info = TRUE;
1782
1783 /* Check if they have Hobbies and Interests */
1784 found = purple_markup_extract_info_field(stripped, stripped_len, s,
1785 " Hobbies and Interests \n\n", 0, "Favorite Quote", '\n', NULL,
1786 _("Hobbies and Interests"), 0, NULL, NULL);
1787
1788 if (!found)
1789 {
1790 found = purple_markup_extract_info_field(stripped, stripped_len, s,
1791 " Hobbies and Interests \n\n", 0, "My Homepage \n\nTake a look",
1792 '\n', NULL, _("Hobbies and Interests"), 0, NULL, NULL);
1793 }
1794
1795 if (!found)
1796 {
1797 purple_markup_extract_info_field(stripped, stripped_len, s,
1798 " Hobbies and Interests \n\n", 0, "last updated", '\n', NULL,
1799 _("Hobbies and Interests"), 0, NULL, NULL);
1800 }
1801
1802 if (found)
1803 has_info = TRUE;
1804
1805 /* Check if they have Favorite Quote */
1806 found = purple_markup_extract_info_field(stripped, stripped_len, s,
1807 "Favorite Quote \n\n", 0, "My Homepage \n\nTake a look", '\n', NULL,
1808 _("Favorite Quote"), 0, NULL, NULL);
1809
1810 if (!found)
1811 {
1812 purple_markup_extract_info_field(stripped, stripped_len, s,
1813 "Favorite Quote \n\n", 0, "last updated", '\n', NULL,
1814 _("Favorite Quote"), 0, NULL, NULL);
1815 }
1816
1817 if (found)
1818 has_info = TRUE;
1819
1820 /* Extract the last updated date and put it in */
1821 found = purple_markup_extract_info_field(stripped, stripped_len, s,
1822 " last updated:", 1, "\n", 0, NULL, _("Last Updated"), 0,
1823 NULL, msn_info_date_reformat);
1824
1825 if (found)
1826 has_info = TRUE;
1827 #endif
1828
1829 /* If we were able to fetch a homepage url earlier, stick it in there */
1830 if (user_url != NULL)
1831 {
1832 tmp = g_strdup_printf("<a href=\"%s\">%s</a>", user_url, user_url);
1833 purple_notify_user_info_add_pair(user_info, _("Homepage"), tmp);
1834 g_free(tmp);
1835 g_free(user_url);
1836
1837 has_info = TRUE;
1838 }
1839
1840 if (!has_info)
1841 {
1842 /* MSN doesn't actually distinguish between "unknown member" and
1843 * a known member with an empty profile. Try to explain this fact.
1844 * Note that if we have a nonempty tooltip_text, we know the user
1845 * exists.
1846 */
1847 /* This doesn't work with the new spaces profiles - Stu 3/2/06
1848 char *p = strstr(url_buffer, "Unknown Member </TITLE>");
1849 * This might not work for long either ... */
1850 /* Nope, it failed some time before 5/2/07 :(
1851 char *p = strstr(url_buffer, "form id=\"SpacesSearch\" name=\"SpacesSearch\"");
1852 * Let's see how long this one holds out for ... */
1853 char *p = strstr(url_buffer, "<form id=\"profile_form\" name=\"profile_form\" action=\"http&#58;&#47;&#47;spaces.live.com&#47;profile.aspx&#63;cid&#61;0\"");
1854 PurpleBuddy *b = purple_find_buddy
1855 (purple_connection_get_account(info_data->gc), info_data->name);
1856 purple_notify_user_info_add_pair(user_info, _("Error retrieving profile"),
1857 ((p && b) ? _("The user has not created a public profile.") :
1858 (p ? _("MSN reported not being able to find the user's profile. "
1859 "This either means that the user does not exist, "
1860 "or that the user exists "
1861 "but has not created a public profile.") :
1862 _("Could not find " /* This should never happen */
1863 "any information in the user's profile. "
1864 "The user most likely does not exist."))));
1865 }
1866
1867 /* put a link to the actual profile URL */
1868 tmp = g_strdup_printf("<a href=\"%s%s\">%s%s</a>",
1869 PROFILE_URL, info_data->name, PROFILE_URL, info_data->name);
1870 purple_notify_user_info_add_pair(user_info, _("Profile URL"), tmp);
1871 g_free(tmp);
1872
1873 #if PHOTO_SUPPORT
1874 /* Find the URL to the photo; must be before the marshalling [Bug 994207] */
1875 photo_url_text = msn_get_photo_url(url_text);
1876
1877 /* Marshall the existing state */
1878 info2_data = g_malloc0(sizeof(MsnGetInfoStepTwoData));
1879 info2_data->info_data = info_data;
1880 info2_data->stripped = stripped;
1881 info2_data->url_buffer = url_buffer;
1882 info2_data->user_info = user_info;
1883 info2_data->photo_url_text = photo_url_text;
1884
1885 /* Try to put the photo in there too, if there's one */
1886 if (photo_url_text)
1887 {
1888 purple_util_fetch_url(photo_url_text, FALSE, NULL, FALSE, msn_got_photo,
1889 info2_data);
1890 }
1891 else
1892 {
1893 /* Emulate a callback */
1894 /* TODO: Huh? */
1895 msn_got_photo(NULL, info2_data, NULL, 0, NULL);
1896 }
1897 }
1898
1899 static void
1900 msn_got_photo(PurpleUtilFetchUrlData *url_data, gpointer user_data,
1901 const gchar *url_text, size_t len, const gchar *error_message)
1902 {
1903 MsnGetInfoStepTwoData *info2_data = (MsnGetInfoStepTwoData *)user_data;
1904 int id = -1;
1905
1906 /* Unmarshall the saved state */
1907 MsnGetInfoData *info_data = info2_data->info_data;
1908 char *stripped = info2_data->stripped;
1909 char *url_buffer = info2_data->url_buffer;
1910 PurpleNotifyUserInfo *user_info = info2_data->user_info;
1911 char *photo_url_text = info2_data->photo_url_text;
1912
1913 /* Make sure the connection is still valid if we got here by fetching a photo url */
1914 if (url_text && (error_message != NULL ||
1915 g_list_find(purple_connections_get_all(), info_data->gc) == NULL))
1916 {
1917 purple_debug_warning("msn", "invalid connection. ignoring buddy photo info.\n");
1918 g_free(stripped);
1919 g_free(url_buffer);
1920 g_free(user_info);
1921 g_free(info_data->name);
1922 g_free(info_data);
1923 g_free(photo_url_text);
1924 g_free(info2_data);
1925
1926 return;
1927 }
1928
1929 /* Try to put the photo in there too, if there's one and is readable */
1930 if (user_data && url_text && len != 0)
1931 {
1932 if (strstr(url_text, "400 Bad Request")
1933 || strstr(url_text, "403 Forbidden")
1934 || strstr(url_text, "404 Not Found"))
1935 {
1936
1937 purple_debug_info("msn", "Error getting %s: %s\n",
1938 photo_url_text, url_text);
1939 }
1940 else
1941 {
1942 char buf[1024];
1943 purple_debug_info("msn", "%s is %d bytes\n", photo_url_text, len);
1944 id = purple_imgstore_add_with_id(g_memdup(url_text, len), len, NULL);
1945 g_snprintf(buf, sizeof(buf), "<img id=\"%d\"><br>", id);
1946 purple_notify_user_info_prepend_pair(user_info, NULL, buf);
1947 }
1948 }
1949
1950 /* We continue here from msn_got_info, as if nothing has happened */
1951 #endif
1952 purple_notify_userinfo(info_data->gc, info_data->name, user_info, NULL, NULL);
1953
1954 g_free(stripped);
1955 g_free(url_buffer);
1956 purple_notify_user_info_destroy(user_info);
1957 g_free(info_data->name);
1958 g_free(info_data);
1959 #if PHOTO_SUPPORT
1960 g_free(photo_url_text);
1961 g_free(info2_data);
1962 if (id != -1)
1963 purple_imgstore_unref_by_id(id);
1964 #endif
1965 }
1966
1967 static void
1968 msn_get_info(PurpleConnection *gc, const char *name)
1969 {
1970 MsnGetInfoData *data;
1971 char *url;
1972
1973 data = g_new0(MsnGetInfoData, 1);
1974 data->gc = gc;
1975 data->name = g_strdup(name);
1976
1977 url = g_strdup_printf("%s%s", PROFILE_URL, name);
1978
1979 purple_util_fetch_url(url, FALSE,
1980 "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)",
1981 TRUE, msn_got_info, data);
1982
1983 g_free(url);
1984 }
1985
1986 static gboolean msn_load(PurplePlugin *plugin)
1987 {
1988 msn_notification_init();
1989 msn_switchboard_init();
1990 msn_sync_init();
1991
1992 return TRUE;
1993 }
1994
1995 static gboolean msn_unload(PurplePlugin *plugin)
1996 {
1997 msn_notification_end();
1998 msn_switchboard_end();
1999 msn_sync_end();
2000
2001 return TRUE;
2002 }
2003
2004 static PurpleAccount *find_acct(const char *prpl, const char *acct_id)
2005 {
2006 PurpleAccount *acct = NULL;
2007
2008 /* If we have a specific acct, use it */
2009 if (acct_id) {
2010 acct = purple_accounts_find(acct_id, prpl);
2011 if (acct && !purple_account_is_connected(acct))
2012 acct = NULL;
2013 } else { /* Otherwise find an active account for the protocol */
2014 GList *l = purple_accounts_get_all();
2015 while (l) {
2016 if (!strcmp(prpl, purple_account_get_protocol_id(l->data))
2017 && purple_account_is_connected(l->data)) {
2018 acct = l->data;
2019 break;
2020 }
2021 l = l->next;
2022 }
2023 }
2024
2025 return acct;
2026 }
2027
2028 static gboolean msn_uri_handler(const char *proto, const char *cmd, GHashTable *params)
2029 {
2030 char *acct_id = g_hash_table_lookup(params, "account");
2031 PurpleAccount *acct;
2032
2033 if (g_ascii_strcasecmp(proto, "msnim"))
2034 return FALSE;
2035
2036 acct = find_acct("prpl-msn", acct_id);
2037
2038 if (!acct)
2039 return FALSE;
2040
2041 /* msnim:chat?contact=user@domain.tld */
2042 if (!g_ascii_strcasecmp(cmd, "Chat")) {
2043 char *sname = g_hash_table_lookup(params, "contact");
2044 if (sname) {
2045 PurpleConversation *conv = purple_find_conversation_with_account(
2046 PURPLE_CONV_TYPE_IM, sname, acct);
2047 if (conv == NULL)
2048 conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, acct, sname);
2049 purple_conversation_present(conv);
2050 }
2051 /*else
2052 **If pidgindialogs_im() was in the core, we could use it here.
2053 * It is all purple_request_* based, but I'm not sure it really belongs in the core
2054 pidgindialogs_im();*/
2055
2056 return TRUE;
2057 }
2058 /* msnim:add?contact=user@domain.tld */
2059 else if (!g_ascii_strcasecmp(cmd, "Add")) {
2060 char *name = g_hash_table_lookup(params, "contact");
2061 purple_blist_request_add_buddy(acct, name, NULL, NULL);
2062 return TRUE;
2063 }
2064
2065 return FALSE;
2066 }
2067
2068
2069 static PurplePluginProtocolInfo prpl_info =
2070 {
2071 OPT_PROTO_MAIL_CHECK,
2072 NULL, /* user_splits */
2073 NULL, /* protocol_options */
2074 {"png", 0, 0, 96, 96, 0, PURPLE_ICON_SCALE_SEND}, /* icon_spec */
2075 msn_list_icon, /* list_icon */
2076 NULL, /* list_emblems */
2077 msn_status_text, /* status_text */
2078 msn_tooltip_text, /* tooltip_text */
2079 msn_status_types, /* away_states */
2080 msn_blist_node_menu, /* blist_node_menu */
2081 NULL, /* chat_info */
2082 NULL, /* chat_info_defaults */
2083 msn_login, /* login */
2084 msn_close, /* close */
2085 msn_send_im, /* send_im */
2086 NULL, /* set_info */
2087 msn_send_typing, /* send_typing */
2088 msn_get_info, /* get_info */
2089 msn_set_status, /* set_away */
2090 msn_set_idle, /* set_idle */
2091 NULL, /* change_passwd */
2092 msn_add_buddy, /* add_buddy */
2093 NULL, /* add_buddies */
2094 msn_rem_buddy, /* remove_buddy */
2095 NULL, /* remove_buddies */
2096 msn_add_permit, /* add_permit */
2097 msn_add_deny, /* add_deny */
2098 msn_rem_permit, /* rem_permit */
2099 msn_rem_deny, /* rem_deny */
2100 msn_set_permit_deny, /* set_permit_deny */
2101 NULL, /* join_chat */
2102 NULL, /* reject chat invite */
2103 NULL, /* get_chat_name */
2104 msn_chat_invite, /* chat_invite */
2105 msn_chat_leave, /* chat_leave */
2106 NULL, /* chat_whisper */
2107 msn_chat_send, /* chat_send */
2108 msn_keepalive, /* keepalive */
2109 NULL, /* register_user */
2110 NULL, /* get_cb_info */
2111 NULL, /* get_cb_away */
2112 NULL, /* alias_buddy */
2113 msn_group_buddy, /* group_buddy */
2114 msn_rename_group, /* rename_group */
2115 NULL, /* buddy_free */
2116 msn_convo_closed, /* convo_closed */
2117 msn_normalize, /* normalize */
2118 msn_set_buddy_icon, /* set_buddy_icon */
2119 msn_remove_group, /* remove_group */
2120 NULL, /* get_cb_real_name */
2121 NULL, /* set_chat_topic */
2122 NULL, /* find_blist_chat */
2123 NULL, /* roomlist_get_list */
2124 NULL, /* roomlist_cancel */
2125 NULL, /* roomlist_expand_category */
2126 msn_can_receive_file, /* can_receive_file */
2127 msn_send_file, /* send_file */
2128 msn_new_xfer, /* new_xfer */
2129 msn_offline_message, /* offline_message */
2130 NULL, /* whiteboard_prpl_ops */
2131 NULL, /* send_raw */
2132 NULL, /* roomlist_room_serialize */
2133 NULL, /* unregister_user */
2134 msn_send_attention, /* send_attention */
2135 msn_attention_types, /* attention_types */
2136
2137 /* padding */
2138 NULL
2139 };
2140
2141 static PurplePluginInfo info =
2142 {
2143 PURPLE_PLUGIN_MAGIC,
2144 PURPLE_MAJOR_VERSION,
2145 PURPLE_MINOR_VERSION,
2146 PURPLE_PLUGIN_PROTOCOL, /**< type */
2147 NULL, /**< ui_requirement */
2148 0, /**< flags */
2149 NULL, /**< dependencies */
2150 PURPLE_PRIORITY_DEFAULT, /**< priority */
2151
2152 "prpl-msn", /**< id */
2153 "MSN", /**< name */
2154 DISPLAY_VERSION, /**< version */
2155 /** summary */
2156 N_("MSN Protocol Plugin"),
2157 /** description */
2158 N_("MSN Protocol Plugin"),
2159 "Christian Hammond <chipx86@gnupdate.org>", /**< author */
2160 PURPLE_WEBSITE, /**< homepage */
2161
2162 msn_load, /**< load */
2163 msn_unload, /**< unload */
2164 NULL, /**< destroy */
2165
2166 NULL, /**< ui_info */
2167 &prpl_info, /**< extra_info */
2168 NULL, /**< prefs_info */
2169 msn_actions,
2170
2171 /* padding */
2172 NULL,
2173 NULL,
2174 NULL,
2175 NULL
2176 };
2177
2178 static void
2179 init_plugin(PurplePlugin *plugin)
2180 {
2181 PurpleAccountOption *option;
2182
2183 option = purple_account_option_string_new(_("Server"), "server",
2184 MSN_SERVER);
2185 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options,
2186 option);
2187
2188 option = purple_account_option_int_new(_("Port"), "port", 1863);
2189 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options,
2190 option);
2191
2192 option = purple_account_option_bool_new(_("Use HTTP Method"),
2193 "http_method", FALSE);
2194 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options,
2195 option);
2196
2197 option = purple_account_option_string_new(_("HTTP Method Server"),
2198 "http_method_server", MSN_HTTPCONN_SERVER);
2199 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options,
2200 option);
2201
2202 option = purple_account_option_bool_new(_("Show custom smileys"),
2203 "custom_smileys", TRUE);
2204 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options,
2205 option);
2206
2207 purple_cmd_register("nudge", "", PURPLE_CMD_P_PRPL,
2208 PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_PRPL_ONLY,
2209 "prpl-msn", msn_cmd_nudge,
2210 _("nudge: nudge a user to get their attention"), NULL);
2211
2212 purple_prefs_remove("/plugins/prpl/msn");
2213
2214 purple_signal_connect(purple_get_core(), "uri-handler", plugin,
2215 PURPLE_CALLBACK(msn_uri_handler), NULL);
2216 }
2217
2218 PURPLE_INIT_PLUGIN(msn, init_plugin, info);