core/protocols/msn/slp.c

changeset 14253
b63ebf84c42b
parent 14147
b6323eeaf37b
equal deleted inserted replaced
14252:d10dda2777a9 14253:b63ebf84c42b
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
343 bin = (char *)gaim_base64_decode(context, &bin_len);
344 file_size = GUINT32_FROM_LE(*((gsize *)bin + 2));
345
346 uni_name = (gunichar2 *)(bin + 20);
347 while(*uni_name != 0 && ((char *)uni_name - (bin + 20)) < MAX_FILE_NAME_LEN) {
348 *uni_name = GUINT16_FROM_LE(*uni_name);
349 uni_name++;
350 }
351
352 file_name = g_utf16_to_utf8((const gunichar2 *)(bin + 20), -1,
353 NULL, NULL, NULL);
354
355 g_free(bin);
356
357 gaim_xfer_set_filename(xfer, file_name);
358 gaim_xfer_set_size(xfer, file_size);
359 gaim_xfer_set_init_fnc(xfer, msn_xfer_init);
360 gaim_xfer_set_request_denied_fnc(xfer, msn_xfer_cancel);
361 gaim_xfer_set_cancel_recv_fnc(xfer, msn_xfer_cancel);
362
363 slpcall->xfer = xfer;
364 xfer->data = slpcall;
365
366 gaim_xfer_request(xfer);
367 }
368 }
369
370 void
371 send_bye(MsnSlpCall *slpcall, const char *type)
372 {
373 MsnSlpLink *slplink;
374 MsnSlpMessage *slpmsg;
375 char *header;
376
377 slplink = slpcall->slplink;
378
379 g_return_if_fail(slplink != NULL);
380
381 header = g_strdup_printf("BYE MSNMSGR:%s MSNSLP/1.0",
382 slplink->local_user);
383
384 slpmsg = msn_slpmsg_sip_new(slpcall, 0, header,
385 "A0D624A6-6C0C-4283-A9E0-BC97B4B46D32",
386 type,
387 "\r\n");
388 g_free(header);
389
390 #ifdef MSN_DEBUG_SLP
391 slpmsg->info = "SLP BYE";
392 slpmsg->text_body = TRUE;
393 #endif
394
395 msn_slplink_queue_slpmsg(slplink, slpmsg);
396 }
397
398 static void
399 got_invite(MsnSlpCall *slpcall,
400 const char *branch, const char *type, const char *content)
401 {
402 MsnSlpLink *slplink;
403
404 slplink = slpcall->slplink;
405
406 if (!strcmp(type, "application/x-msnmsgr-sessionreqbody"))
407 {
408 char *euf_guid, *context;
409 char *temp;
410
411 euf_guid = get_token(content, "EUF-GUID: {", "}\r\n");
412
413 temp = get_token(content, "SessionID: ", "\r\n");
414 if (temp != NULL)
415 slpcall->session_id = atoi(temp);
416 g_free(temp);
417
418 temp = get_token(content, "AppID: ", "\r\n");
419 if (temp != NULL)
420 slpcall->app_id = atoi(temp);
421 g_free(temp);
422
423 context = get_token(content, "Context: ", "\r\n");
424
425 got_sessionreq(slpcall, branch, euf_guid, context);
426
427 g_free(context);
428 g_free(euf_guid);
429 }
430 else if (!strcmp(type, "application/x-msnmsgr-transreqbody"))
431 {
432 /* A direct connection? */
433
434 char *listening, *nonce;
435 char *content;
436
437 if (FALSE)
438 {
439 #if 0
440 MsnDirectConn *directconn;
441 /* const char *ip_addr; */
442 char *ip_port;
443 int port;
444
445 /* ip_addr = gaim_prefs_get_string("/core/ft/public_ip"); */
446 ip_port = "5190";
447 listening = "true";
448 nonce = rand_guid();
449
450 directconn = msn_directconn_new(slplink);
451
452 /* msn_directconn_parse_nonce(directconn, nonce); */
453 directconn->nonce = g_strdup(nonce);
454
455 msn_directconn_listen(directconn);
456
457 port = directconn->port;
458
459 content = g_strdup_printf(
460 "Bridge: TCPv1\r\n"
461 "Listening: %s\r\n"
462 "Nonce: {%s}\r\n"
463 "Ipv4Internal-Addrs: 192.168.0.82\r\n"
464 "Ipv4Internal-Port: %d\r\n"
465 "\r\n",
466 listening,
467 nonce,
468 port);
469 #endif
470 }
471 else
472 {
473 listening = "false";
474 nonce = g_strdup("00000000-0000-0000-0000-000000000000");
475
476 content = g_strdup_printf(
477 "Bridge: TCPv1\r\n"
478 "Listening: %s\r\n"
479 "Nonce: {%s}\r\n"
480 "\r\n",
481 listening,
482 nonce);
483 }
484
485 send_ok(slpcall, branch,
486 "application/x-msnmsgr-transrespbody", content);
487
488 g_free(content);
489 g_free(nonce);
490 }
491 else if (!strcmp(type, "application/x-msnmsgr-transrespbody"))
492 {
493 #if 0
494 char *ip_addrs;
495 char *temp;
496 char *nonce;
497 int port;
498
499 nonce = get_token(content, "Nonce: {", "}\r\n");
500 ip_addrs = get_token(content, "IPv4Internal-Addrs: ", "\r\n");
501
502 temp = get_token(content, "IPv4Internal-Port: ", "\r\n");
503 if (temp != NULL)
504 port = atoi(temp);
505 else
506 port = -1;
507 g_free(temp);
508
509 if (ip_addrs == NULL)
510 return;
511
512 if (port > 0)
513 got_transresp(slpcall, nonce, ip_addrs, port);
514
515 g_free(nonce);
516 g_free(ip_addrs);
517 #endif
518 }
519 }
520
521 static void
522 got_ok(MsnSlpCall *slpcall,
523 const char *type, const char *content)
524 {
525 g_return_if_fail(slpcall != NULL);
526 g_return_if_fail(type != NULL);
527
528 if (!strcmp(type, "application/x-msnmsgr-sessionreqbody"))
529 {
530 #if 0
531 if (slpcall->type == MSN_SLPCALL_DC)
532 {
533 /* First let's try a DirectConnection. */
534
535 MsnSlpLink *slplink;
536 MsnSlpMessage *slpmsg;
537 char *header;
538 char *content;
539 char *branch;
540
541 slplink = slpcall->slplink;
542
543 branch = rand_guid();
544
545 content = g_strdup_printf(
546 "Bridges: TRUDPv1 TCPv1\r\n"
547 "NetID: 0\r\n"
548 "Conn-Type: Direct-Connect\r\n"
549 "UPnPNat: false\r\n"
550 "ICF: false\r\n"
551 );
552
553 header = g_strdup_printf("INVITE MSNMSGR:%s MSNSLP/1.0",
554 slplink->remote_user);
555
556 slpmsg = msn_slp_sipmsg_new(slpcall, 0, header, branch,
557 "application/x-msnmsgr-transreqbody",
558 content);
559
560 #ifdef MSN_DEBUG_SLP
561 slpmsg->info = "SLP INVITE";
562 slpmsg->text_body = TRUE;
563 #endif
564 msn_slplink_send_slpmsg(slplink, slpmsg);
565
566 g_free(header);
567 g_free(content);
568
569 g_free(branch);
570 }
571 else
572 {
573 msn_slp_call_session_init(slpcall);
574 }
575 #else
576 msn_slp_call_session_init(slpcall);
577 #endif
578 }
579 else if (!strcmp(type, "application/x-msnmsgr-transreqbody"))
580 {
581 /* Do we get this? */
582 gaim_debug_info("msn", "OK with transreqbody\n");
583 }
584 else if (!strcmp(type, "application/x-msnmsgr-transrespbody"))
585 {
586 #if 0
587 char *ip_addrs;
588 char *temp;
589 char *nonce;
590 int port;
591
592 nonce = get_token(content, "Nonce: {", "}\r\n");
593 ip_addrs = get_token(content, "IPv4Internal-Addrs: ", "\r\n");
594
595 temp = get_token(content, "IPv4Internal-Port: ", "\r\n");
596 if (temp != NULL)
597 port = atoi(temp);
598 else
599 port = -1;
600 g_free(temp);
601
602 if (ip_addrs == NULL)
603 return;
604
605 if (port > 0)
606 got_transresp(slpcall, nonce, ip_addrs, port);
607
608 g_free(nonce);
609 g_free(ip_addrs);
610 #endif
611 }
612 }
613
614 MsnSlpCall *
615 msn_slp_sip_recv(MsnSlpLink *slplink, const char *body)
616 {
617 MsnSlpCall *slpcall;
618
619 if (body == NULL)
620 {
621 gaim_debug_warning("msn", "received bogus message\n");
622 return NULL;
623 }
624
625 if (!strncmp(body, "INVITE", strlen("INVITE")))
626 {
627 char *branch;
628 char *content;
629 char *content_type;
630
631 slpcall = msn_slp_call_new(slplink);
632
633 /* From: <msnmsgr:buddy@hotmail.com> */
634 #if 0
635 slpcall->remote_user = get_token(body, "From: <msnmsgr:", ">\r\n");
636 #endif
637
638 branch = get_token(body, ";branch={", "}");
639
640 slpcall->id = get_token(body, "Call-ID: {", "}");
641
642 #if 0
643 long content_len = -1;
644
645 temp = get_token(body, "Content-Length: ", "\r\n");
646 if (temp != NULL)
647 content_len = atoi(temp);
648 g_free(temp);
649 #endif
650 content_type = get_token(body, "Content-Type: ", "\r\n");
651
652 content = get_token(body, "\r\n\r\n", NULL);
653
654 got_invite(slpcall, branch, content_type, content);
655
656 g_free(branch);
657 g_free(content_type);
658 g_free(content);
659 }
660 else if (!strncmp(body, "MSNSLP/1.0 ", strlen("MSNSLP/1.0 ")))
661 {
662 char *content;
663 char *content_type;
664 /* Make sure this is "OK" */
665 const char *status = body + strlen("MSNSLP/1.0 ");
666 char *call_id;
667
668 call_id = get_token(body, "Call-ID: {", "}");
669 slpcall = msn_slplink_find_slp_call(slplink, call_id);
670 g_free(call_id);
671
672 g_return_val_if_fail(slpcall != NULL, NULL);
673
674 if (strncmp(status, "200 OK", 6))
675 {
676 /* It's not valid. Kill this off. */
677 char temp[32];
678 const char *c;
679
680 /* Eww */
681 if ((c = strchr(status, '\r')) || (c = strchr(status, '\n')) ||
682 (c = strchr(status, '\0')))
683 {
684 size_t offset = c - status;
685 if (offset >= sizeof(temp))
686 offset = sizeof(temp) - 1;
687
688 strncpy(temp, status, offset);
689 temp[offset] = '\0';
690 }
691
692 gaim_debug_error("msn", "Received non-OK result: %s\n", temp);
693
694 slpcall->wasted = TRUE;
695
696 /* msn_slp_call_destroy(slpcall); */
697 return slpcall;
698 }
699
700 content_type = get_token(body, "Content-Type: ", "\r\n");
701
702 content = get_token(body, "\r\n\r\n", NULL);
703
704 got_ok(slpcall, content_type, content);
705
706 g_free(content_type);
707 g_free(content);
708 }
709 else if (!strncmp(body, "BYE", strlen("BYE")))
710 {
711 char *call_id;
712
713 call_id = get_token(body, "Call-ID: {", "}");
714 slpcall = msn_slplink_find_slp_call(slplink, call_id);
715 g_free(call_id);
716
717 if (slpcall != NULL)
718 slpcall->wasted = TRUE;
719
720 /* msn_slp_call_destroy(slpcall); */
721 }
722 else
723 slpcall = NULL;
724
725 return slpcall;
726 }
727
728 /**************************************************************************
729 * Msg Callbacks
730 **************************************************************************/
731
732 void
733 msn_p2p_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
734 {
735 MsnSession *session;
736 MsnSlpLink *slplink;
737
738 session = cmdproc->servconn->session;
739 slplink = msn_session_get_slplink(session, msg->remote_user);
740
741 if (slplink->swboard == NULL)
742 {
743 /* We will need this in order to change its flags. */
744 slplink->swboard = (MsnSwitchBoard *)cmdproc->data;
745 /* If swboard is NULL, something has probably gone wrong earlier on
746 * I didn't want to do this, but MSN 7 is somehow causing us to crash
747 * here, I couldn't reproduce it to debug more, and people are
748 * reporting bugs. Hopefully this doesn't cause more crashes. Stu.
749 */
750 if (slplink->swboard != NULL)
751 slplink->swboard->slplinks = g_list_prepend(slplink->swboard->slplinks, slplink);
752 else
753 gaim_debug_error("msn", "msn_p2p_msg, swboard is NULL, ouch!\n");
754 }
755
756 msn_slplink_process_msg(slplink, msg);
757 }
758
759 static void
760 got_emoticon(MsnSlpCall *slpcall,
761 const guchar *data, gsize size)
762 {
763
764 GaimConversation *conv;
765 GaimConnection *gc;
766 const char *who;
767
768 gc = slpcall->slplink->session->account->gc;
769 who = slpcall->slplink->remote_user;
770
771 if ((conv = gaim_find_conversation_with_account(GAIM_CONV_TYPE_ANY, who, gc->account))) {
772
773 /* FIXME: it would be better if we wrote the data as we received it
774 instead of all at once, calling write multiple times and
775 close once at the very end
776 */
777 gaim_conv_custom_smiley_write(conv, slpcall->data_info, data, size);
778 gaim_conv_custom_smiley_close(conv, slpcall->data_info);
779 }
780 #ifdef MSN_DEBUG_UD
781 gaim_debug_info("msn", "Got smiley: %s\n", slpcall->data_info);
782 #endif
783 }
784
785 void
786 msn_emoticon_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
787 {
788 MsnSession *session;
789 MsnSlpLink *slplink;
790 MsnObject *obj;
791 char **tokens;
792 char *smile, *body_str;
793 const char *body, *who, *sha1c;
794 guint tok;
795 size_t body_len;
796
797 GaimConversation *conv;
798
799 session = cmdproc->servconn->session;
800
801 if (!gaim_account_get_bool(session->account, "custom_smileys", TRUE))
802 return;
803
804 body = msn_message_get_bin_data(msg, &body_len);
805 body_str = g_strndup(body, body_len);
806
807 /* MSN Messenger 7 may send more than one MSNObject in a single message...
808 * Maybe 10 tokens is a reasonable max value. */
809 tokens = g_strsplit(body_str, "\t", 10);
810
811 g_free(body_str);
812
813 for (tok = 0; tok < 9; tok += 2) {
814 if (tokens[tok] == NULL || tokens[tok + 1] == NULL) {
815 break;
816 }
817
818 smile = tokens[tok];
819 obj = msn_object_new_from_string(gaim_url_decode(tokens[tok + 1]));
820
821 who = msn_object_get_creator(obj);
822 sha1c = msn_object_get_sha1c(obj);
823
824 slplink = msn_session_get_slplink(session, who);
825
826 conv = gaim_find_conversation_with_account(GAIM_CONV_TYPE_ANY, who,
827 session->account);
828
829 /* If the conversation doesn't exist then this is a custom smiley
830 * used in the first message in a MSN conversation: we need to create
831 * the conversation now, otherwise the custom smiley won't be shown.
832 * This happens because every GtkIMHtml has its own smiley tree: if
833 * the conversation doesn't exist then we cannot associate the new
834 * smiley with its GtkIMHtml widget. */
835 if (!conv) {
836 conv = gaim_conversation_new(GAIM_CONV_TYPE_IM, session->account, who);
837 }
838
839 if (gaim_conv_custom_smiley_add(conv, smile, "sha1", sha1c, TRUE)) {
840 msn_slplink_request_object(slplink, smile, got_emoticon, NULL, obj);
841 }
842
843 msn_object_destroy(obj);
844 obj = NULL;
845 who = NULL;
846 sha1c = NULL;
847 }
848 g_strfreev(tokens);
849 }
850
851 static gboolean
852 buddy_icon_cached(GaimConnection *gc, MsnObject *obj)
853 {
854 GaimAccount *account;
855 GaimBuddy *buddy;
856 const char *old;
857 const char *new;
858
859 g_return_val_if_fail(obj != NULL, FALSE);
860
861 account = gaim_connection_get_account(gc);
862
863 buddy = gaim_find_buddy(account, msn_object_get_creator(obj));
864 if (buddy == NULL)
865 return FALSE;
866
867 old = gaim_blist_node_get_string((GaimBlistNode *)buddy, "icon_checksum");
868 new = msn_object_get_sha1c(obj);
869
870 if (new == NULL)
871 return FALSE;
872
873 /* If the old and new checksums are the same, and the file actually exists,
874 * then return TRUE */
875 if (old != NULL && !strcmp(old, new) && (gaim_buddy_icons_find(account, gaim_buddy_get_name(buddy)) != NULL))
876 return TRUE;
877
878 return FALSE;
879 }
880
881 static void
882 msn_release_buddy_icon_request(MsnUserList *userlist)
883 {
884 MsnUser *user;
885
886 g_return_if_fail(userlist != NULL);
887
888 #ifdef MSN_DEBUG_UD
889 gaim_debug_info("msn", "Releasing buddy icon request\n");
890 #endif
891
892 if (userlist->buddy_icon_window > 0)
893 {
894 GQueue *queue;
895 GaimAccount *account;
896 const char *username;
897
898 queue = userlist->buddy_icon_requests;
899
900 if (g_queue_is_empty(userlist->buddy_icon_requests))
901 return;
902
903 user = g_queue_pop_head(queue);
904
905 account = userlist->session->account;
906 username = user->passport;
907
908 userlist->buddy_icon_window--;
909 msn_request_user_display(user);
910
911 #ifdef MSN_DEBUG_UD
912 gaim_debug_info("msn", "msn_release_buddy_icon_request(): buddy_icon_window-- yields =%d\n",
913 userlist->buddy_icon_window);
914 #endif
915 }
916 }
917
918 /*
919 * Called on a timeout from end_user_display(). Frees a buddy icon window slow and dequeues the next
920 * buddy icon request if there is one.
921 */
922 static gboolean
923 msn_release_buddy_icon_request_timeout(gpointer data)
924 {
925 MsnUserList *userlist = (MsnUserList *)data;
926
927 /* Free one window slot */
928 userlist->buddy_icon_window++;
929
930 /* Clear the tag for our former request timer */
931 userlist->buddy_icon_request_timer = 0;
932
933 msn_release_buddy_icon_request(userlist);
934
935 return FALSE;
936 }
937
938 void
939 msn_queue_buddy_icon_request(MsnUser *user)
940 {
941 GaimAccount *account;
942 MsnObject *obj;
943 GQueue *queue;
944
945 g_return_if_fail(user != NULL);
946
947 account = user->userlist->session->account;
948
949 obj = msn_user_get_object(user);
950
951 if (obj == NULL)
952 {
953 /* It seems the user has not set a msnobject */
954 GSList *sl, *list;
955
956 /* TODO: I think we need better buddy icon core functions. */
957 gaim_buddy_icons_set_for_user(account, user->passport, NULL, -1);
958
959 list = gaim_find_buddies(account, user->passport);
960
961 for (sl = list; sl != NULL; sl = sl->next)
962 {
963 GaimBuddy *buddy = (GaimBuddy *)sl->data;
964 gaim_blist_node_remove_setting((GaimBlistNode*)buddy, "icon_checksum");
965 }
966 g_slist_free(list);
967
968 return;
969 }
970
971 if (!buddy_icon_cached(account->gc, obj))
972 {
973 MsnUserList *userlist;
974
975 userlist = user->userlist;
976 queue = userlist->buddy_icon_requests;
977
978 #ifdef MSN_DEBUG_UD
979 gaim_debug_info("msn", "Queueing buddy icon request for %s (buddy_icon_window = %i)\n",
980 user->passport, userlist->buddy_icon_window);
981 #endif
982
983 g_queue_push_tail(queue, user);
984
985 if (userlist->buddy_icon_window > 0)
986 msn_release_buddy_icon_request(userlist);
987 }
988 }
989
990 static void
991 got_user_display(MsnSlpCall *slpcall,
992 const guchar *data, gsize size)
993 {
994 MsnUserList *userlist;
995 const char *info;
996 GaimAccount *account;
997 GSList *sl, *list;
998
999 g_return_if_fail(slpcall != NULL);
1000
1001 info = slpcall->data_info;
1002 #ifdef MSN_DEBUG_UD
1003 gaim_debug_info("msn", "Got User Display: %s\n", slpcall->slplink->remote_user);
1004 #endif
1005
1006 userlist = slpcall->slplink->session->userlist;
1007 account = slpcall->slplink->session->account;
1008
1009 /* TODO: I think we need better buddy icon core functions. */
1010 gaim_buddy_icons_set_for_user(account, slpcall->slplink->remote_user,
1011 (void *)data, size);
1012
1013 list = gaim_find_buddies(account, slpcall->slplink->remote_user);
1014
1015 for (sl = list; sl != NULL; sl = sl->next)
1016 {
1017 GaimBuddy *buddy = (GaimBuddy *)sl->data;
1018 gaim_blist_node_set_string((GaimBlistNode*)buddy, "icon_checksum", info);
1019 }
1020 g_slist_free(list);
1021
1022 #if 0
1023 /* Free one window slot */
1024 userlist->buddy_icon_window++;
1025
1026 gaim_debug_info("msn", "got_user_display(): buddy_icon_window++ yields =%d\n",
1027 userlist->buddy_icon_window);
1028
1029 msn_release_buddy_icon_request(userlist);
1030 #endif
1031 }
1032
1033 static void
1034 end_user_display(MsnSlpCall *slpcall, MsnSession *session)
1035 {
1036 MsnUserList *userlist;
1037
1038 g_return_if_fail(session != NULL);
1039
1040 #ifdef MSN_DEBUG_UD
1041 gaim_debug_info("msn", "End User Display\n");
1042 #endif
1043
1044 userlist = session->userlist;
1045
1046 /* If the session is being destroyed we better stop doing anything. */
1047 if (session->destroying)
1048 return;
1049
1050 /* Delay before freeing a buddy icon window slot and requesting the next icon, if appropriate.
1051 * If we don't delay, we'll rapidly hit the MSN equivalent of AIM's rate limiting; the server will
1052 * send us an error 800 like so:
1053 *
1054 * C: NS 000: XFR 21 SB
1055 * S: NS 000: 800 21
1056 */
1057 if (userlist->buddy_icon_request_timer) {
1058 /* Free the window slot used by this previous request */
1059 userlist->buddy_icon_window++;
1060
1061 /* Clear our pending timeout */
1062 gaim_timeout_remove(userlist->buddy_icon_request_timer);
1063 }
1064
1065 /* Wait BUDDY_ICON_DELAY ms before freeing our window slot and requesting the next icon. */
1066 userlist->buddy_icon_request_timer = gaim_timeout_add(BUDDY_ICON_DELAY,
1067 msn_release_buddy_icon_request_timeout, userlist);
1068 }
1069
1070 void
1071 msn_request_user_display(MsnUser *user)
1072 {
1073 GaimAccount *account;
1074 MsnSession *session;
1075 MsnSlpLink *slplink;
1076 MsnObject *obj;
1077 const char *info;
1078
1079 session = user->userlist->session;
1080 account = session->account;
1081
1082 slplink = msn_session_get_slplink(session, user->passport);
1083
1084 obj = msn_user_get_object(user);
1085
1086 info = msn_object_get_sha1c(obj);
1087
1088 if (g_ascii_strcasecmp(user->passport,
1089 gaim_account_get_username(account)))
1090 {
1091 msn_slplink_request_object(slplink, info, got_user_display,
1092 end_user_display, obj);
1093 }
1094 else
1095 {
1096 MsnObject *my_obj = NULL;
1097 gchar *data = NULL;
1098 gsize len = 0;
1099 GSList *sl, *list;
1100
1101 #ifdef MSN_DEBUG_UD
1102 gaim_debug_info("msn", "Requesting our own user display\n");
1103 #endif
1104
1105 my_obj = msn_user_get_object(session->user);
1106
1107 if (my_obj != NULL)
1108 {
1109 const char *filename = msn_object_get_real_location(my_obj);
1110
1111 if (filename != NULL)
1112 g_file_get_contents(filename, &data, &len, NULL);
1113 }
1114
1115 /* TODO: I think we need better buddy icon core functions. */
1116 gaim_buddy_icons_set_for_user(account, user->passport, (void *)data, len);
1117 g_free(data);
1118
1119 list = gaim_find_buddies(account, user->passport);
1120
1121 for (sl = list; sl != NULL; sl = sl->next)
1122 {
1123 GaimBuddy *buddy = (GaimBuddy *)sl->data;
1124 gaim_blist_node_set_string((GaimBlistNode*)buddy, "icon_checksum", info);
1125 }
1126 g_slist_free(list);
1127
1128 /* Free one window slot */
1129 session->userlist->buddy_icon_window++;
1130
1131 #ifdef MSN_DEBUG_UD
1132 gaim_debug_info("msn", "msn_request_user_display(): buddy_icon_window++ yields =%d\n",
1133 session->userlist->buddy_icon_window);
1134 #endif
1135
1136 msn_release_buddy_icon_request(session->userlist);
1137 }
1138 }

mercurial