src/protocols/msn/switchboard.c

branch
gaim
changeset 20470
77693555855f
parent 13071
b98e72d4089a
parent 20469
b2836a24d81e
child 20471
1966704b3e42
equal deleted inserted replaced
13071:b98e72d4089a 20470:77693555855f
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 "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 if (swboard->slplink != NULL)
86 msn_slplink_destroy(swboard->slplink);
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 if (swboard->im_user != NULL)
108 g_free(swboard->im_user);
109
110 if (swboard->auth_key != NULL)
111 g_free(swboard->auth_key);
112
113 if (swboard->session_id != NULL)
114 g_free(swboard->session_id);
115
116 for (l = swboard->users; l != NULL; l = l->next)
117 g_free(l->data);
118
119 session = swboard->session;
120 session->switches = g_list_remove(session->switches, swboard);
121
122 #if 0
123 /* This should never happen or we are in trouble. */
124 if (swboard->servconn != NULL)
125 msn_servconn_destroy(swboard->servconn);
126 #endif
127
128 swboard->cmdproc->data = NULL;
129
130 msn_servconn_set_disconnect_cb(swboard->servconn, NULL);
131
132 msn_servconn_destroy(swboard->servconn);
133
134 g_free(swboard);
135 }
136
137 void
138 msn_switchboard_set_auth_key(MsnSwitchBoard *swboard, const char *key)
139 {
140 g_return_if_fail(swboard != NULL);
141 g_return_if_fail(key != NULL);
142
143 swboard->auth_key = g_strdup(key);
144 }
145
146 const char *
147 msn_switchboard_get_auth_key(MsnSwitchBoard *swboard)
148 {
149 g_return_val_if_fail(swboard != NULL, NULL);
150
151 return swboard->auth_key;
152 }
153
154 void
155 msn_switchboard_set_session_id(MsnSwitchBoard *swboard, const char *id)
156 {
157 g_return_if_fail(swboard != NULL);
158 g_return_if_fail(id != NULL);
159
160 if (swboard->session_id != NULL)
161 g_free(swboard->session_id);
162
163 swboard->session_id = g_strdup(id);
164 }
165
166 const char *
167 msn_switchboard_get_session_id(MsnSwitchBoard *swboard)
168 {
169 g_return_val_if_fail(swboard != NULL, NULL);
170
171 return swboard->session_id;
172 }
173
174 void
175 msn_switchboard_set_invited(MsnSwitchBoard *swboard, gboolean invited)
176 {
177 g_return_if_fail(swboard != NULL);
178
179 swboard->invited = invited;
180 }
181
182 gboolean
183 msn_switchboard_is_invited(MsnSwitchBoard *swboard)
184 {
185 g_return_val_if_fail(swboard != NULL, FALSE);
186
187 return swboard->invited;
188 }
189
190 /**************************************************************************
191 * Utility
192 **************************************************************************/
193
194 static void
195 send_clientcaps(MsnSwitchBoard *swboard)
196 {
197 MsnMessage *msg;
198
199 msg = msn_message_new(MSN_MSG_CAPS);
200 msn_message_set_content_type(msg, "text/x-clientcaps");
201 msn_message_set_flag(msg, 'U');
202 msn_message_set_bin_data(msg, MSN_CLIENTINFO, strlen(MSN_CLIENTINFO));
203
204 msn_switchboard_send_msg(swboard, msg, TRUE);
205
206 msn_message_destroy(msg);
207 }
208
209 static void
210 msn_switchboard_add_user(MsnSwitchBoard *swboard, const char *user)
211 {
212 MsnCmdProc *cmdproc;
213 GaimAccount *account;
214
215 g_return_if_fail(swboard != NULL);
216
217 cmdproc = swboard->cmdproc;
218 account = cmdproc->session->account;
219
220 swboard->users = g_list_prepend(swboard->users, g_strdup(user));
221 swboard->current_users++;
222 swboard->empty = FALSE;
223
224 #ifdef MSN_DEBUG_CHAT
225 gaim_debug_info("msn", "user=[%s], total=%d\n", user,
226 swboard->current_users);
227 #endif
228
229 if (!(swboard->flag & MSN_SB_FLAG_IM) && (swboard->conv != NULL))
230 {
231 /* This is a helper switchboard. */
232 gaim_debug_error("msn", "switchboard_add_user: conv != NULL\n");
233 return;
234 }
235
236 if ((swboard->conv != NULL) &&
237 (gaim_conversation_get_type(swboard->conv) == GAIM_CONV_TYPE_CHAT))
238 {
239 gaim_conv_chat_add_user(GAIM_CONV_CHAT(swboard->conv), user, NULL,
240 GAIM_CBFLAGS_NONE, TRUE);
241 }
242 else if (swboard->current_users > 1 || swboard->total_users > 1)
243 {
244 if (swboard->conv == NULL ||
245 gaim_conversation_get_type(swboard->conv) != GAIM_CONV_TYPE_CHAT)
246 {
247 GList *l;
248
249 #ifdef MSN_DEBUG_CHAT
250 gaim_debug_info("msn", "[chat] Switching to chat.\n");
251 #endif
252
253 #if 0
254 /* this is bad - it causes msn_switchboard_close to be called on the
255 * switchboard we're in the middle of using :( */
256 if (swboard->conv != NULL)
257 gaim_conversation_destroy(swboard->conv);
258 #endif
259
260 cmdproc->session->conv_seq++;
261 swboard->chat_id = cmdproc->session->conv_seq;
262 swboard->flag |= MSN_SB_FLAG_IM;
263 swboard->conv = serv_got_joined_chat(account->gc,
264 swboard->chat_id,
265 "MSN Chat");
266
267 for (l = swboard->users; l != NULL; l = l->next)
268 {
269 const char *tmp_user;
270
271 tmp_user = l->data;
272
273 #ifdef MSN_DEBUG_CHAT
274 gaim_debug_info("msn", "[chat] Adding [%s].\n", tmp_user);
275 #endif
276
277 gaim_conv_chat_add_user(GAIM_CONV_CHAT(swboard->conv),
278 tmp_user, NULL, GAIM_CBFLAGS_NONE, TRUE);
279 }
280
281 #ifdef MSN_DEBUG_CHAT
282 gaim_debug_info("msn", "[chat] We add ourselves.\n");
283 #endif
284
285 gaim_conv_chat_add_user(GAIM_CONV_CHAT(swboard->conv),
286 gaim_account_get_username(account),
287 NULL, GAIM_CBFLAGS_NONE, TRUE);
288
289 g_free(swboard->im_user);
290 swboard->im_user = NULL;
291 }
292 }
293 else if (swboard->conv == NULL)
294 {
295 /* XXX - I think this should probably be GAIM_CONV_TYPE_CHAT, but I'm hedging */
296 swboard->conv = gaim_find_conversation_with_account(GAIM_CONV_TYPE_ANY,
297 user, account);
298 }
299 else
300 {
301 gaim_debug_warning("msn", "switchboard_add_user: This should not happen!\n");
302 }
303 }
304
305 static GaimConversation *
306 msn_switchboard_get_conv(MsnSwitchBoard *swboard)
307 {
308 GaimAccount *account;
309
310 g_return_val_if_fail(swboard != NULL, NULL);
311
312 if (swboard->conv != NULL)
313 return swboard->conv;
314
315 gaim_debug_error("msn", "Switchboard with unassigned conversation\n");
316
317 account = swboard->session->account;
318
319 /* XXX - I think this should probably be GAIM_CONV_TYPE_IM, but I'm hedging */
320 return gaim_find_conversation_with_account(GAIM_CONV_TYPE_IM,
321 swboard->im_user, account);
322 }
323
324 static void
325 msn_switchboard_report_user(MsnSwitchBoard *swboard, GaimMessageFlags flags, const char *msg)
326 {
327 GaimConversation *conv;
328
329 g_return_if_fail(swboard != NULL);
330 g_return_if_fail(msg != NULL);
331
332 if ((conv = msn_switchboard_get_conv(swboard)) != NULL)
333 {
334 gaim_conversation_write(conv, NULL, msg, flags, time(NULL));
335 }
336 }
337
338 static void
339 swboard_error_helper(MsnSwitchBoard *swboard, int reason, const char *passport)
340 {
341 g_return_if_fail(swboard != NULL);
342
343 gaim_debug_info("msg", "Error: Unable to call the user %s\n", passport);
344
345 /* TODO: if current_users > 0, this is probably a chat and an invite failed,
346 * we should report that in the chat or something */
347 if (swboard->current_users == 0)
348 {
349 swboard->error = reason;
350 msn_switchboard_close(swboard);
351 }
352 }
353
354 static void
355 cal_error_helper(MsnTransaction *trans, int reason)
356 {
357 MsnSwitchBoard *swboard;
358 const char *passport;
359 char **params;
360
361 params = g_strsplit(trans->params, " ", 0);
362
363 passport = params[0];
364
365 swboard = trans->data;
366
367 swboard_error_helper(swboard, reason, passport);
368
369 g_strfreev(params);
370 }
371
372 static void
373 msg_error_helper(MsnCmdProc *cmdproc, MsnMessage *msg, MsnMsgErrorType error)
374 {
375 MsnSwitchBoard *swboard;
376
377 g_return_if_fail(cmdproc != NULL);
378 g_return_if_fail(msg != NULL);
379
380 if ((error != MSN_MSG_ERROR_SB) && (msg->nak_cb != NULL))
381 msg->nak_cb(msg, msg->ack_data);
382
383 swboard = cmdproc->data;
384
385 /* This is not good, and should be fixed somewhere else. */
386 g_return_if_fail(swboard != NULL);
387
388 if (msg->type == MSN_MSG_TEXT)
389 {
390 const char *format, *str_reason;
391 char *body_str, *body_enc, *pre, *post;
392
393 #if 0
394 if (swboard->conv == NULL)
395 {
396 if (msg->ack_ref)
397 msn_message_unref(msg);
398
399 return;
400 }
401 #endif
402
403 if (error == MSN_MSG_ERROR_TIMEOUT)
404 {
405 str_reason = _("Message may have not been sent "
406 "because a timeout occurred:");
407 }
408 else if (error == MSN_MSG_ERROR_SB)
409 {
410 switch (swboard->error)
411 {
412 case MSN_SB_ERROR_OFFLINE:
413 str_reason = _("Message could not be sent, "
414 "not allowed while invisible:");
415 break;
416 case MSN_SB_ERROR_USER_OFFLINE:
417 str_reason = _("Message could not be sent "
418 "because the user is offline:");
419 break;
420 case MSN_SB_ERROR_CONNECTION:
421 str_reason = _("Message could not be sent "
422 "because a connection error occurred:");
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 const char *value;
903 char *passport;
904
905 gc = cmdproc->session->account->gc;
906 swboard = cmdproc->data;
907 passport = msg->remote_user;
908
909 if (swboard->current_users == 1 &&
910 (value = msn_message_get_attr(msg, "TypingUser")) != NULL)
911 {
912 serv_got_typing(gc, passport, MSN_TYPING_RECV_TIMEOUT,
913 GAIM_TYPING);
914 }
915 }
916
917 static void
918 clientcaps_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
919 {
920 #if 0
921 MsnSession *session;
922 MsnSwitchBoard *swboard;
923 MsnUser *user;
924 GHashTable *clientcaps;
925 const char *value;
926
927 char *passport = msg->sender;
928
929 session = cmdproc->session;
930 swboard = cmdproc->servconn->swboard;
931
932 clientcaps = msn_message_get_hashtable_from_body(msg);
933 #endif
934 }
935
936 static void
937 nudge_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
938 {
939 MsnSwitchBoard *swboard;
940 char *username, *str;
941 GaimAccount *account;
942 GaimBuddy *buddy;
943 const char *user;
944
945 swboard = cmdproc->data;
946 account = cmdproc->session->account;
947 user = msg->remote_user;
948
949 if ((buddy = gaim_find_buddy(account, user)) != NULL)
950 username = g_markup_escape_text(gaim_buddy_get_alias(buddy), -1);
951 else
952 username = g_markup_escape_text(user, -1);
953
954 str = g_strdup_printf(_("%s just sent you a Nudge!"), username);
955 g_free(username);
956 msn_switchboard_report_user(swboard, GAIM_MESSAGE_SYSTEM, str);
957 g_free(str);
958 }
959
960 /**************************************************************************
961 * Connect stuff
962 **************************************************************************/
963 static void
964 connect_cb(MsnServConn *servconn)
965 {
966 MsnSwitchBoard *swboard;
967 MsnCmdProc *cmdproc;
968 GaimAccount *account;
969
970 cmdproc = servconn->cmdproc;
971 g_return_if_fail(cmdproc != NULL);
972
973 account = cmdproc->session->account;
974 swboard = cmdproc->data;
975 g_return_if_fail(swboard != NULL);
976
977 if (msn_switchboard_is_invited(swboard))
978 {
979 swboard->empty = FALSE;
980
981 msn_cmdproc_send(cmdproc, "ANS", "%s %s %s",
982 gaim_account_get_username(account),
983 swboard->auth_key, swboard->session_id);
984 }
985 else
986 {
987 msn_cmdproc_send(cmdproc, "USR", "%s %s",
988 gaim_account_get_username(account),
989 swboard->auth_key);
990 }
991 }
992
993 static void
994 disconnect_cb(MsnServConn *servconn)
995 {
996 MsnSwitchBoard *swboard;
997
998 swboard = servconn->cmdproc->data;
999 g_return_if_fail(swboard != NULL);
1000
1001 msn_servconn_set_disconnect_cb(swboard->servconn, NULL);
1002
1003 msn_switchboard_destroy(swboard);
1004 }
1005
1006 gboolean
1007 msn_switchboard_connect(MsnSwitchBoard *swboard, const char *host, int port)
1008 {
1009 g_return_val_if_fail(swboard != NULL, FALSE);
1010
1011 msn_servconn_set_connect_cb(swboard->servconn, connect_cb);
1012 msn_servconn_set_disconnect_cb(swboard->servconn, disconnect_cb);
1013
1014 return msn_servconn_connect(swboard->servconn, host, port);
1015 }
1016
1017 void
1018 msn_switchboard_disconnect(MsnSwitchBoard *swboard)
1019 {
1020 g_return_if_fail(swboard != NULL);
1021
1022 msn_servconn_disconnect(swboard->servconn);
1023 }
1024
1025 /**************************************************************************
1026 * Call stuff
1027 **************************************************************************/
1028 static void
1029 got_cal(MsnCmdProc *cmdproc, MsnCommand *cmd)
1030 {
1031 #if 0
1032 MsnSwitchBoard *swboard;
1033 const char *user;
1034
1035 swboard = cmdproc->data;
1036
1037 user = cmd->params[0];
1038
1039 msn_switchboard_add_user(swboard, user);
1040 #endif
1041 }
1042
1043 static void
1044 cal_timeout(MsnCmdProc *cmdproc, MsnTransaction *trans)
1045 {
1046 cal_error_helper(trans, MSN_SB_ERROR_UNKNOWN);
1047 }
1048
1049 static void
1050 cal_error(MsnCmdProc *cmdproc, MsnTransaction *trans, int error)
1051 {
1052 int reason = MSN_SB_ERROR_UNKNOWN;
1053
1054 if (error == 215)
1055 {
1056 gaim_debug_info("msn", "Invited user already in switchboard\n");
1057 return;
1058 }
1059 else if (error == 217)
1060 {
1061 reason = MSN_SB_ERROR_USER_OFFLINE;
1062 }
1063
1064 cal_error_helper(trans, reason);
1065 }
1066
1067 void
1068 msn_switchboard_request_add_user(MsnSwitchBoard *swboard, const char *user)
1069 {
1070 MsnTransaction *trans;
1071 MsnCmdProc *cmdproc;
1072
1073 g_return_if_fail(swboard != NULL);
1074
1075 cmdproc = swboard->cmdproc;
1076
1077 trans = msn_transaction_new(cmdproc, "CAL", "%s", user);
1078 /* this doesn't do anything, but users seem to think that
1079 * 'Unhandled command' is some kind of error, so we don't report it */
1080 msn_transaction_add_cb(trans, "CAL", got_cal);
1081
1082 msn_transaction_set_data(trans, swboard);
1083 msn_transaction_set_timeout_cb(trans, cal_timeout);
1084
1085 if (swboard->ready)
1086 msn_cmdproc_send_trans(cmdproc, trans);
1087 else
1088 msn_cmdproc_queue_trans(cmdproc, trans);
1089 }
1090
1091 /**************************************************************************
1092 * Create & Transfer stuff
1093 **************************************************************************/
1094
1095 static void
1096 got_swboard(MsnCmdProc *cmdproc, MsnCommand *cmd)
1097 {
1098 MsnSwitchBoard *swboard;
1099 char *host;
1100 int port;
1101 swboard = cmd->trans->data;
1102
1103 if (g_list_find(cmdproc->session->switches, swboard) == NULL)
1104 /* The conversation window was closed. */
1105 return;
1106
1107 msn_switchboard_set_auth_key(swboard, cmd->params[4]);
1108
1109 msn_parse_socket(cmd->params[2], &host, &port);
1110
1111 if (!msn_switchboard_connect(swboard, host, port))
1112 msn_switchboard_destroy(swboard);
1113
1114 g_free(host);
1115 }
1116
1117 static void
1118 xfr_error(MsnCmdProc *cmdproc, MsnTransaction *trans, int error)
1119 {
1120 MsnSwitchBoard *swboard;
1121 int reason = MSN_SB_ERROR_UNKNOWN;
1122
1123 if (error == 913)
1124 reason = MSN_SB_ERROR_OFFLINE;
1125
1126 swboard = trans->data;
1127
1128 swboard_error_helper(swboard, reason, swboard->im_user);
1129 }
1130
1131 void
1132 msn_switchboard_request(MsnSwitchBoard *swboard)
1133 {
1134 MsnCmdProc *cmdproc;
1135 MsnTransaction *trans;
1136
1137 g_return_if_fail(swboard != NULL);
1138
1139 cmdproc = swboard->session->notification->cmdproc;
1140
1141 trans = msn_transaction_new(cmdproc, "XFR", "%s", "SB");
1142 msn_transaction_add_cb(trans, "XFR", got_swboard);
1143
1144 msn_transaction_set_data(trans, swboard);
1145 msn_transaction_set_error_cb(trans, xfr_error);
1146
1147 msn_cmdproc_send_trans(cmdproc, trans);
1148 }
1149
1150 void
1151 msn_switchboard_close(MsnSwitchBoard *swboard)
1152 {
1153 g_return_if_fail(swboard != NULL);
1154
1155 if (swboard->error != MSN_SB_ERROR_NONE)
1156 {
1157 msn_switchboard_destroy(swboard);
1158 }
1159 else if (g_queue_is_empty(swboard->msg_queue) ||
1160 !swboard->session->connected)
1161 {
1162 MsnCmdProc *cmdproc;
1163 cmdproc = swboard->cmdproc;
1164 msn_cmdproc_send_quick(cmdproc, "OUT", NULL, NULL);
1165
1166 msn_switchboard_destroy(swboard);
1167 }
1168 else
1169 {
1170 swboard->closed = TRUE;
1171 }
1172 }
1173
1174 gboolean
1175 msn_switchboard_release(MsnSwitchBoard *swboard, MsnSBFlag flag)
1176 {
1177 g_return_val_if_fail(swboard != NULL, FALSE);
1178
1179 swboard->flag &= ~flag;
1180
1181 if (flag == MSN_SB_FLAG_IM)
1182 /* Forget any conversation that used to be associated with this
1183 * swboard. */
1184 swboard->conv = NULL;
1185
1186 if (swboard->flag == 0)
1187 {
1188 msn_switchboard_close(swboard);
1189 return TRUE;
1190 }
1191
1192 return FALSE;
1193 }
1194
1195 /**************************************************************************
1196 * Init stuff
1197 **************************************************************************/
1198
1199 void
1200 msn_switchboard_init(void)
1201 {
1202 cbs_table = msn_table_new();
1203
1204 msn_table_add_cmd(cbs_table, "ANS", "ANS", ans_cmd);
1205 msn_table_add_cmd(cbs_table, "ANS", "IRO", iro_cmd);
1206
1207 msn_table_add_cmd(cbs_table, "MSG", "ACK", ack_cmd);
1208 msn_table_add_cmd(cbs_table, "MSG", "NAK", nak_cmd);
1209
1210 msn_table_add_cmd(cbs_table, "USR", "USR", usr_cmd);
1211
1212 msn_table_add_cmd(cbs_table, NULL, "MSG", msg_cmd);
1213 msn_table_add_cmd(cbs_table, NULL, "JOI", joi_cmd);
1214 msn_table_add_cmd(cbs_table, NULL, "BYE", bye_cmd);
1215 msn_table_add_cmd(cbs_table, NULL, "OUT", out_cmd);
1216
1217 #if 0
1218 /* They might skip the history */
1219 msn_table_add_cmd(cbs_table, NULL, "ACK", NULL);
1220 #endif
1221
1222 msn_table_add_error(cbs_table, "MSG", msg_error);
1223 msn_table_add_error(cbs_table, "CAL", cal_error);
1224
1225 /* Register the message type callbacks. */
1226 msn_table_add_msg_type(cbs_table, "text/plain",
1227 plain_msg);
1228 msn_table_add_msg_type(cbs_table, "text/x-msmsgscontrol",
1229 control_msg);
1230 msn_table_add_msg_type(cbs_table, "text/x-clientcaps",
1231 clientcaps_msg);
1232 msn_table_add_msg_type(cbs_table, "text/x-clientinfo",
1233 clientcaps_msg);
1234 msn_table_add_msg_type(cbs_table, "application/x-msnmsgrp2p",
1235 msn_p2p_msg);
1236 msn_table_add_msg_type(cbs_table, "text/x-mms-emoticon",
1237 msn_emoticon_msg);
1238 msn_table_add_msg_type(cbs_table, "text/x-mms-animemoticon",
1239 msn_emoticon_msg);
1240 msn_table_add_msg_type(cbs_table, "text/x-msnmsgr-datacast",
1241 nudge_msg);
1242 #if 0
1243 msn_table_add_msg_type(cbs_table, "text/x-msmmsginvite",
1244 msn_invite_msg);
1245 #endif
1246 }
1247
1248 void
1249 msn_switchboard_end(void)
1250 {
1251 msn_table_destroy(cbs_table);
1252 }

mercurial