comparison libpurple/protocols/msn/slp.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 msnslp.c MSNSLP support
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 "slp.h"
26 #include "slpcall.h"
27 #include "slpmsg.h"
28 #include "slpsession.h"
29
30 #include "object.h"
31 #include "user.h"
32 #include "switchboard.h"
33
34 /* ms to delay between sending buddy icon requests to the server. */
35 #define BUDDY_ICON_DELAY 20000
36
37 static void send_ok(MsnSlpCall *slpcall, const char *branch,
38 const char *type, const char *content);
39
40 static void send_decline(MsnSlpCall *slpcall, const char *branch,
41 const char *type, const char *content);
42
43 void msn_request_user_display(MsnUser *user);
44
45 /**************************************************************************
46 * Util
47 **************************************************************************/
48
49 static char *
50 get_token(const char *str, const char *start, const char *end)
51 {
52 const char *c, *c2;
53
54 if ((c = strstr(str, start)) == NULL)
55 return NULL;
56
57 c += strlen(start);
58
59 if (end != NULL)
60 {
61 if ((c2 = strstr(c, end)) == NULL)
62 return NULL;
63
64 return g_strndup(c, c2 - c);
65 }
66 else
67 {
68 /* This has to be changed */
69 return g_strdup(c);
70 }
71
72 }
73
74 /**************************************************************************
75 * Xfer
76 **************************************************************************/
77
78 static void
79 msn_xfer_init(GaimXfer *xfer)
80 {
81 MsnSlpCall *slpcall;
82 /* MsnSlpLink *slplink; */
83 char *content;
84
85 gaim_debug_info("msn", "xfer_init\n");
86
87 slpcall = xfer->data;
88
89 /* Send Ok */
90 content = g_strdup_printf("SessionID: %lu\r\n\r\n",
91 slpcall->session_id);
92
93 send_ok(slpcall, slpcall->branch, "application/x-msnmsgr-sessionreqbody",
94 content);
95
96 g_free(content);
97 msn_slplink_unleash(slpcall->slplink);
98 }
99
100 void
101 msn_xfer_cancel(GaimXfer *xfer)
102 {
103 MsnSlpCall *slpcall;
104 char *content;
105
106 g_return_if_fail(xfer != NULL);
107 g_return_if_fail(xfer->data != NULL);
108
109 slpcall = xfer->data;
110
111 if (gaim_xfer_get_status(xfer) == GAIM_XFER_STATUS_CANCEL_LOCAL)
112 {
113 if (slpcall->started)
114 {
115 msn_slp_call_close(slpcall);
116 }
117 else
118 {
119 content = g_strdup_printf("SessionID: %lu\r\n\r\n",
120 slpcall->session_id);
121
122 send_decline(slpcall, slpcall->branch, "application/x-msnmsgr-sessionreqbody",
123 content);
124
125 g_free(content);
126 msn_slplink_unleash(slpcall->slplink);
127
128 msn_slp_call_destroy(slpcall);
129 }
130 }
131 }
132
133 void
134 msn_xfer_progress_cb(MsnSlpCall *slpcall, gsize total_length, gsize len, gsize offset)
135 {
136 GaimXfer *xfer;
137
138 xfer = slpcall->xfer;
139
140 xfer->bytes_sent = (offset + len);
141 xfer->bytes_remaining = total_length - (offset + len);
142
143 gaim_xfer_update_progress(xfer);
144 }
145
146 void
147 msn_xfer_end_cb(MsnSlpCall *slpcall, MsnSession *session)
148 {
149 if ((gaim_xfer_get_status(slpcall->xfer) != GAIM_XFER_STATUS_DONE) &&
150 (gaim_xfer_get_status(slpcall->xfer) != GAIM_XFER_STATUS_CANCEL_REMOTE) &&
151 (gaim_xfer_get_status(slpcall->xfer) != GAIM_XFER_STATUS_CANCEL_LOCAL))
152 {
153 gaim_xfer_cancel_remote(slpcall->xfer);
154 }
155 }
156
157 void
158 msn_xfer_completed_cb(MsnSlpCall *slpcall, const guchar *body,
159 gsize size)
160 {
161 gaim_xfer_set_completed(slpcall->xfer, TRUE);
162 }
163
164 /**************************************************************************
165 * SLP Control
166 **************************************************************************/
167
168 #if 0
169 static void
170 got_transresp(MsnSlpCall *slpcall, const char *nonce,
171 const char *ips_str, int port)
172 {
173 MsnDirectConn *directconn;
174 char **ip_addrs, **c;
175
176 directconn = msn_directconn_new(slpcall->slplink);
177
178 directconn->initial_call = slpcall;
179
180 /* msn_directconn_parse_nonce(directconn, nonce); */
181 directconn->nonce = g_strdup(nonce);
182
183 ip_addrs = g_strsplit(ips_str, " ", -1);
184
185 for (c = ip_addrs; *c != NULL; c++)
186 {
187 gaim_debug_info("msn", "ip_addr = %s\n", *c);
188 if (msn_directconn_connect(directconn, *c, port))
189 break;
190 }
191
192 g_strfreev(ip_addrs);
193 }
194 #endif
195
196 static void
197 send_ok(MsnSlpCall *slpcall, const char *branch,
198 const char *type, const char *content)
199 {
200 MsnSlpLink *slplink;
201 MsnSlpMessage *slpmsg;
202
203 slplink = slpcall->slplink;
204
205 /* 200 OK */
206 slpmsg = msn_slpmsg_sip_new(slpcall, 1,
207 "MSNSLP/1.0 200 OK",
208 branch, type, content);
209
210 #ifdef MSN_DEBUG_SLP
211 slpmsg->info = "SLP 200 OK";
212 slpmsg->text_body = TRUE;
213 #endif
214
215 msn_slplink_queue_slpmsg(slplink, slpmsg);
216
217 msn_slp_call_session_init(slpcall);
218 }
219
220 static void
221 send_decline(MsnSlpCall *slpcall, const char *branch,
222 const char *type, const char *content)
223 {
224 MsnSlpLink *slplink;
225 MsnSlpMessage *slpmsg;
226
227 slplink = slpcall->slplink;
228
229 /* 603 Decline */
230 slpmsg = msn_slpmsg_sip_new(slpcall, 1,
231 "MSNSLP/1.0 603 Decline",
232 branch, type, content);
233
234 #ifdef MSN_DEBUG_SLP
235 slpmsg->info = "SLP 603 Decline";
236 slpmsg->text_body = TRUE;
237 #endif
238
239 msn_slplink_queue_slpmsg(slplink, slpmsg);
240 }
241
242 #define MAX_FILE_NAME_LEN 0x226
243
244 static void
245 got_sessionreq(MsnSlpCall *slpcall, const char *branch,
246 const char *euf_guid, const char *context)
247 {
248 if (!strcmp(euf_guid, "A4268EEC-FEC5-49E5-95C3-F126696BDBF6"))
249 {
250 /* Emoticon or UserDisplay */
251 MsnSlpSession *slpsession;
252 MsnSlpLink *slplink;
253 MsnSlpMessage *slpmsg;
254 MsnObject *obj;
255 char *msnobj_data;
256 const char *file_name;
257 char *content;
258 gsize len;
259 int type;
260
261 /* Send Ok */
262 content = g_strdup_printf("SessionID: %lu\r\n\r\n",
263 slpcall->session_id);
264
265 send_ok(slpcall, branch, "application/x-msnmsgr-sessionreqbody",
266 content);
267
268 g_free(content);
269
270 slplink = slpcall->slplink;
271
272 msnobj_data = (char *)gaim_base64_decode(context, &len);
273 obj = msn_object_new_from_string(msnobj_data);
274 type = msn_object_get_type(obj);
275 g_free(msnobj_data);
276
277 if (!(type == MSN_OBJECT_USERTILE))
278 {
279 gaim_debug_error("msn", "Wrong object?\n");
280 msn_object_destroy(obj);
281 g_return_if_reached();
282 }
283
284 file_name = msn_object_get_real_location(obj);
285
286 if (file_name == NULL)
287 {
288 gaim_debug_error("msn", "Wrong object.\n");
289 msn_object_destroy(obj);
290 g_return_if_reached();
291 }
292
293 msn_object_destroy(obj);
294
295 slpsession = msn_slplink_find_slp_session(slplink,
296 slpcall->session_id);
297
298 /* DATA PREP */
299 slpmsg = msn_slpmsg_new(slplink);
300 slpmsg->slpcall = slpcall;
301 slpmsg->slpsession = slpsession;
302 slpmsg->session_id = slpsession->id;
303 msn_slpmsg_set_body(slpmsg, NULL, 4);
304 #ifdef MSN_DEBUG_SLP
305 slpmsg->info = "SLP DATA PREP";
306 #endif
307 msn_slplink_queue_slpmsg(slplink, slpmsg);
308
309 /* DATA */
310 slpmsg = msn_slpmsg_new(slplink);
311 slpmsg->slpcall = slpcall;
312 slpmsg->slpsession = slpsession;
313 slpmsg->flags = 0x20;
314 #ifdef MSN_DEBUG_SLP
315 slpmsg->info = "SLP DATA";
316 #endif
317 msn_slpmsg_open_file(slpmsg, file_name);
318 msn_slplink_queue_slpmsg(slplink, slpmsg);
319 }
320 else if (!strcmp(euf_guid, "5D3E02AB-6190-11D3-BBBB-00C04F795683"))
321 {
322 /* File Transfer */
323 GaimAccount *account;
324 GaimXfer *xfer;
325 char *bin;
326 gsize bin_len;
327 guint32 file_size;
328 char *file_name;
329 gunichar2 *uni_name;
330
331 account = slpcall->slplink->session->account;
332
333 slpcall->cb = msn_xfer_completed_cb;
334 slpcall->end_cb = msn_xfer_end_cb;
335 slpcall->progress_cb = msn_xfer_progress_cb;
336 slpcall->branch = g_strdup(branch);
337
338 slpcall->pending = TRUE;
339
340 xfer = gaim_xfer_new(account, GAIM_XFER_RECEIVE,
341 slpcall->slplink->remote_user);
342 if (xfer)
343 {
344 bin = (char *)gaim_base64_decode(context, &bin_len);
345 file_size = GUINT32_FROM_LE(*((gsize *)bin + 2));
346
347 uni_name = (gunichar2 *)(bin + 20);
348 while(*uni_name != 0 && ((char *)uni_name - (bin + 20)) < MAX_FILE_NAME_LEN) {
349 *uni_name = GUINT16_FROM_LE(*uni_name);
350 uni_name++;
351 }
352
353 file_name = g_utf16_to_utf8((const gunichar2 *)(bin + 20), -1,
354 NULL, NULL, NULL);
355
356 g_free(bin);
357
358 gaim_xfer_set_filename(xfer, file_name);
359 gaim_xfer_set_size(xfer, file_size);
360 gaim_xfer_set_init_fnc(xfer, msn_xfer_init);
361 gaim_xfer_set_request_denied_fnc(xfer, msn_xfer_cancel);
362 gaim_xfer_set_cancel_recv_fnc(xfer, msn_xfer_cancel);
363
364 slpcall->xfer = xfer;
365 xfer->data = slpcall;
366
367 gaim_xfer_request(xfer);
368 }
369 }
370 }
371
372 void
373 send_bye(MsnSlpCall *slpcall, const char *type)
374 {
375 MsnSlpLink *slplink;
376 MsnSlpMessage *slpmsg;
377 char *header;
378
379 slplink = slpcall->slplink;
380
381 g_return_if_fail(slplink != NULL);
382
383 header = g_strdup_printf("BYE MSNMSGR:%s MSNSLP/1.0",
384 slplink->local_user);
385
386 slpmsg = msn_slpmsg_sip_new(slpcall, 0, header,
387 "A0D624A6-6C0C-4283-A9E0-BC97B4B46D32",
388 type,
389 "\r\n");
390 g_free(header);
391
392 #ifdef MSN_DEBUG_SLP
393 slpmsg->info = "SLP BYE";
394 slpmsg->text_body = TRUE;
395 #endif
396
397 msn_slplink_queue_slpmsg(slplink, slpmsg);
398 }
399
400 static void
401 got_invite(MsnSlpCall *slpcall,
402 const char *branch, const char *type, const char *content)
403 {
404 MsnSlpLink *slplink;
405
406 slplink = slpcall->slplink;
407
408 if (!strcmp(type, "application/x-msnmsgr-sessionreqbody"))
409 {
410 char *euf_guid, *context;
411 char *temp;
412
413 euf_guid = get_token(content, "EUF-GUID: {", "}\r\n");
414
415 temp = get_token(content, "SessionID: ", "\r\n");
416 if (temp != NULL)
417 slpcall->session_id = atoi(temp);
418 g_free(temp);
419
420 temp = get_token(content, "AppID: ", "\r\n");
421 if (temp != NULL)
422 slpcall->app_id = atoi(temp);
423 g_free(temp);
424
425 context = get_token(content, "Context: ", "\r\n");
426
427 if (context != NULL)
428 got_sessionreq(slpcall, branch, euf_guid, context);
429
430 g_free(context);
431 g_free(euf_guid);
432 }
433 else if (!strcmp(type, "application/x-msnmsgr-transreqbody"))
434 {
435 /* A direct connection? */
436
437 char *listening, *nonce;
438 char *content;
439
440 if (FALSE)
441 {
442 #if 0
443 MsnDirectConn *directconn;
444 /* const char *ip_addr; */
445 char *ip_port;
446 int port;
447
448 /* ip_addr = gaim_prefs_get_string("/core/ft/public_ip"); */
449 ip_port = "5190";
450 listening = "true";
451 nonce = rand_guid();
452
453 directconn = msn_directconn_new(slplink);
454
455 /* msn_directconn_parse_nonce(directconn, nonce); */
456 directconn->nonce = g_strdup(nonce);
457
458 msn_directconn_listen(directconn);
459
460 port = directconn->port;
461
462 content = g_strdup_printf(
463 "Bridge: TCPv1\r\n"
464 "Listening: %s\r\n"
465 "Nonce: {%s}\r\n"
466 "Ipv4Internal-Addrs: 192.168.0.82\r\n"
467 "Ipv4Internal-Port: %d\r\n"
468 "\r\n",
469 listening,
470 nonce,
471 port);
472 #endif
473 }
474 else
475 {
476 listening = "false";
477 nonce = g_strdup("00000000-0000-0000-0000-000000000000");
478
479 content = g_strdup_printf(
480 "Bridge: TCPv1\r\n"
481 "Listening: %s\r\n"
482 "Nonce: {%s}\r\n"
483 "\r\n",
484 listening,
485 nonce);
486 }
487
488 send_ok(slpcall, branch,
489 "application/x-msnmsgr-transrespbody", content);
490
491 g_free(content);
492 g_free(nonce);
493 }
494 else if (!strcmp(type, "application/x-msnmsgr-transrespbody"))
495 {
496 #if 0
497 char *ip_addrs;
498 char *temp;
499 char *nonce;
500 int port;
501
502 nonce = get_token(content, "Nonce: {", "}\r\n");
503 ip_addrs = get_token(content, "IPv4Internal-Addrs: ", "\r\n");
504
505 temp = get_token(content, "IPv4Internal-Port: ", "\r\n");
506 if (temp != NULL)
507 port = atoi(temp);
508 else
509 port = -1;
510 g_free(temp);
511
512 if (ip_addrs == NULL)
513 return;
514
515 if (port > 0)
516 got_transresp(slpcall, nonce, ip_addrs, port);
517
518 g_free(nonce);
519 g_free(ip_addrs);
520 #endif
521 }
522 }
523
524 static void
525 got_ok(MsnSlpCall *slpcall,
526 const char *type, const char *content)
527 {
528 g_return_if_fail(slpcall != NULL);
529 g_return_if_fail(type != NULL);
530
531 if (!strcmp(type, "application/x-msnmsgr-sessionreqbody"))
532 {
533 #if 0
534 if (slpcall->type == MSN_SLPCALL_DC)
535 {
536 /* First let's try a DirectConnection. */
537
538 MsnSlpLink *slplink;
539 MsnSlpMessage *slpmsg;
540 char *header;
541 char *content;
542 char *branch;
543
544 slplink = slpcall->slplink;
545
546 branch = rand_guid();
547
548 content = g_strdup_printf(
549 "Bridges: TRUDPv1 TCPv1\r\n"
550 "NetID: 0\r\n"
551 "Conn-Type: Direct-Connect\r\n"
552 "UPnPNat: false\r\n"
553 "ICF: false\r\n"
554 );
555
556 header = g_strdup_printf("INVITE MSNMSGR:%s MSNSLP/1.0",
557 slplink->remote_user);
558
559 slpmsg = msn_slp_sipmsg_new(slpcall, 0, header, branch,
560 "application/x-msnmsgr-transreqbody",
561 content);
562
563 #ifdef MSN_DEBUG_SLP
564 slpmsg->info = "SLP INVITE";
565 slpmsg->text_body = TRUE;
566 #endif
567 msn_slplink_send_slpmsg(slplink, slpmsg);
568
569 g_free(header);
570 g_free(content);
571
572 g_free(branch);
573 }
574 else
575 {
576 msn_slp_call_session_init(slpcall);
577 }
578 #else
579 msn_slp_call_session_init(slpcall);
580 #endif
581 }
582 else if (!strcmp(type, "application/x-msnmsgr-transreqbody"))
583 {
584 /* Do we get this? */
585 gaim_debug_info("msn", "OK with transreqbody\n");
586 }
587 else if (!strcmp(type, "application/x-msnmsgr-transrespbody"))
588 {
589 #if 0
590 char *ip_addrs;
591 char *temp;
592 char *nonce;
593 int port;
594
595 nonce = get_token(content, "Nonce: {", "}\r\n");
596 ip_addrs = get_token(content, "IPv4Internal-Addrs: ", "\r\n");
597
598 temp = get_token(content, "IPv4Internal-Port: ", "\r\n");
599 if (temp != NULL)
600 port = atoi(temp);
601 else
602 port = -1;
603 g_free(temp);
604
605 if (ip_addrs == NULL)
606 return;
607
608 if (port > 0)
609 got_transresp(slpcall, nonce, ip_addrs, port);
610
611 g_free(nonce);
612 g_free(ip_addrs);
613 #endif
614 }
615 }
616
617 MsnSlpCall *
618 msn_slp_sip_recv(MsnSlpLink *slplink, const char *body)
619 {
620 MsnSlpCall *slpcall;
621
622 if (body == NULL)
623 {
624 gaim_debug_warning("msn", "received bogus message\n");
625 return NULL;
626 }
627
628 if (!strncmp(body, "INVITE", strlen("INVITE")))
629 {
630 char *branch;
631 char *content;
632 char *content_type;
633
634 slpcall = msn_slp_call_new(slplink);
635
636 /* From: <msnmsgr:buddy@hotmail.com> */
637 #if 0
638 slpcall->remote_user = get_token(body, "From: <msnmsgr:", ">\r\n");
639 #endif
640
641 branch = get_token(body, ";branch={", "}");
642
643 slpcall->id = get_token(body, "Call-ID: {", "}");
644
645 #if 0
646 long content_len = -1;
647
648 temp = get_token(body, "Content-Length: ", "\r\n");
649 if (temp != NULL)
650 content_len = atoi(temp);
651 g_free(temp);
652 #endif
653 content_type = get_token(body, "Content-Type: ", "\r\n");
654
655 content = get_token(body, "\r\n\r\n", NULL);
656
657 got_invite(slpcall, branch, content_type, content);
658
659 g_free(branch);
660 g_free(content_type);
661 g_free(content);
662 }
663 else if (!strncmp(body, "MSNSLP/1.0 ", strlen("MSNSLP/1.0 ")))
664 {
665 char *content;
666 char *content_type;
667 /* Make sure this is "OK" */
668 const char *status = body + strlen("MSNSLP/1.0 ");
669 char *call_id;
670
671 call_id = get_token(body, "Call-ID: {", "}");
672 slpcall = msn_slplink_find_slp_call(slplink, call_id);
673 g_free(call_id);
674
675 g_return_val_if_fail(slpcall != NULL, NULL);
676
677 if (strncmp(status, "200 OK", 6))
678 {
679 /* It's not valid. Kill this off. */
680 char temp[32];
681 const char *c;
682
683 /* Eww */
684 if ((c = strchr(status, '\r')) || (c = strchr(status, '\n')) ||
685 (c = strchr(status, '\0')))
686 {
687 size_t offset = c - status;
688 if (offset >= sizeof(temp))
689 offset = sizeof(temp) - 1;
690
691 strncpy(temp, status, offset);
692 temp[offset] = '\0';
693 }
694
695 gaim_debug_error("msn", "Received non-OK result: %s\n", temp);
696
697 slpcall->wasted = TRUE;
698
699 /* msn_slp_call_destroy(slpcall); */
700 return slpcall;
701 }
702
703 content_type = get_token(body, "Content-Type: ", "\r\n");
704
705 content = get_token(body, "\r\n\r\n", NULL);
706
707 got_ok(slpcall, content_type, content);
708
709 g_free(content_type);
710 g_free(content);
711 }
712 else if (!strncmp(body, "BYE", strlen("BYE")))
713 {
714 char *call_id;
715
716 call_id = get_token(body, "Call-ID: {", "}");
717 slpcall = msn_slplink_find_slp_call(slplink, call_id);
718 g_free(call_id);
719
720 if (slpcall != NULL)
721 slpcall->wasted = TRUE;
722
723 /* msn_slp_call_destroy(slpcall); */
724 }
725 else
726 slpcall = NULL;
727
728 return slpcall;
729 }
730
731 /**************************************************************************
732 * Msg Callbacks
733 **************************************************************************/
734
735 void
736 msn_p2p_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
737 {
738 MsnSession *session;
739 MsnSlpLink *slplink;
740
741 session = cmdproc->servconn->session;
742 slplink = msn_session_get_slplink(session, msg->remote_user);
743
744 if (slplink->swboard == NULL)
745 {
746 /* We will need this in order to change its flags. */
747 slplink->swboard = (MsnSwitchBoard *)cmdproc->data;
748 /* If swboard is NULL, something has probably gone wrong earlier on
749 * I didn't want to do this, but MSN 7 is somehow causing us to crash
750 * here, I couldn't reproduce it to debug more, and people are
751 * reporting bugs. Hopefully this doesn't cause more crashes. Stu.
752 */
753 if (slplink->swboard != NULL)
754 slplink->swboard->slplinks = g_list_prepend(slplink->swboard->slplinks, slplink);
755 else
756 gaim_debug_error("msn", "msn_p2p_msg, swboard is NULL, ouch!\n");
757 }
758
759 msn_slplink_process_msg(slplink, msg);
760 }
761
762 static void
763 got_emoticon(MsnSlpCall *slpcall,
764 const guchar *data, gsize size)
765 {
766
767 GaimConversation *conv;
768 GaimConnection *gc;
769 const char *who;
770
771 gc = slpcall->slplink->session->account->gc;
772 who = slpcall->slplink->remote_user;
773
774 if ((conv = gaim_find_conversation_with_account(GAIM_CONV_TYPE_ANY, who, gc->account))) {
775
776 /* FIXME: it would be better if we wrote the data as we received it
777 instead of all at once, calling write multiple times and
778 close once at the very end
779 */
780 gaim_conv_custom_smiley_write(conv, slpcall->data_info, data, size);
781 gaim_conv_custom_smiley_close(conv, slpcall->data_info);
782 }
783 #ifdef MSN_DEBUG_UD
784 gaim_debug_info("msn", "Got smiley: %s\n", slpcall->data_info);
785 #endif
786 }
787
788 void
789 msn_emoticon_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
790 {
791 MsnSession *session;
792 MsnSlpLink *slplink;
793 MsnObject *obj;
794 char **tokens;
795 char *smile, *body_str;
796 const char *body, *who, *sha1c;
797 guint tok;
798 size_t body_len;
799
800 GaimConversation *conv;
801
802 session = cmdproc->servconn->session;
803
804 if (!gaim_account_get_bool(session->account, "custom_smileys", TRUE))
805 return;
806
807 body = msn_message_get_bin_data(msg, &body_len);
808 body_str = g_strndup(body, body_len);
809
810 /* MSN Messenger 7 may send more than one MSNObject in a single message...
811 * Maybe 10 tokens is a reasonable max value. */
812 tokens = g_strsplit(body_str, "\t", 10);
813
814 g_free(body_str);
815
816 for (tok = 0; tok < 9; tok += 2) {
817 if (tokens[tok] == NULL || tokens[tok + 1] == NULL) {
818 break;
819 }
820
821 smile = tokens[tok];
822 obj = msn_object_new_from_string(gaim_url_decode(tokens[tok + 1]));
823
824 if (obj == NULL)
825 break;
826
827 who = msn_object_get_creator(obj);
828 sha1c = msn_object_get_sha1c(obj);
829
830 slplink = msn_session_get_slplink(session, who);
831
832 conv = gaim_find_conversation_with_account(GAIM_CONV_TYPE_ANY, who,
833 session->account);
834
835 /* If the conversation doesn't exist then this is a custom smiley
836 * used in the first message in a MSN conversation: we need to create
837 * the conversation now, otherwise the custom smiley won't be shown.
838 * This happens because every GtkIMHtml has its own smiley tree: if
839 * the conversation doesn't exist then we cannot associate the new
840 * smiley with its GtkIMHtml widget. */
841 if (!conv) {
842 conv = gaim_conversation_new(GAIM_CONV_TYPE_IM, session->account, who);
843 }
844
845 if (gaim_conv_custom_smiley_add(conv, smile, "sha1", sha1c, TRUE)) {
846 msn_slplink_request_object(slplink, smile, got_emoticon, NULL, obj);
847 }
848
849 msn_object_destroy(obj);
850 obj = NULL;
851 who = NULL;
852 sha1c = NULL;
853 }
854 g_strfreev(tokens);
855 }
856
857 static gboolean
858 buddy_icon_cached(GaimConnection *gc, MsnObject *obj)
859 {
860 GaimAccount *account;
861 GaimBuddy *buddy;
862 const char *old;
863 const char *new;
864
865 g_return_val_if_fail(obj != NULL, FALSE);
866
867 account = gaim_connection_get_account(gc);
868
869 buddy = gaim_find_buddy(account, msn_object_get_creator(obj));
870 if (buddy == NULL)
871 return FALSE;
872
873 old = gaim_blist_node_get_string((GaimBlistNode *)buddy, "icon_checksum");
874 new = msn_object_get_sha1c(obj);
875
876 if (new == NULL)
877 return FALSE;
878
879 /* If the old and new checksums are the same, and the file actually exists,
880 * then return TRUE */
881 if (old != NULL && !strcmp(old, new) && (gaim_buddy_icons_find(account, gaim_buddy_get_name(buddy)) != NULL))
882 return TRUE;
883
884 return FALSE;
885 }
886
887 static void
888 msn_release_buddy_icon_request(MsnUserList *userlist)
889 {
890 MsnUser *user;
891
892 g_return_if_fail(userlist != NULL);
893
894 #ifdef MSN_DEBUG_UD
895 gaim_debug_info("msn", "Releasing buddy icon request\n");
896 #endif
897
898 if (userlist->buddy_icon_window > 0)
899 {
900 GQueue *queue;
901 GaimAccount *account;
902 const char *username;
903
904 queue = userlist->buddy_icon_requests;
905
906 if (g_queue_is_empty(userlist->buddy_icon_requests))
907 return;
908
909 user = g_queue_pop_head(queue);
910
911 account = userlist->session->account;
912 username = user->passport;
913
914 userlist->buddy_icon_window--;
915 msn_request_user_display(user);
916
917 #ifdef MSN_DEBUG_UD
918 gaim_debug_info("msn", "msn_release_buddy_icon_request(): buddy_icon_window-- yields =%d\n",
919 userlist->buddy_icon_window);
920 #endif
921 }
922 }
923
924 /*
925 * Called on a timeout from end_user_display(). Frees a buddy icon window slow and dequeues the next
926 * buddy icon request if there is one.
927 */
928 static gboolean
929 msn_release_buddy_icon_request_timeout(gpointer data)
930 {
931 MsnUserList *userlist = (MsnUserList *)data;
932
933 /* Free one window slot */
934 userlist->buddy_icon_window++;
935
936 /* Clear the tag for our former request timer */
937 userlist->buddy_icon_request_timer = 0;
938
939 msn_release_buddy_icon_request(userlist);
940
941 return FALSE;
942 }
943
944 void
945 msn_queue_buddy_icon_request(MsnUser *user)
946 {
947 GaimAccount *account;
948 MsnObject *obj;
949 GQueue *queue;
950
951 g_return_if_fail(user != NULL);
952
953 account = user->userlist->session->account;
954
955 obj = msn_user_get_object(user);
956
957 if (obj == NULL)
958 {
959 /* It seems the user has not set a msnobject */
960 GSList *sl, *list;
961
962 list = gaim_find_buddies(account, user->passport);
963
964 for (sl = list; sl != NULL; sl = sl->next)
965 {
966 GaimBuddy *buddy = (GaimBuddy *)sl->data;
967 if (buddy->icon)
968 gaim_blist_node_remove_setting((GaimBlistNode*)buddy, "icon_checksum");
969 }
970 g_slist_free(list);
971
972 /* TODO: I think we need better buddy icon core functions. */
973 gaim_buddy_icons_set_for_user(account, user->passport, NULL, 0);
974
975 return;
976 }
977
978 if (!buddy_icon_cached(account->gc, obj))
979 {
980 MsnUserList *userlist;
981
982 userlist = user->userlist;
983 queue = userlist->buddy_icon_requests;
984
985 #ifdef MSN_DEBUG_UD
986 gaim_debug_info("msn", "Queueing buddy icon request for %s (buddy_icon_window = %i)\n",
987 user->passport, userlist->buddy_icon_window);
988 #endif
989
990 g_queue_push_tail(queue, user);
991
992 if (userlist->buddy_icon_window > 0)
993 msn_release_buddy_icon_request(userlist);
994 }
995 }
996
997 static void
998 got_user_display(MsnSlpCall *slpcall,
999 const guchar *data, gsize size)
1000 {
1001 MsnUserList *userlist;
1002 const char *info;
1003 GaimAccount *account;
1004 GSList *sl, *list;
1005
1006 g_return_if_fail(slpcall != NULL);
1007
1008 info = slpcall->data_info;
1009 #ifdef MSN_DEBUG_UD
1010 gaim_debug_info("msn", "Got User Display: %s\n", slpcall->slplink->remote_user);
1011 #endif
1012
1013 userlist = slpcall->slplink->session->userlist;
1014 account = slpcall->slplink->session->account;
1015
1016 /* TODO: I think we need better buddy icon core functions. */
1017 gaim_buddy_icons_set_for_user(account, slpcall->slplink->remote_user,
1018 (void *)data, size);
1019
1020 list = gaim_find_buddies(account, slpcall->slplink->remote_user);
1021
1022 for (sl = list; sl != NULL; sl = sl->next)
1023 {
1024 GaimBuddy *buddy = (GaimBuddy *)sl->data;
1025 gaim_blist_node_set_string((GaimBlistNode*)buddy, "icon_checksum", info);
1026 }
1027 g_slist_free(list);
1028
1029 #if 0
1030 /* Free one window slot */
1031 userlist->buddy_icon_window++;
1032
1033 gaim_debug_info("msn", "got_user_display(): buddy_icon_window++ yields =%d\n",
1034 userlist->buddy_icon_window);
1035
1036 msn_release_buddy_icon_request(userlist);
1037 #endif
1038 }
1039
1040 static void
1041 end_user_display(MsnSlpCall *slpcall, MsnSession *session)
1042 {
1043 MsnUserList *userlist;
1044
1045 g_return_if_fail(session != NULL);
1046
1047 #ifdef MSN_DEBUG_UD
1048 gaim_debug_info("msn", "End User Display\n");
1049 #endif
1050
1051 userlist = session->userlist;
1052
1053 /* If the session is being destroyed we better stop doing anything. */
1054 if (session->destroying)
1055 return;
1056
1057 /* Delay before freeing a buddy icon window slot and requesting the next icon, if appropriate.
1058 * If we don't delay, we'll rapidly hit the MSN equivalent of AIM's rate limiting; the server will
1059 * send us an error 800 like so:
1060 *
1061 * C: NS 000: XFR 21 SB
1062 * S: NS 000: 800 21
1063 */
1064 if (userlist->buddy_icon_request_timer) {
1065 /* Free the window slot used by this previous request */
1066 userlist->buddy_icon_window++;
1067
1068 /* Clear our pending timeout */
1069 gaim_timeout_remove(userlist->buddy_icon_request_timer);
1070 }
1071
1072 /* Wait BUDDY_ICON_DELAY ms before freeing our window slot and requesting the next icon. */
1073 userlist->buddy_icon_request_timer = gaim_timeout_add(BUDDY_ICON_DELAY,
1074 msn_release_buddy_icon_request_timeout, userlist);
1075 }
1076
1077 void
1078 msn_request_user_display(MsnUser *user)
1079 {
1080 GaimAccount *account;
1081 MsnSession *session;
1082 MsnSlpLink *slplink;
1083 MsnObject *obj;
1084 const char *info;
1085
1086 session = user->userlist->session;
1087 account = session->account;
1088
1089 slplink = msn_session_get_slplink(session, user->passport);
1090
1091 obj = msn_user_get_object(user);
1092
1093 info = msn_object_get_sha1c(obj);
1094
1095 if (g_ascii_strcasecmp(user->passport,
1096 gaim_account_get_username(account)))
1097 {
1098 msn_slplink_request_object(slplink, info, got_user_display,
1099 end_user_display, obj);
1100 }
1101 else
1102 {
1103 MsnObject *my_obj = NULL;
1104 gchar *data = NULL;
1105 gsize len = 0;
1106 GSList *sl, *list;
1107
1108 #ifdef MSN_DEBUG_UD
1109 gaim_debug_info("msn", "Requesting our own user display\n");
1110 #endif
1111
1112 my_obj = msn_user_get_object(session->user);
1113
1114 if (my_obj != NULL)
1115 {
1116 const char *filename = msn_object_get_real_location(my_obj);
1117
1118 if (filename != NULL)
1119 g_file_get_contents(filename, &data, &len, NULL);
1120 }
1121
1122 /* TODO: I think we need better buddy icon core functions. */
1123 gaim_buddy_icons_set_for_user(account, user->passport, (void *)data, len);
1124 g_free(data);
1125
1126 list = gaim_find_buddies(account, user->passport);
1127
1128 for (sl = list; sl != NULL; sl = sl->next)
1129 {
1130 GaimBuddy *buddy = (GaimBuddy *)sl->data;
1131 gaim_blist_node_set_string((GaimBlistNode*)buddy, "icon_checksum", info);
1132 }
1133 g_slist_free(list);
1134
1135 /* Free one window slot */
1136 session->userlist->buddy_icon_window++;
1137
1138 #ifdef MSN_DEBUG_UD
1139 gaim_debug_info("msn", "msn_request_user_display(): buddy_icon_window++ yields =%d\n",
1140 session->userlist->buddy_icon_window);
1141 #endif
1142
1143 msn_release_buddy_icon_request(session->userlist);
1144 }
1145 }