comparison libgaim/protocols/msn/switchboard.c @ 14192:60b1bc8dbf37

[gaim-migrate @ 16863] Renamed 'core' to 'libgaim' committer: Tailor Script <tailor@pidgin.im>
author Evan Schoenberg <evan.s@dreskin.net>
date Sat, 19 Aug 2006 01:50:10 +0000
parents
children f71894e1700d
comparison
equal deleted inserted replaced
14191:009db0b357b5 14192:60b1bc8dbf37
1 /**
2 * @file switchboard.c MSN switchboard functions
3 *
4 * gaim
5 *
6 * Gaim 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 */
24 #include "msn.h"
25 #include "prefs.h"
26 #include "switchboard.h"
27 #include "notification.h"
28 #include "msn-utils.h"
29
30 #include "error.h"
31
32 static MsnTable *cbs_table;
33
34 static void msg_error_helper(MsnCmdProc *cmdproc, MsnMessage *msg,
35 MsnMsgErrorType error);
36
37 /**************************************************************************
38 * Main
39 **************************************************************************/
40
41 MsnSwitchBoard *
42 msn_switchboard_new(MsnSession *session)
43 {
44 MsnSwitchBoard *swboard;
45 MsnServConn *servconn;
46
47 g_return_val_if_fail(session != NULL, NULL);
48
49 swboard = g_new0(MsnSwitchBoard, 1);
50
51 swboard->session = session;
52 swboard->servconn = servconn = msn_servconn_new(session, MSN_SERVCONN_SB);
53 swboard->cmdproc = servconn->cmdproc;
54
55 swboard->msg_queue = g_queue_new();
56 swboard->empty = TRUE;
57
58 swboard->cmdproc->data = swboard;
59 swboard->cmdproc->cbs_table = cbs_table;
60
61 session->switches = g_list_append(session->switches, swboard);
62
63 return swboard;
64 }
65
66 void
67 msn_switchboard_destroy(MsnSwitchBoard *swboard)
68 {
69 MsnSession *session;
70 MsnMessage *msg;
71 GList *l;
72
73 #ifdef MSN_DEBUG_SB
74 gaim_debug_info("msn", "switchboard_destroy: swboard(%p)\n", swboard);
75 #endif
76
77 g_return_if_fail(swboard != NULL);
78
79 if (swboard->destroying)
80 return;
81
82 swboard->destroying = TRUE;
83
84 /* If it linked us is because its looking for trouble */
85 while (swboard->slplinks != NULL)
86 msn_slplink_destroy(swboard->slplinks->data);
87
88 /* Destroy the message queue */
89 while ((msg = g_queue_pop_head(swboard->msg_queue)) != NULL)
90 {
91 if (swboard->error != MSN_SB_ERROR_NONE)
92 {
93 /* The messages could not be sent due to a switchboard error */
94 msg_error_helper(swboard->cmdproc, msg,
95 MSN_MSG_ERROR_SB);
96 }
97 msn_message_unref(msg);
98 }
99
100 g_queue_free(swboard->msg_queue);
101
102 /* msg_error_helper will both remove the msg from ack_list and
103 unref it, so we don't need to do either here */
104 while ((l = swboard->ack_list) != NULL)
105 msg_error_helper(swboard->cmdproc, l->data, MSN_MSG_ERROR_SB);
106
107 g_free(swboard->im_user);
108 g_free(swboard->auth_key);
109 g_free(swboard->session_id);
110
111 for (l = swboard->users; l != NULL; l = l->next)
112 g_free(l->data);
113
114 session = swboard->session;
115 session->switches = g_list_remove(session->switches, swboard);
116
117 #if 0
118 /* This should never happen or we are in trouble. */
119 if (swboard->servconn != NULL)
120 msn_servconn_destroy(swboard->servconn);
121 #endif
122
123 swboard->cmdproc->data = NULL;
124
125 msn_servconn_set_disconnect_cb(swboard->servconn, NULL);
126
127 msn_servconn_destroy(swboard->servconn);
128
129 g_free(swboard);
130 }
131
132 void
133 msn_switchboard_set_auth_key(MsnSwitchBoard *swboard, const char *key)
134 {
135 g_return_if_fail(swboard != NULL);
136 g_return_if_fail(key != NULL);
137
138 swboard->auth_key = g_strdup(key);
139 }
140
141 const char *
142 msn_switchboard_get_auth_key(MsnSwitchBoard *swboard)
143 {
144 g_return_val_if_fail(swboard != NULL, NULL);
145
146 return swboard->auth_key;
147 }
148
149 void
150 msn_switchboard_set_session_id(MsnSwitchBoard *swboard, const char *id)
151 {
152 g_return_if_fail(swboard != NULL);
153 g_return_if_fail(id != NULL);
154
155 if (swboard->session_id != NULL)
156 g_free(swboard->session_id);
157
158 swboard->session_id = g_strdup(id);
159 }
160
161 const char *
162 msn_switchboard_get_session_id(MsnSwitchBoard *swboard)
163 {
164 g_return_val_if_fail(swboard != NULL, NULL);
165
166 return swboard->session_id;
167 }
168
169 void
170 msn_switchboard_set_invited(MsnSwitchBoard *swboard, gboolean invited)
171 {
172 g_return_if_fail(swboard != NULL);
173
174 swboard->invited = invited;
175 }
176
177 gboolean
178 msn_switchboard_is_invited(MsnSwitchBoard *swboard)
179 {
180 g_return_val_if_fail(swboard != NULL, FALSE);
181
182 return swboard->invited;
183 }
184
185 /**************************************************************************
186 * Utility
187 **************************************************************************/
188
189 static void
190 send_clientcaps(MsnSwitchBoard *swboard)
191 {
192 MsnMessage *msg;
193
194 msg = msn_message_new(MSN_MSG_CAPS);
195 msn_message_set_content_type(msg, "text/x-clientcaps");
196 msn_message_set_flag(msg, 'U');
197 msn_message_set_bin_data(msg, MSN_CLIENTINFO, strlen(MSN_CLIENTINFO));
198
199 msn_switchboard_send_msg(swboard, msg, TRUE);
200
201 msn_message_destroy(msg);
202 }
203
204 static void
205 msn_switchboard_add_user(MsnSwitchBoard *swboard, const char *user)
206 {
207 MsnCmdProc *cmdproc;
208 GaimAccount *account;
209
210 g_return_if_fail(swboard != NULL);
211
212 cmdproc = swboard->cmdproc;
213 account = cmdproc->session->account;
214
215 swboard->users = g_list_prepend(swboard->users, g_strdup(user));
216 swboard->current_users++;
217 swboard->empty = FALSE;
218
219 #ifdef MSN_DEBUG_CHAT
220 gaim_debug_info("msn", "user=[%s], total=%d\n", user,
221 swboard->current_users);
222 #endif
223
224 if (!(swboard->flag & MSN_SB_FLAG_IM) && (swboard->conv != NULL))
225 {
226 /* This is a helper switchboard. */
227 gaim_debug_error("msn", "switchboard_add_user: conv != NULL\n");
228 return;
229 }
230
231 if ((swboard->conv != NULL) &&
232 (gaim_conversation_get_type(swboard->conv) == GAIM_CONV_TYPE_CHAT))
233 {
234 gaim_conv_chat_add_user(GAIM_CONV_CHAT(swboard->conv), user, NULL,
235 GAIM_CBFLAGS_NONE, TRUE);
236 }
237 else if (swboard->current_users > 1 || swboard->total_users > 1)
238 {
239 if (swboard->conv == NULL ||
240 gaim_conversation_get_type(swboard->conv) != GAIM_CONV_TYPE_CHAT)
241 {
242 GList *l;
243
244 #ifdef MSN_DEBUG_CHAT
245 gaim_debug_info("msn", "[chat] Switching to chat.\n");
246 #endif
247
248 #if 0
249 /* this is bad - it causes msn_switchboard_close to be called on the
250 * switchboard we're in the middle of using :( */
251 if (swboard->conv != NULL)
252 gaim_conversation_destroy(swboard->conv);
253 #endif
254
255 cmdproc->session->conv_seq++;
256 swboard->chat_id = cmdproc->session->conv_seq;
257 swboard->flag |= MSN_SB_FLAG_IM;
258 swboard->conv = serv_got_joined_chat(account->gc,
259 swboard->chat_id,
260 "MSN Chat");
261
262 for (l = swboard->users; l != NULL; l = l->next)
263 {
264 const char *tmp_user;
265
266 tmp_user = l->data;
267
268 #ifdef MSN_DEBUG_CHAT
269 gaim_debug_info("msn", "[chat] Adding [%s].\n", tmp_user);
270 #endif
271
272 gaim_conv_chat_add_user(GAIM_CONV_CHAT(swboard->conv),
273 tmp_user, NULL, GAIM_CBFLAGS_NONE, TRUE);
274 }
275
276 #ifdef MSN_DEBUG_CHAT
277 gaim_debug_info("msn", "[chat] We add ourselves.\n");
278 #endif
279
280 gaim_conv_chat_add_user(GAIM_CONV_CHAT(swboard->conv),
281 gaim_account_get_username(account),
282 NULL, GAIM_CBFLAGS_NONE, TRUE);
283
284 g_free(swboard->im_user);
285 swboard->im_user = NULL;
286 }
287 }
288 else if (swboard->conv == NULL)
289 {
290 swboard->conv = gaim_find_conversation_with_account(GAIM_CONV_TYPE_IM,
291 user, account);
292 }
293 else
294 {
295 gaim_debug_warning("msn", "switchboard_add_user: This should not happen!\n");
296 }
297 }
298
299 static GaimConversation *
300 msn_switchboard_get_conv(MsnSwitchBoard *swboard)
301 {
302 GaimAccount *account;
303
304 g_return_val_if_fail(swboard != NULL, NULL);
305
306 if (swboard->conv != NULL)
307 return swboard->conv;
308
309 gaim_debug_error("msn", "Switchboard with unassigned conversation\n");
310
311 account = swboard->session->account;
312
313 return (swboard->conv = gaim_conversation_new(GAIM_CONV_TYPE_IM,
314 account, swboard->im_user));
315 }
316
317 static void
318 msn_switchboard_report_user(MsnSwitchBoard *swboard, GaimMessageFlags flags, const char *msg)
319 {
320 GaimConversation *conv;
321
322 g_return_if_fail(swboard != NULL);
323 g_return_if_fail(msg != NULL);
324
325 if ((conv = msn_switchboard_get_conv(swboard)) != NULL)
326 {
327 gaim_conversation_write(conv, NULL, msg, flags, time(NULL));
328 }
329 }
330
331 static void
332 swboard_error_helper(MsnSwitchBoard *swboard, int reason, const char *passport)
333 {
334 g_return_if_fail(swboard != NULL);
335
336 gaim_debug_warning("msg", "Error: Unable to call the user %s for reason %i\n",
337 passport ? passport : "(null)", reason);
338
339 /* TODO: if current_users > 0, this is probably a chat and an invite failed,
340 * we should report that in the chat or something */
341 if (swboard->current_users == 0)
342 {
343 swboard->error = reason;
344 msn_switchboard_close(swboard);
345 }
346 }
347
348 static void
349 cal_error_helper(MsnTransaction *trans, int reason)
350 {
351 MsnSwitchBoard *swboard;
352 const char *passport;
353 char **params;
354
355 params = g_strsplit(trans->params, " ", 0);
356
357 passport = params[0];
358
359 swboard = trans->data;
360
361 gaim_debug_warning("msn", "cal_error_helper: command %s failed for reason %i\n",trans->command,reason);
362
363 swboard_error_helper(swboard, reason, passport);
364
365 g_strfreev(params);
366 }
367
368 static void
369 msg_error_helper(MsnCmdProc *cmdproc, MsnMessage *msg, MsnMsgErrorType error)
370 {
371 MsnSwitchBoard *swboard;
372
373 g_return_if_fail(cmdproc != NULL);
374 g_return_if_fail(msg != NULL);
375
376 if ((error != MSN_MSG_ERROR_SB) && (msg->nak_cb != NULL))
377 msg->nak_cb(msg, msg->ack_data);
378
379 swboard = cmdproc->data;
380
381 /* This is not good, and should be fixed somewhere else. */
382 g_return_if_fail(swboard != NULL);
383
384 if (msg->type == MSN_MSG_TEXT)
385 {
386 const char *format, *str_reason;
387 char *body_str, *body_enc, *pre, *post;
388
389 #if 0
390 if (swboard->conv == NULL)
391 {
392 if (msg->ack_ref)
393 msn_message_unref(msg);
394
395 return;
396 }
397 #endif
398
399 if (error == MSN_MSG_ERROR_TIMEOUT)
400 {
401 str_reason = _("Message may have not been sent "
402 "because a timeout occurred:");
403 }
404 else if (error == MSN_MSG_ERROR_SB)
405 {
406 switch (swboard->error)
407 {
408 case MSN_SB_ERROR_OFFLINE:
409 str_reason = _("Message could not be sent, "
410 "not allowed while invisible:");
411 break;
412 case MSN_SB_ERROR_USER_OFFLINE:
413 str_reason = _("Message could not be sent "
414 "because the user is offline:");
415 break;
416 case MSN_SB_ERROR_CONNECTION:
417 str_reason = _("Message could not be sent "
418 "because a connection error occurred:");
419 break;
420 case MSN_SB_ERROR_TOO_FAST:
421 str_reason = _("Message could not be sent "
422 "because we are sending too quickly:");
423 break;
424 default:
425 str_reason = _("Message could not be sent "
426 "because an error with "
427 "the switchboard occurred:");
428 break;
429 }
430 }
431 else
432 {
433 str_reason = _("Message may have not been sent "
434 "because an unknown error occurred:");
435 }
436
437 body_str = msn_message_to_string(msg);
438 body_enc = g_markup_escape_text(body_str, -1);
439 g_free(body_str);
440
441 format = msn_message_get_attr(msg, "X-MMS-IM-Format");
442 msn_parse_format(format, &pre, &post);
443 body_str = g_strdup_printf("%s%s%s", pre ? pre : "",
444 body_enc ? body_enc : "", post ? post : "");
445 g_free(body_enc);
446 g_free(pre);
447 g_free(post);
448
449 msn_switchboard_report_user(swboard, GAIM_MESSAGE_ERROR,
450 str_reason);
451 msn_switchboard_report_user(swboard, GAIM_MESSAGE_RAW,
452 body_str);
453
454 g_free(body_str);
455 }
456
457 /* If a timeout occures we will want the msg around just in case we
458 * receive the ACK after the timeout. */
459 if (msg->ack_ref && error != MSN_MSG_ERROR_TIMEOUT)
460 {
461 swboard->ack_list = g_list_remove(swboard->ack_list, msg);
462 msn_message_unref(msg);
463 }
464 }
465
466 /**************************************************************************
467 * Message Stuff
468 **************************************************************************/
469
470 /** Called when a message times out. */
471 static void
472 msg_timeout(MsnCmdProc *cmdproc, MsnTransaction *trans)
473 {
474 MsnMessage *msg;
475
476 msg = trans->data;
477
478 msg_error_helper(cmdproc, msg, MSN_MSG_ERROR_TIMEOUT);
479 }
480
481 /** Called when we receive an error of a message. */
482 static void
483 msg_error(MsnCmdProc *cmdproc, MsnTransaction *trans, int error)
484 {
485 msg_error_helper(cmdproc, trans->data, MSN_MSG_ERROR_UNKNOWN);
486 }
487
488 #if 0
489 /** Called when we receive an ack of a special message. */
490 static void
491 msg_ack(MsnCmdProc *cmdproc, MsnCommand *cmd)
492 {
493 MsnMessage *msg;
494
495 msg = cmd->trans->data;
496
497 if (msg->ack_cb != NULL)
498 msg->ack_cb(msg->ack_data);
499
500 msn_message_unref(msg);
501 }
502
503 /** Called when we receive a nak of a special message. */
504 static void
505 msg_nak(MsnCmdProc *cmdproc, MsnCommand *cmd)
506 {
507 MsnMessage *msg;
508
509 msg = cmd->trans->data;
510
511 msn_message_unref(msg);
512 }
513 #endif
514
515 static void
516 release_msg(MsnSwitchBoard *swboard, MsnMessage *msg)
517 {
518 MsnCmdProc *cmdproc;
519 MsnTransaction *trans;
520 char *payload;
521 gsize payload_len;
522
523 g_return_if_fail(swboard != NULL);
524 g_return_if_fail(msg != NULL);
525
526 cmdproc = swboard->cmdproc;
527
528 payload = msn_message_gen_payload(msg, &payload_len);
529
530 #ifdef MSN_DEBUG_SB
531 msn_message_show_readable(msg, "SB SEND", FALSE);
532 #endif
533
534 trans = msn_transaction_new(cmdproc, "MSG", "%c %d",
535 msn_message_get_flag(msg), payload_len);
536
537 /* Data for callbacks */
538 msn_transaction_set_data(trans, msg);
539
540 if (msg->type == MSN_MSG_TEXT)
541 {
542 msg->ack_ref = TRUE;
543 msn_message_ref(msg);
544 swboard->ack_list = g_list_append(swboard->ack_list, msg);
545 msn_transaction_set_timeout_cb(trans, msg_timeout);
546 }
547 else if (msg->type == MSN_MSG_SLP)
548 {
549 msg->ack_ref = TRUE;
550 msn_message_ref(msg);
551 swboard->ack_list = g_list_append(swboard->ack_list, msg);
552 msn_transaction_set_timeout_cb(trans, msg_timeout);
553 #if 0
554 if (msg->ack_cb != NULL)
555 {
556 msn_transaction_add_cb(trans, "ACK", msg_ack);
557 msn_transaction_add_cb(trans, "NAK", msg_nak);
558 }
559 #endif
560 }
561
562 trans->payload = payload;
563 trans->payload_len = payload_len;
564
565 msg->trans = trans;
566
567 msn_cmdproc_send_trans(cmdproc, trans);
568 }
569
570 static void
571 queue_msg(MsnSwitchBoard *swboard, MsnMessage *msg)
572 {
573 g_return_if_fail(swboard != NULL);
574 g_return_if_fail(msg != NULL);
575
576 gaim_debug_info("msn", "Appending message to queue.\n");
577
578 g_queue_push_tail(swboard->msg_queue, msg);
579
580 msn_message_ref(msg);
581 }
582
583 static void
584 process_queue(MsnSwitchBoard *swboard)
585 {
586 MsnMessage *msg;
587
588 g_return_if_fail(swboard != NULL);
589
590 gaim_debug_info("msn", "Processing queue\n");
591
592 while ((msg = g_queue_pop_head(swboard->msg_queue)) != NULL)
593 {
594 gaim_debug_info("msn", "Sending message\n");
595 release_msg(swboard, msg);
596 msn_message_unref(msg);
597 }
598 }
599
600 gboolean
601 msn_switchboard_can_send(MsnSwitchBoard *swboard)
602 {
603 g_return_val_if_fail(swboard != NULL, FALSE);
604
605 if (swboard->empty || !g_queue_is_empty(swboard->msg_queue))
606 return FALSE;
607
608 return TRUE;
609 }
610
611 void
612 msn_switchboard_send_msg(MsnSwitchBoard *swboard, MsnMessage *msg,
613 gboolean queue)
614 {
615 g_return_if_fail(swboard != NULL);
616 g_return_if_fail(msg != NULL);
617
618 if (msn_switchboard_can_send(swboard))
619 release_msg(swboard, msg);
620 else if (queue)
621 queue_msg(swboard, msg);
622 }
623
624 /**************************************************************************
625 * Switchboard Commands
626 **************************************************************************/
627
628 static void
629 ans_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
630 {
631 MsnSwitchBoard *swboard;
632
633 swboard = cmdproc->data;
634 swboard->ready = TRUE;
635 }
636
637 static void
638 bye_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
639 {
640 MsnSwitchBoard *swboard;
641 const char *user;
642
643 swboard = cmdproc->data;
644 user = cmd->params[0];
645
646 if (!(swboard->flag & MSN_SB_FLAG_IM) && (swboard->conv != NULL))
647 gaim_debug_error("msn_switchboard", "bye_cmd: helper bug\n");
648
649 if (swboard->conv == NULL)
650 {
651 /* This is a helper switchboard */
652 msn_switchboard_destroy(swboard);
653 }
654 else if ((swboard->current_users > 1) ||
655 (gaim_conversation_get_type(swboard->conv) == GAIM_CONV_TYPE_CHAT))
656 {
657 /* This is a switchboard used for a chat */
658 gaim_conv_chat_remove_user(GAIM_CONV_CHAT(swboard->conv), user, NULL);
659 swboard->current_users--;
660 if (swboard->current_users == 0)
661 msn_switchboard_destroy(swboard);
662 }
663 else
664 {
665 /* This is a switchboard used for a im session */
666 msn_switchboard_destroy(swboard);
667 }
668 }
669
670 static void
671 iro_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
672 {
673 GaimAccount *account;
674 GaimConnection *gc;
675 MsnSwitchBoard *swboard;
676
677 account = cmdproc->session->account;
678 gc = account->gc;
679 swboard = cmdproc->data;
680
681 swboard->total_users = atoi(cmd->params[2]);
682
683 msn_switchboard_add_user(swboard, cmd->params[3]);
684 }
685
686 static void
687 joi_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
688 {
689 MsnSession *session;
690 GaimAccount *account;
691 GaimConnection *gc;
692 MsnSwitchBoard *swboard;
693 const char *passport;
694
695 passport = cmd->params[0];
696
697 session = cmdproc->session;
698 account = session->account;
699 gc = account->gc;
700 swboard = cmdproc->data;
701
702 msn_switchboard_add_user(swboard, passport);
703
704 process_queue(swboard);
705
706 if (!session->http_method)
707 send_clientcaps(swboard);
708
709 if (swboard->closed)
710 msn_switchboard_close(swboard);
711 }
712
713 static void
714 msg_cmd_post(MsnCmdProc *cmdproc, MsnCommand *cmd, char *payload, size_t len)
715 {
716 MsnMessage *msg;
717
718 msg = msn_message_new_from_cmd(cmdproc->session, cmd);
719
720 msn_message_parse_payload(msg, payload, len);
721 #ifdef MSN_DEBUG_SB
722 msn_message_show_readable(msg, "SB RECV", FALSE);
723 #endif
724
725 if (msg->remote_user != NULL)
726 g_free (msg->remote_user);
727
728 msg->remote_user = g_strdup(cmd->params[0]);
729 msn_cmdproc_process_msg(cmdproc, msg);
730
731 msn_message_destroy(msg);
732 }
733
734 static void
735 msg_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
736 {
737 cmdproc->servconn->payload_len = atoi(cmd->params[2]);
738 cmdproc->last_cmd->payload_cb = msg_cmd_post;
739 }
740
741 static void
742 nak_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
743 {
744 MsnMessage *msg;
745
746 msg = cmd->trans->data;
747 g_return_if_fail(msg != NULL);
748
749 msg_error_helper(cmdproc, msg, MSN_MSG_ERROR_NAK);
750 }
751
752 static void
753 ack_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
754 {
755 MsnSwitchBoard *swboard;
756 MsnMessage *msg;
757
758 msg = cmd->trans->data;
759
760 if (msg->ack_cb != NULL)
761 msg->ack_cb(msg, msg->ack_data);
762
763 swboard = cmdproc->data;
764 swboard->ack_list = g_list_remove(swboard->ack_list, msg);
765 msn_message_unref(msg);
766 }
767
768 static void
769 out_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
770 {
771 GaimConnection *gc;
772 MsnSwitchBoard *swboard;
773
774 gc = cmdproc->session->account->gc;
775 swboard = cmdproc->data;
776
777 if (swboard->current_users > 1)
778 serv_got_chat_left(gc, swboard->chat_id);
779
780 msn_switchboard_disconnect(swboard);
781 }
782
783 static void
784 usr_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
785 {
786 MsnSwitchBoard *swboard;
787
788 swboard = cmdproc->data;
789
790 #if 0
791 GList *l;
792
793 for (l = swboard->users; l != NULL; l = l->next)
794 {
795 const char *user;
796 user = l->data;
797
798 msn_cmdproc_send(cmdproc, "CAL", "%s", user);
799 }
800 #endif
801
802 swboard->ready = TRUE;
803 msn_cmdproc_process_queue(cmdproc);
804 }
805
806 /**************************************************************************
807 * Message Handlers
808 **************************************************************************/
809 static void
810 plain_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
811 {
812 GaimConnection *gc;
813 MsnSwitchBoard *swboard;
814 const char *body;
815 char *body_str;
816 char *body_enc;
817 char *body_final;
818 size_t body_len;
819 const char *passport;
820 const char *value;
821
822 gc = cmdproc->session->account->gc;
823 swboard = cmdproc->data;
824
825 body = msn_message_get_bin_data(msg, &body_len);
826 body_str = g_strndup(body, body_len);
827 body_enc = g_markup_escape_text(body_str, -1);
828 g_free(body_str);
829
830 passport = msg->remote_user;
831
832 if (!strcmp(passport, "messenger@microsoft.com") &&
833 strstr(body, "immediate security update"))
834 {
835 return;
836 }
837
838 #if 0
839 if ((value = msn_message_get_attr(msg, "User-Agent")) != NULL)
840 {
841 gaim_debug_misc("msn", "User-Agent = '%s'\n", value);
842 }
843 #endif
844
845 if ((value = msn_message_get_attr(msg, "X-MMS-IM-Format")) != NULL)
846 {
847 char *pre, *post;
848
849 msn_parse_format(value, &pre, &post);
850
851 body_final = g_strdup_printf("%s%s%s", pre ? pre : "",
852 body_enc ? body_enc : "", post ? post : "");
853
854 g_free(pre);
855 g_free(post);
856 g_free(body_enc);
857 }
858 else
859 {
860 body_final = body_enc;
861 }
862
863 swboard->flag |= MSN_SB_FLAG_IM;
864
865 if (swboard->current_users > 1 ||
866 ((swboard->conv != NULL) &&
867 gaim_conversation_get_type(swboard->conv) == GAIM_CONV_TYPE_CHAT))
868 {
869 /* If current_users is always ok as it should then there is no need to
870 * check if this is a chat. */
871 if (swboard->current_users <= 1)
872 gaim_debug_misc("msn", "plain_msg: current_users(%d)\n",
873 swboard->current_users);
874
875 serv_got_chat_in(gc, swboard->chat_id, passport, 0, body_final,
876 time(NULL));
877 if (swboard->conv == NULL)
878 {
879 swboard->conv = gaim_find_chat(gc, swboard->chat_id);
880 swboard->flag |= MSN_SB_FLAG_IM;
881 }
882 }
883 else
884 {
885 serv_got_im(gc, passport, body_final, 0, time(NULL));
886 if (swboard->conv == NULL)
887 {
888 swboard->conv = gaim_find_conversation_with_account(GAIM_CONV_TYPE_IM,
889 passport, gaim_connection_get_account(gc));
890 swboard->flag |= MSN_SB_FLAG_IM;
891 }
892 }
893
894 g_free(body_final);
895 }
896
897 static void
898 control_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
899 {
900 GaimConnection *gc;
901 MsnSwitchBoard *swboard;
902 char *passport;
903
904 gc = cmdproc->session->account->gc;
905 swboard = cmdproc->data;
906 passport = msg->remote_user;
907
908 if (swboard->current_users == 1 &&
909 msn_message_get_attr(msg, "TypingUser") != NULL)
910 {
911 serv_got_typing(gc, passport, MSN_TYPING_RECV_TIMEOUT,
912 GAIM_TYPING);
913 }
914 }
915
916 static void
917 clientcaps_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
918 {
919 #if 0
920 MsnSession *session;
921 MsnSwitchBoard *swboard;
922 MsnUser *user;
923 GHashTable *clientcaps;
924 const char *value;
925
926 char *passport = msg->sender;
927
928 session = cmdproc->session;
929 swboard = cmdproc->servconn->swboard;
930
931 clientcaps = msn_message_get_hashtable_from_body(msg);
932 #endif
933 }
934
935 static void
936 nudge_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
937 {
938 MsnSwitchBoard *swboard;
939 char *username, *str;
940 GaimAccount *account;
941 GaimBuddy *buddy;
942 const char *user;
943
944 swboard = cmdproc->data;
945 account = cmdproc->session->account;
946 user = msg->remote_user;
947
948 if ((buddy = gaim_find_buddy(account, user)) != NULL)
949 username = g_markup_escape_text(gaim_buddy_get_alias(buddy), -1);
950 else
951 username = g_markup_escape_text(user, -1);
952
953 str = g_strdup_printf(_("%s just sent you a Nudge!"), username);
954 g_free(username);
955 msn_switchboard_report_user(swboard, GAIM_MESSAGE_SYSTEM, str);
956 g_free(str);
957 }
958
959 /**************************************************************************
960 * Connect stuff
961 **************************************************************************/
962 static void
963 connect_cb(MsnServConn *servconn)
964 {
965 MsnSwitchBoard *swboard;
966 MsnCmdProc *cmdproc;
967 GaimAccount *account;
968
969 cmdproc = servconn->cmdproc;
970 g_return_if_fail(cmdproc != NULL);
971
972 account = cmdproc->session->account;
973 swboard = cmdproc->data;
974 g_return_if_fail(swboard != NULL);
975
976 if (msn_switchboard_is_invited(swboard))
977 {
978 swboard->empty = FALSE;
979
980 msn_cmdproc_send(cmdproc, "ANS", "%s %s %s",
981 gaim_account_get_username(account),
982 swboard->auth_key, swboard->session_id);
983 }
984 else
985 {
986 msn_cmdproc_send(cmdproc, "USR", "%s %s",
987 gaim_account_get_username(account),
988 swboard->auth_key);
989 }
990 }
991
992 static void
993 disconnect_cb(MsnServConn *servconn)
994 {
995 MsnSwitchBoard *swboard;
996
997 swboard = servconn->cmdproc->data;
998 g_return_if_fail(swboard != NULL);
999
1000 msn_servconn_set_disconnect_cb(swboard->servconn, NULL);
1001
1002 msn_switchboard_destroy(swboard);
1003 }
1004
1005 gboolean
1006 msn_switchboard_connect(MsnSwitchBoard *swboard, const char *host, int port)
1007 {
1008 g_return_val_if_fail(swboard != NULL, FALSE);
1009
1010 msn_servconn_set_connect_cb(swboard->servconn, connect_cb);
1011 msn_servconn_set_disconnect_cb(swboard->servconn, disconnect_cb);
1012
1013 return msn_servconn_connect(swboard->servconn, host, port);
1014 }
1015
1016 void
1017 msn_switchboard_disconnect(MsnSwitchBoard *swboard)
1018 {
1019 g_return_if_fail(swboard != NULL);
1020
1021 msn_servconn_disconnect(swboard->servconn);
1022 }
1023
1024 /**************************************************************************
1025 * Call stuff
1026 **************************************************************************/
1027 static void
1028 got_cal(MsnCmdProc *cmdproc, MsnCommand *cmd)
1029 {
1030 #if 0
1031 MsnSwitchBoard *swboard;
1032 const char *user;
1033
1034 swboard = cmdproc->data;
1035
1036 user = cmd->params[0];
1037
1038 msn_switchboard_add_user(swboard, user);
1039 #endif
1040 }
1041
1042 static void
1043 cal_timeout(MsnCmdProc *cmdproc, MsnTransaction *trans)
1044 {
1045 gaim_debug_warning("msn", "cal_timeout: command %s timed out\n", trans->command);
1046
1047 cal_error_helper(trans, MSN_SB_ERROR_UNKNOWN);
1048 }
1049
1050 static void
1051 cal_error(MsnCmdProc *cmdproc, MsnTransaction *trans, int error)
1052 {
1053 int reason = MSN_SB_ERROR_UNKNOWN;
1054
1055 if (error == 215)
1056 {
1057 gaim_debug_info("msn", "Invited user already in switchboard\n");
1058 return;
1059 }
1060 else if (error == 217)
1061 {
1062 reason = MSN_SB_ERROR_USER_OFFLINE;
1063 }
1064
1065 gaim_debug_warning("msn", "cal_error: command %s gave error %i\n", trans->command, error);
1066
1067 cal_error_helper(trans, reason);
1068 }
1069
1070 void
1071 msn_switchboard_request_add_user(MsnSwitchBoard *swboard, const char *user)
1072 {
1073 MsnTransaction *trans;
1074 MsnCmdProc *cmdproc;
1075
1076 g_return_if_fail(swboard != NULL);
1077
1078 cmdproc = swboard->cmdproc;
1079
1080 trans = msn_transaction_new(cmdproc, "CAL", "%s", user);
1081 /* this doesn't do anything, but users seem to think that
1082 * 'Unhandled command' is some kind of error, so we don't report it */
1083 msn_transaction_add_cb(trans, "CAL", got_cal);
1084
1085 msn_transaction_set_data(trans, swboard);
1086 msn_transaction_set_timeout_cb(trans, cal_timeout);
1087
1088 if (swboard->ready)
1089 msn_cmdproc_send_trans(cmdproc, trans);
1090 else
1091 msn_cmdproc_queue_trans(cmdproc, trans);
1092 }
1093
1094 /**************************************************************************
1095 * Create & Transfer stuff
1096 **************************************************************************/
1097
1098 static void
1099 got_swboard(MsnCmdProc *cmdproc, MsnCommand *cmd)
1100 {
1101 MsnSwitchBoard *swboard;
1102 char *host;
1103 int port;
1104 swboard = cmd->trans->data;
1105
1106 if (g_list_find(cmdproc->session->switches, swboard) == NULL)
1107 /* The conversation window was closed. */
1108 return;
1109
1110 msn_switchboard_set_auth_key(swboard, cmd->params[4]);
1111
1112 msn_parse_socket(cmd->params[2], &host, &port);
1113
1114 if (!msn_switchboard_connect(swboard, host, port))
1115 msn_switchboard_destroy(swboard);
1116
1117 g_free(host);
1118 }
1119
1120 static void
1121 xfr_error(MsnCmdProc *cmdproc, MsnTransaction *trans, int error)
1122 {
1123 MsnSwitchBoard *swboard;
1124 int reason = MSN_SB_ERROR_UNKNOWN;
1125
1126 if (error == 913)
1127 reason = MSN_SB_ERROR_OFFLINE;
1128 else if (error == 800)
1129 reason = MSN_SB_ERROR_TOO_FAST;
1130
1131 swboard = trans->data;
1132
1133 gaim_debug_info("msn", "xfr_error %i for %s: trans %x, command %s, reason %i\n",
1134 error, (swboard->im_user ? swboard->im_user : "(null)"), trans,
1135 (trans->command ? trans->command : "(null)"), reason);
1136
1137 swboard_error_helper(swboard, reason, swboard->im_user);
1138 }
1139
1140 void
1141 msn_switchboard_request(MsnSwitchBoard *swboard)
1142 {
1143 MsnCmdProc *cmdproc;
1144 MsnTransaction *trans;
1145
1146 g_return_if_fail(swboard != NULL);
1147
1148 cmdproc = swboard->session->notification->cmdproc;
1149
1150 trans = msn_transaction_new(cmdproc, "XFR", "%s", "SB");
1151 msn_transaction_add_cb(trans, "XFR", got_swboard);
1152
1153 msn_transaction_set_data(trans, swboard);
1154 msn_transaction_set_error_cb(trans, xfr_error);
1155
1156 msn_cmdproc_send_trans(cmdproc, trans);
1157 }
1158
1159 void
1160 msn_switchboard_close(MsnSwitchBoard *swboard)
1161 {
1162 g_return_if_fail(swboard != NULL);
1163
1164 if (swboard->error != MSN_SB_ERROR_NONE)
1165 {
1166 msn_switchboard_destroy(swboard);
1167 }
1168 else if (g_queue_is_empty(swboard->msg_queue) ||
1169 !swboard->session->connected)
1170 {
1171 MsnCmdProc *cmdproc;
1172 cmdproc = swboard->cmdproc;
1173 msn_cmdproc_send_quick(cmdproc, "OUT", NULL, NULL);
1174
1175 msn_switchboard_destroy(swboard);
1176 }
1177 else
1178 {
1179 swboard->closed = TRUE;
1180 }
1181 }
1182
1183 gboolean
1184 msn_switchboard_release(MsnSwitchBoard *swboard, MsnSBFlag flag)
1185 {
1186 g_return_val_if_fail(swboard != NULL, FALSE);
1187
1188 swboard->flag &= ~flag;
1189
1190 if (flag == MSN_SB_FLAG_IM)
1191 /* Forget any conversation that used to be associated with this
1192 * swboard. */
1193 swboard->conv = NULL;
1194
1195 if (swboard->flag == 0)
1196 {
1197 msn_switchboard_close(swboard);
1198 return TRUE;
1199 }
1200
1201 return FALSE;
1202 }
1203
1204 /**************************************************************************
1205 * Init stuff
1206 **************************************************************************/
1207
1208 void
1209 msn_switchboard_init(void)
1210 {
1211 cbs_table = msn_table_new();
1212
1213 msn_table_add_cmd(cbs_table, "ANS", "ANS", ans_cmd);
1214 msn_table_add_cmd(cbs_table, "ANS", "IRO", iro_cmd);
1215
1216 msn_table_add_cmd(cbs_table, "MSG", "ACK", ack_cmd);
1217 msn_table_add_cmd(cbs_table, "MSG", "NAK", nak_cmd);
1218
1219 msn_table_add_cmd(cbs_table, "USR", "USR", usr_cmd);
1220
1221 msn_table_add_cmd(cbs_table, NULL, "MSG", msg_cmd);
1222 msn_table_add_cmd(cbs_table, NULL, "JOI", joi_cmd);
1223 msn_table_add_cmd(cbs_table, NULL, "BYE", bye_cmd);
1224 msn_table_add_cmd(cbs_table, NULL, "OUT", out_cmd);
1225
1226 #if 0
1227 /* They might skip the history */
1228 msn_table_add_cmd(cbs_table, NULL, "ACK", NULL);
1229 #endif
1230
1231 msn_table_add_error(cbs_table, "MSG", msg_error);
1232 msn_table_add_error(cbs_table, "CAL", cal_error);
1233
1234 /* Register the message type callbacks. */
1235 msn_table_add_msg_type(cbs_table, "text/plain",
1236 plain_msg);
1237 msn_table_add_msg_type(cbs_table, "text/x-msmsgscontrol",
1238 control_msg);
1239 msn_table_add_msg_type(cbs_table, "text/x-clientcaps",
1240 clientcaps_msg);
1241 msn_table_add_msg_type(cbs_table, "text/x-clientinfo",
1242 clientcaps_msg);
1243 msn_table_add_msg_type(cbs_table, "application/x-msnmsgrp2p",
1244 msn_p2p_msg);
1245 msn_table_add_msg_type(cbs_table, "text/x-mms-emoticon",
1246 msn_emoticon_msg);
1247 msn_table_add_msg_type(cbs_table, "text/x-mms-animemoticon",
1248 msn_emoticon_msg);
1249 msn_table_add_msg_type(cbs_table, "text/x-msnmsgr-datacast",
1250 nudge_msg);
1251 #if 0
1252 msn_table_add_msg_type(cbs_table, "text/x-msmmsginvite",
1253 msn_invite_msg);
1254 #endif
1255 }
1256
1257 void
1258 msn_switchboard_end(void)
1259 {
1260 msn_table_destroy(cbs_table);
1261 }