comparison libpurple/protocols/msnp9/slp.c @ 21312:a07cfce78345

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