Fix crashes when filenames end up being NULL in some prpls.
[pidgin-git.git] / libpurple / protocols / msn / slp.c
blob7ab7a8cc5bb283acd1fee2187bd5a70fc0ff8049
1 /**
2 * @file msnslp.c MSNSLP support
4 * purple
6 * Purple is the legal property of its developers, whose names are too numerous
7 * to list here. Please refer to the COPYRIGHT file distributed with this
8 * source distribution.
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.
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.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
24 #include "msn.h"
25 #include "slp.h"
26 #include "slpcall.h"
27 #include "slpmsg.h"
29 #include "object.h"
30 #include "user.h"
31 #include "switchboard.h"
33 #include "smiley.h"
35 /* ms to delay between sending buddy icon requests to the server. */
36 #define BUDDY_ICON_DELAY 20
37 /*debug SLP*/
38 #define MSN_DEBUG_UD
40 static void send_ok(MsnSlpCall *slpcall, const char *branch,
41 const char *type, const char *content);
43 static void send_decline(MsnSlpCall *slpcall, const char *branch,
44 const char *type, const char *content);
46 static void request_user_display(MsnUser *user);
48 /**************************************************************************
49 * Util
50 **************************************************************************/
52 static char *
53 get_token(const char *str, const char *start, const char *end)
55 const char *c, *c2;
57 if ((c = strstr(str, start)) == NULL)
58 return NULL;
60 c += strlen(start);
62 if (end != NULL)
64 if ((c2 = strstr(c, end)) == NULL)
65 return NULL;
67 return g_strndup(c, c2 - c);
69 else
71 /* This has to be changed */
72 return g_strdup(c);
77 /**************************************************************************
78 * Xfer
79 **************************************************************************/
81 static void
82 msn_xfer_init(PurpleXfer *xfer)
84 MsnSlpCall *slpcall;
85 /* MsnSlpLink *slplink; */
86 char *content;
88 purple_debug_info("msn", "xfer_init\n");
90 slpcall = xfer->data;
92 /* Send Ok */
93 content = g_strdup_printf("SessionID: %lu\r\n\r\n",
94 slpcall->session_id);
96 send_ok(slpcall, slpcall->branch, "application/x-msnmsgr-sessionreqbody",
97 content);
99 g_free(content);
100 msn_slplink_send_queued_slpmsgs(slpcall->slplink);
103 void
104 msn_xfer_cancel(PurpleXfer *xfer)
106 MsnSlpCall *slpcall;
107 char *content;
109 g_return_if_fail(xfer != NULL);
110 g_return_if_fail(xfer->data != NULL);
112 slpcall = xfer->data;
114 if (purple_xfer_get_status(xfer) == PURPLE_XFER_STATUS_CANCEL_LOCAL)
116 if (slpcall->started)
118 msn_slpcall_close(slpcall);
120 else
122 content = g_strdup_printf("SessionID: %lu\r\n\r\n",
123 slpcall->session_id);
125 send_decline(slpcall, slpcall->branch, "application/x-msnmsgr-sessionreqbody",
126 content);
128 g_free(content);
129 msn_slplink_send_queued_slpmsgs(slpcall->slplink);
131 msn_slpcall_destroy(slpcall);
136 void
137 msn_xfer_progress_cb(MsnSlpCall *slpcall, gsize total_length, gsize len, gsize offset)
139 PurpleXfer *xfer;
141 xfer = slpcall->xfer;
143 xfer->bytes_sent = (offset + len);
144 xfer->bytes_remaining = total_length - (offset + len);
146 purple_xfer_update_progress(xfer);
149 void
150 msn_xfer_end_cb(MsnSlpCall *slpcall, MsnSession *session)
152 if ((purple_xfer_get_status(slpcall->xfer) != PURPLE_XFER_STATUS_DONE) &&
153 (purple_xfer_get_status(slpcall->xfer) != PURPLE_XFER_STATUS_CANCEL_REMOTE) &&
154 (purple_xfer_get_status(slpcall->xfer) != PURPLE_XFER_STATUS_CANCEL_LOCAL))
156 purple_xfer_cancel_remote(slpcall->xfer);
160 void
161 msn_xfer_completed_cb(MsnSlpCall *slpcall, const guchar *body,
162 gsize size)
164 PurpleXfer *xfer = slpcall->xfer;
165 purple_xfer_set_completed(xfer, TRUE);
166 purple_xfer_end(xfer);
169 /**************************************************************************
170 * SLP Control
171 **************************************************************************/
173 #if 0
174 static void
175 got_transresp(MsnSlpCall *slpcall, const char *nonce,
176 const char *ips_str, int port)
178 MsnDirectConn *directconn;
179 char **ip_addrs, **c;
181 directconn = msn_directconn_new(slpcall->slplink);
183 directconn->initial_call = slpcall;
185 /* msn_directconn_parse_nonce(directconn, nonce); */
186 directconn->nonce = g_strdup(nonce);
188 ip_addrs = g_strsplit(ips_str, " ", -1);
190 for (c = ip_addrs; *c != NULL; c++)
192 purple_debug_info("msn", "ip_addr = %s\n", *c);
193 if (msn_directconn_connect(directconn, *c, port))
194 break;
197 g_strfreev(ip_addrs);
199 #endif
201 static void
202 send_ok(MsnSlpCall *slpcall, const char *branch,
203 const char *type, const char *content)
205 MsnSlpLink *slplink;
206 MsnSlpMessage *slpmsg;
208 slplink = slpcall->slplink;
210 /* 200 OK */
211 slpmsg = msn_slpmsg_sip_new(slpcall, 1,
212 "MSNSLP/1.0 200 OK",
213 branch, type, content);
215 #ifdef MSN_DEBUG_SLP
216 slpmsg->info = "SLP 200 OK";
217 slpmsg->text_body = TRUE;
218 #endif
220 msn_slplink_queue_slpmsg(slplink, slpmsg);
222 msn_slpcall_session_init(slpcall);
225 static void
226 send_decline(MsnSlpCall *slpcall, const char *branch,
227 const char *type, const char *content)
229 MsnSlpLink *slplink;
230 MsnSlpMessage *slpmsg;
232 slplink = slpcall->slplink;
234 /* 603 Decline */
235 slpmsg = msn_slpmsg_sip_new(slpcall, 1,
236 "MSNSLP/1.0 603 Decline",
237 branch, type, content);
239 #ifdef MSN_DEBUG_SLP
240 slpmsg->info = "SLP 603 Decline";
241 slpmsg->text_body = TRUE;
242 #endif
244 msn_slplink_queue_slpmsg(slplink, slpmsg);
247 #define MAX_FILE_NAME_LEN 0x226
249 static void
250 got_sessionreq(MsnSlpCall *slpcall, const char *branch,
251 const char *euf_guid, const char *context)
253 if (!strcmp(euf_guid, MSN_OBJ_GUID))
255 /* Emoticon or UserDisplay */
256 char *content;
257 gsize len;
258 MsnSlpLink *slplink;
259 MsnSlpMessage *slpmsg;
260 MsnObject *obj;
261 char *msnobj_data;
262 PurpleStoredImage *img;
263 int type;
265 /* Send Ok */
266 content = g_strdup_printf("SessionID: %lu\r\n\r\n",
267 slpcall->session_id);
269 send_ok(slpcall, branch, "application/x-msnmsgr-sessionreqbody",
270 content);
272 g_free(content);
274 slplink = slpcall->slplink;
276 msnobj_data = (char *)purple_base64_decode(context, &len);
277 obj = msn_object_new_from_string(msnobj_data);
278 type = msn_object_get_type(obj);
279 g_free(msnobj_data);
281 if ((type != MSN_OBJECT_USERTILE) && (type != MSN_OBJECT_EMOTICON))
283 purple_debug_error("msn", "Wrong object?\n");
284 msn_object_destroy(obj);
285 g_return_if_reached();
288 if (type == MSN_OBJECT_EMOTICON) {
289 char *path;
290 path = g_build_filename(purple_smileys_get_storing_dir(),
291 obj->location, NULL);
292 img = purple_imgstore_new_from_file(path);
293 g_free(path);
294 } else {
295 img = msn_object_get_image(obj);
296 if (img)
297 purple_imgstore_ref(img);
299 msn_object_destroy(obj);
301 if (img == NULL)
303 purple_debug_error("msn", "Wrong object.\n");
304 g_return_if_reached();
307 /* DATA PREP */
308 slpmsg = msn_slpmsg_new(slplink);
309 slpmsg->slpcall = slpcall;
310 slpmsg->session_id = slpcall->session_id;
311 msn_slpmsg_set_body(slpmsg, NULL, 4);
312 #ifdef MSN_DEBUG_SLP
313 slpmsg->info = "SLP DATA PREP";
314 #endif
315 msn_slplink_queue_slpmsg(slplink, slpmsg);
317 /* DATA */
318 slpmsg = msn_slpmsg_new(slplink);
319 slpmsg->slpcall = slpcall;
320 slpmsg->flags = 0x20;
321 #ifdef MSN_DEBUG_SLP
322 slpmsg->info = "SLP DATA";
323 #endif
324 msn_slpmsg_set_image(slpmsg, img);
325 msn_slplink_queue_slpmsg(slplink, slpmsg);
326 purple_imgstore_unref(img);
328 else if (!strcmp(euf_guid, MSN_FT_GUID))
330 /* File Transfer */
331 PurpleAccount *account;
332 PurpleXfer *xfer;
333 char *bin;
334 gsize bin_len;
335 guint32 file_size;
336 char *file_name;
337 gunichar2 *uni_name;
339 account = slpcall->slplink->session->account;
341 slpcall->cb = msn_xfer_completed_cb;
342 slpcall->end_cb = msn_xfer_end_cb;
343 slpcall->progress_cb = msn_xfer_progress_cb;
344 slpcall->branch = g_strdup(branch);
346 slpcall->pending = TRUE;
348 xfer = purple_xfer_new(account, PURPLE_XFER_RECEIVE,
349 slpcall->slplink->remote_user);
350 if (xfer)
352 bin = (char *)purple_base64_decode(context, &bin_len);
353 file_size = GUINT32_FROM_LE(*(gsize *)(bin + 8));
355 uni_name = (gunichar2 *)(bin + 20);
356 while(*uni_name != 0 && ((char *)uni_name - (bin + 20)) < MAX_FILE_NAME_LEN) {
357 *uni_name = GUINT16_FROM_LE(*uni_name);
358 uni_name++;
361 file_name = g_utf16_to_utf8((const gunichar2 *)(bin + 20), -1,
362 NULL, NULL, NULL);
364 g_free(bin);
366 purple_xfer_set_filename(xfer, file_name ? file_name : "");
367 g_free(file_name);
368 purple_xfer_set_size(xfer, file_size);
369 purple_xfer_set_init_fnc(xfer, msn_xfer_init);
370 purple_xfer_set_request_denied_fnc(xfer, msn_xfer_cancel);
371 purple_xfer_set_cancel_recv_fnc(xfer, msn_xfer_cancel);
373 slpcall->xfer = xfer;
374 purple_xfer_ref(slpcall->xfer);
376 xfer->data = slpcall;
378 purple_xfer_request(xfer);
380 } else
381 purple_debug_warning("msn", "SLP SessionReq with unknown EUF-GUID: %s\n", euf_guid);
384 void
385 send_bye(MsnSlpCall *slpcall, const char *type)
387 MsnSlpLink *slplink;
388 PurpleAccount *account;
389 MsnSlpMessage *slpmsg;
390 char *header;
392 slplink = slpcall->slplink;
394 g_return_if_fail(slplink != NULL);
396 account = slplink->session->account;
398 header = g_strdup_printf("BYE MSNMSGR:%s MSNSLP/1.0",
399 purple_account_get_username(account));
401 slpmsg = msn_slpmsg_sip_new(slpcall, 0, header,
402 "A0D624A6-6C0C-4283-A9E0-BC97B4B46D32",
403 type,
404 "\r\n");
405 g_free(header);
407 #ifdef MSN_DEBUG_SLP
408 slpmsg->info = "SLP BYE";
409 slpmsg->text_body = TRUE;
410 #endif
412 msn_slplink_queue_slpmsg(slplink, slpmsg);
415 static void
416 got_invite(MsnSlpCall *slpcall,
417 const char *branch, const char *type, const char *content)
419 MsnSlpLink *slplink;
421 slplink = slpcall->slplink;
423 if (!strcmp(type, "application/x-msnmsgr-sessionreqbody"))
425 char *euf_guid, *context;
426 char *temp;
428 euf_guid = get_token(content, "EUF-GUID: {", "}\r\n");
430 temp = get_token(content, "SessionID: ", "\r\n");
431 if (temp != NULL)
432 slpcall->session_id = atoi(temp);
433 g_free(temp);
435 temp = get_token(content, "AppID: ", "\r\n");
436 if (temp != NULL)
437 slpcall->app_id = atoi(temp);
438 g_free(temp);
440 context = get_token(content, "Context: ", "\r\n");
442 if (context != NULL)
443 got_sessionreq(slpcall, branch, euf_guid, context);
445 g_free(context);
446 g_free(euf_guid);
448 else if (!strcmp(type, "application/x-msnmsgr-transreqbody"))
450 /* A direct connection? */
452 char *listening, *nonce;
453 char *content;
455 if (FALSE)
457 #if 0
458 MsnDirectConn *directconn;
459 /* const char *ip_addr; */
460 char *ip_port;
461 int port;
463 /* ip_addr = purple_prefs_get_string("/purple/ft/public_ip"); */
464 ip_port = "5190";
465 listening = "true";
466 nonce = rand_guid();
468 directconn = msn_directconn_new(slplink);
470 /* msn_directconn_parse_nonce(directconn, nonce); */
471 directconn->nonce = g_strdup(nonce);
473 msn_directconn_listen(directconn);
475 port = directconn->port;
477 content = g_strdup_printf(
478 "Bridge: TCPv1\r\n"
479 "Listening: %s\r\n"
480 "Nonce: {%s}\r\n"
481 "Ipv4Internal-Addrs: 192.168.0.82\r\n"
482 "Ipv4Internal-Port: %d\r\n"
483 "\r\n",
484 listening,
485 nonce,
486 port);
487 #endif
489 else
491 listening = "false";
492 nonce = g_strdup("00000000-0000-0000-0000-000000000000");
494 content = g_strdup_printf(
495 "Bridge: TCPv1\r\n"
496 "Listening: %s\r\n"
497 "Nonce: {%s}\r\n"
498 "\r\n",
499 listening,
500 nonce);
503 send_ok(slpcall, branch,
504 "application/x-msnmsgr-transrespbody", content);
506 g_free(content);
507 g_free(nonce);
509 else if (!strcmp(type, "application/x-msnmsgr-transrespbody"))
511 #if 0
512 char *ip_addrs;
513 char *temp;
514 char *nonce;
515 int port;
517 nonce = get_token(content, "Nonce: {", "}\r\n");
518 if (ip_addrs == NULL)
519 return;
521 ip_addrs = get_token(content, "IPv4Internal-Addrs: ", "\r\n");
523 temp = get_token(content, "IPv4Internal-Port: ", "\r\n");
524 if (temp != NULL)
525 port = atoi(temp);
526 else
527 port = -1;
528 g_free(temp);
530 if (port > 0)
531 got_transresp(slpcall, nonce, ip_addrs, port);
533 g_free(nonce);
534 g_free(ip_addrs);
535 #endif
539 static void
540 got_ok(MsnSlpCall *slpcall,
541 const char *type, const char *content)
543 g_return_if_fail(slpcall != NULL);
544 g_return_if_fail(type != NULL);
546 if (!strcmp(type, "application/x-msnmsgr-sessionreqbody"))
548 #if 0
549 if (slpcall->type == MSN_SLPCALL_DC)
551 /* First let's try a DirectConnection. */
553 MsnSlpLink *slplink;
554 MsnSlpMessage *slpmsg;
555 char *header;
556 char *content;
557 char *branch;
559 slplink = slpcall->slplink;
561 branch = rand_guid();
563 content = g_strdup_printf(
564 "Bridges: TRUDPv1 TCPv1\r\n"
565 "NetID: 0\r\n"
566 "Conn-Type: Direct-Connect\r\n"
567 "UPnPNat: false\r\n"
568 "ICF: false\r\n"
571 header = g_strdup_printf("INVITE MSNMSGR:%s MSNSLP/1.0",
572 slplink->remote_user);
574 slpmsg = msn_slp_sipmsg_new(slpcall, 0, header, branch,
575 "application/x-msnmsgr-transreqbody",
576 content);
578 #ifdef MSN_DEBUG_SLP
579 slpmsg->info = "SLP INVITE";
580 slpmsg->text_body = TRUE;
581 #endif
582 msn_slplink_send_slpmsg(slplink, slpmsg);
584 g_free(header);
585 g_free(content);
587 g_free(branch);
589 else
591 msn_slpcall_session_init(slpcall);
593 #else
594 msn_slpcall_session_init(slpcall);
595 #endif
597 else if (!strcmp(type, "application/x-msnmsgr-transreqbody"))
599 /* Do we get this? */
600 purple_debug_info("msn", "OK with transreqbody\n");
602 else if (!strcmp(type, "application/x-msnmsgr-transrespbody"))
604 #if 0
605 char *ip_addrs;
606 char *temp;
607 char *nonce;
608 int port;
610 nonce = get_token(content, "Nonce: {", "}\r\n");
611 if (ip_addrs == NULL)
612 return;
614 ip_addrs = get_token(content, "IPv4Internal-Addrs: ", "\r\n");
616 temp = get_token(content, "IPv4Internal-Port: ", "\r\n");
617 if (temp != NULL)
618 port = atoi(temp);
619 else
620 port = -1;
621 g_free(temp);
623 if (port > 0)
624 got_transresp(slpcall, nonce, ip_addrs, port);
626 g_free(nonce);
627 g_free(ip_addrs);
628 #endif
632 MsnSlpCall *
633 msn_slp_sip_recv(MsnSlpLink *slplink, const char *body)
635 MsnSlpCall *slpcall;
637 if (body == NULL)
639 purple_debug_warning("msn", "received bogus message\n");
640 return NULL;
643 if (!strncmp(body, "INVITE", strlen("INVITE")))
645 char *branch;
646 char *content;
647 char *content_type;
649 slpcall = msn_slpcall_new(slplink);
651 /* From: <msnmsgr:buddy@hotmail.com> */
652 #if 0
653 slpcall->remote_user = get_token(body, "From: <msnmsgr:", ">\r\n");
654 #endif
656 branch = get_token(body, ";branch={", "}");
658 slpcall->id = get_token(body, "Call-ID: {", "}");
660 #if 0
661 long content_len = -1;
663 temp = get_token(body, "Content-Length: ", "\r\n");
664 if (temp != NULL)
665 content_len = atoi(temp);
666 g_free(temp);
667 #endif
668 content_type = get_token(body, "Content-Type: ", "\r\n");
670 content = get_token(body, "\r\n\r\n", NULL);
672 got_invite(slpcall, branch, content_type, content);
674 g_free(branch);
675 g_free(content_type);
676 g_free(content);
678 else if (!strncmp(body, "MSNSLP/1.0 ", strlen("MSNSLP/1.0 ")))
680 char *content;
681 char *content_type;
682 /* Make sure this is "OK" */
683 const char *status = body + strlen("MSNSLP/1.0 ");
684 char *call_id;
686 call_id = get_token(body, "Call-ID: {", "}");
687 slpcall = msn_slplink_find_slp_call(slplink, call_id);
688 g_free(call_id);
690 g_return_val_if_fail(slpcall != NULL, NULL);
692 if (strncmp(status, "200 OK", 6))
694 /* It's not valid. Kill this off. */
695 char temp[32];
696 const char *c;
698 /* Eww */
699 if ((c = strchr(status, '\r')) || (c = strchr(status, '\n')) ||
700 (c = strchr(status, '\0')))
702 size_t offset = c - status;
703 if (offset >= sizeof(temp))
704 offset = sizeof(temp) - 1;
706 strncpy(temp, status, offset);
707 temp[offset] = '\0';
710 purple_debug_error("msn", "Received non-OK result: %s\n", temp);
712 slpcall->wasted = TRUE;
714 /* msn_slpcall_destroy(slpcall); */
715 return slpcall;
718 content_type = get_token(body, "Content-Type: ", "\r\n");
720 content = get_token(body, "\r\n\r\n", NULL);
722 got_ok(slpcall, content_type, content);
724 g_free(content_type);
725 g_free(content);
727 else if (!strncmp(body, "BYE", strlen("BYE")))
729 char *call_id;
731 call_id = get_token(body, "Call-ID: {", "}");
732 slpcall = msn_slplink_find_slp_call(slplink, call_id);
733 g_free(call_id);
735 if (slpcall != NULL)
736 slpcall->wasted = TRUE;
738 /* msn_slpcall_destroy(slpcall); */
740 else
741 slpcall = NULL;
743 return slpcall;
746 /**************************************************************************
747 * Msg Callbacks
748 **************************************************************************/
750 void
751 msn_p2p_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
753 MsnSession *session;
754 MsnSlpLink *slplink;
756 session = cmdproc->servconn->session;
757 slplink = msn_session_get_slplink(session, msg->remote_user);
759 if (slplink->swboard == NULL)
762 * We will need swboard in order to change its flags. If its
763 * NULL, something has probably gone wrong earlier on. I
764 * didn't want to do this, but MSN 7 is somehow causing us
765 * to crash here, I couldn't reproduce it to debug more,
766 * and people are reporting bugs. Hopefully this doesn't
767 * cause more crashes. Stu.
769 if (cmdproc->data == NULL)
770 g_warning("msn_p2p_msg cmdproc->data was NULL\n");
771 else {
772 slplink->swboard = (MsnSwitchBoard *)cmdproc->data;
773 slplink->swboard->slplinks = g_list_prepend(slplink->swboard->slplinks, slplink);
777 msn_slplink_process_msg(slplink, msg);
780 static void
781 got_emoticon(MsnSlpCall *slpcall,
782 const guchar *data, gsize size)
784 PurpleConversation *conv;
785 MsnSwitchBoard *swboard;
787 swboard = slpcall->slplink->swboard;
788 conv = swboard->conv;
790 if (conv) {
791 /* FIXME: it would be better if we wrote the data as we received it
792 instead of all at once, calling write multiple times and
793 close once at the very end
795 purple_conv_custom_smiley_write(conv, slpcall->data_info, data, size);
796 purple_conv_custom_smiley_close(conv, slpcall->data_info );
798 #ifdef MSN_DEBUG_UD
799 purple_debug_info("msn", "Got smiley: %s\n", slpcall->data_info);
800 #endif
803 void
804 msn_emoticon_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
806 MsnSession *session;
807 MsnSlpLink *slplink;
808 MsnSwitchBoard *swboard;
809 MsnObject *obj;
810 char **tokens;
811 char *smile, *body_str;
812 const char *body, *who, *sha1;
813 guint tok;
814 size_t body_len;
816 PurpleConversation *conv;
818 session = cmdproc->servconn->session;
820 if (!purple_account_get_bool(session->account, "custom_smileys", TRUE))
821 return;
823 swboard = cmdproc->data;
824 conv = swboard->conv;
826 body = msn_message_get_bin_data(msg, &body_len);
827 body_str = g_strndup(body, body_len);
829 /* MSN Messenger 7 may send more than one MSNObject in a single message...
830 * Maybe 10 tokens is a reasonable max value. */
831 tokens = g_strsplit(body_str, "\t", 10);
833 g_free(body_str);
835 for (tok = 0; tok < 9; tok += 2) {
836 if (tokens[tok] == NULL || tokens[tok + 1] == NULL) {
837 break;
840 smile = tokens[tok];
841 obj = msn_object_new_from_string(purple_url_decode(tokens[tok + 1]));
843 if (obj == NULL)
844 break;
846 who = msn_object_get_creator(obj);
847 sha1 = msn_object_get_sha1(obj);
849 slplink = msn_session_get_slplink(session, who);
850 if (slplink->swboard != swboard) {
851 if (slplink->swboard != NULL)
853 * Apparently we're using a different switchboard now or
854 * something? I don't know if this is normal, but it
855 * definitely happens. So make sure the old switchboard
856 * doesn't still have a reference to us.
858 slplink->swboard->slplinks = g_list_remove(slplink->swboard->slplinks, slplink);
859 slplink->swboard = swboard;
860 slplink->swboard->slplinks = g_list_prepend(slplink->swboard->slplinks, slplink);
863 /* If the conversation doesn't exist then this is a custom smiley
864 * used in the first message in a MSN conversation: we need to create
865 * the conversation now, otherwise the custom smiley won't be shown.
866 * This happens because every GtkIMHtml has its own smiley tree: if
867 * the conversation doesn't exist then we cannot associate the new
868 * smiley with its GtkIMHtml widget. */
869 if (!conv) {
870 conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, session->account, who);
873 if (purple_conv_custom_smiley_add(conv, smile, "sha1", sha1, TRUE)) {
874 msn_slplink_request_object(slplink, smile, got_emoticon, NULL, obj);
877 msn_object_destroy(obj);
878 obj = NULL;
879 who = NULL;
880 sha1 = NULL;
882 g_strfreev(tokens);
885 static gboolean
886 buddy_icon_cached(PurpleConnection *gc, MsnObject *obj)
888 PurpleAccount *account;
889 PurpleBuddy *buddy;
890 const char *old;
891 const char *new;
893 g_return_val_if_fail(obj != NULL, FALSE);
895 account = purple_connection_get_account(gc);
897 buddy = purple_find_buddy(account, msn_object_get_creator(obj));
898 if (buddy == NULL)
899 return FALSE;
901 old = purple_buddy_icons_get_checksum_for_user(buddy);
902 new = msn_object_get_sha1(obj);
904 if (new == NULL)
905 return FALSE;
907 /* If the old and new checksums are the same, and the file actually exists,
908 * then return TRUE */
909 if (old != NULL && !strcmp(old, new))
910 return TRUE;
912 return FALSE;
915 static void
916 msn_release_buddy_icon_request(MsnUserList *userlist)
918 MsnUser *user;
920 g_return_if_fail(userlist != NULL);
922 #ifdef MSN_DEBUG_UD
923 purple_debug_info("msn", "Releasing buddy icon request\n");
924 #endif
926 if (userlist->buddy_icon_window > 0)
928 GQueue *queue;
929 PurpleAccount *account;
930 const char *username;
932 queue = userlist->buddy_icon_requests;
934 if (g_queue_is_empty(userlist->buddy_icon_requests))
935 return;
937 user = g_queue_pop_head(queue);
939 account = userlist->session->account;
940 username = user->passport;
942 userlist->buddy_icon_window--;
943 request_user_display(user);
945 #ifdef MSN_DEBUG_UD
946 purple_debug_info("msn", "msn_release_buddy_icon_request(): buddy_icon_window-- yields =%d\n",
947 userlist->buddy_icon_window);
948 #endif
953 * Called on a timeout from end_user_display(). Frees a buddy icon window slow and dequeues the next
954 * buddy icon request if there is one.
956 static gboolean
957 msn_release_buddy_icon_request_timeout(gpointer data)
959 MsnUserList *userlist = (MsnUserList *)data;
961 /* Free one window slot */
962 userlist->buddy_icon_window++;
964 /* Clear the tag for our former request timer */
965 userlist->buddy_icon_request_timer = 0;
967 msn_release_buddy_icon_request(userlist);
969 return FALSE;
972 void
973 msn_queue_buddy_icon_request(MsnUser *user)
975 PurpleAccount *account;
976 MsnObject *obj;
977 GQueue *queue;
979 g_return_if_fail(user != NULL);
981 account = user->userlist->session->account;
983 obj = msn_user_get_object(user);
985 if (obj == NULL)
987 purple_buddy_icons_set_for_user(account, user->passport, NULL, 0, NULL);
988 return;
991 if (!buddy_icon_cached(account->gc, obj))
993 MsnUserList *userlist;
995 userlist = user->userlist;
996 queue = userlist->buddy_icon_requests;
998 #ifdef MSN_DEBUG_UD
999 purple_debug_info("msn", "Queueing buddy icon request for %s (buddy_icon_window = %i)\n",
1000 user->passport, userlist->buddy_icon_window);
1001 #endif
1003 g_queue_push_tail(queue, user);
1005 if (userlist->buddy_icon_window > 0)
1006 msn_release_buddy_icon_request(userlist);
1010 static void
1011 got_user_display(MsnSlpCall *slpcall,
1012 const guchar *data, gsize size)
1014 MsnUserList *userlist;
1015 const char *info;
1016 PurpleAccount *account;
1018 g_return_if_fail(slpcall != NULL);
1020 info = slpcall->data_info;
1021 #ifdef MSN_DEBUG_UD
1022 purple_debug_info("msn", "Got User Display: %s\n", slpcall->slplink->remote_user);
1023 #endif
1025 userlist = slpcall->slplink->session->userlist;
1026 account = slpcall->slplink->session->account;
1028 purple_buddy_icons_set_for_user(account, slpcall->slplink->remote_user,
1029 g_memdup(data, size), size, info);
1031 #if 0
1032 /* Free one window slot */
1033 userlist->buddy_icon_window++;
1035 purple_debug_info("msn", "got_user_display(): buddy_icon_window++ yields =%d\n",
1036 userlist->buddy_icon_window);
1038 msn_release_buddy_icon_request(userlist);
1039 #endif
1042 static void
1043 end_user_display(MsnSlpCall *slpcall, MsnSession *session)
1045 MsnUserList *userlist;
1047 g_return_if_fail(session != NULL);
1049 #ifdef MSN_DEBUG_UD
1050 purple_debug_info("msn", "End User Display\n");
1051 #endif
1053 userlist = session->userlist;
1055 /* If the session is being destroyed we better stop doing anything. */
1056 if (session->destroying)
1057 return;
1059 /* Delay before freeing a buddy icon window slot and requesting the next icon, if appropriate.
1060 * If we don't delay, we'll rapidly hit the MSN equivalent of AIM's rate limiting; the server will
1061 * send us an error 800 like so:
1063 * C: NS 000: XFR 21 SB
1064 * S: NS 000: 800 21
1066 if (userlist->buddy_icon_request_timer) {
1067 /* Free the window slot used by this previous request */
1068 userlist->buddy_icon_window++;
1070 /* Clear our pending timeout */
1071 purple_timeout_remove(userlist->buddy_icon_request_timer);
1074 /* Wait BUDDY_ICON_DELAY s before freeing our window slot and requesting the next icon. */
1075 userlist->buddy_icon_request_timer = purple_timeout_add_seconds(BUDDY_ICON_DELAY,
1076 msn_release_buddy_icon_request_timeout, userlist);
1079 static void
1080 request_user_display(MsnUser *user)
1082 PurpleAccount *account;
1083 MsnSession *session;
1084 MsnSlpLink *slplink;
1085 MsnObject *obj;
1086 const char *info;
1088 session = user->userlist->session;
1089 account = session->account;
1091 slplink = msn_session_get_slplink(session, user->passport);
1093 obj = msn_user_get_object(user);
1095 info = msn_object_get_sha1(obj);
1097 if (g_ascii_strcasecmp(user->passport,
1098 purple_account_get_username(account)))
1100 msn_slplink_request_object(slplink, info, got_user_display,
1101 end_user_display, obj);
1103 else
1105 MsnObject *my_obj = NULL;
1106 gconstpointer data = NULL;
1107 size_t len = 0;
1109 #ifdef MSN_DEBUG_UD
1110 purple_debug_info("msn", "Requesting our own user display\n");
1111 #endif
1113 my_obj = msn_user_get_object(session->user);
1115 if (my_obj != NULL)
1117 PurpleStoredImage *img = msn_object_get_image(my_obj);
1118 data = purple_imgstore_get_data(img);
1119 len = purple_imgstore_get_size(img);
1122 purple_buddy_icons_set_for_user(account, user->passport, g_memdup(data, len), len, info);
1124 /* Free one window slot */
1125 session->userlist->buddy_icon_window++;
1127 #ifdef MSN_DEBUG_UD
1128 purple_debug_info("msn", "request_user_display(): buddy_icon_window++ yields =%d\n",
1129 session->userlist->buddy_icon_window);
1130 #endif
1132 msn_release_buddy_icon_request(session->userlist);