comparison libpurple/protocols/msn/switchboard.c @ 15374:5fe8042783c1

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