comparison libpurple/protocols/msn/slp.c @ 31292:47b6eda87723

propagate from branch 'im.pidgin.pidgin' (head 07d0765c444a097af45c2650f54323afb900a07b) to branch 'im.pidgin.soc.2010.msn-tlc' (head f3998422a4724ab424e4e2328f58fc0504856557)
author masca@cpw.pidgin.im
date Mon, 19 Jul 2010 21:11:32 +0000
parents 7b1b7a4e0bb4
children 23be655cc688
comparison
equal deleted inserted replaced
30698:e874875a74a7 31292:47b6eda87723
19 * 19 *
20 * You should have received a copy of the GNU General Public License 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 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 22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
23 */ 23 */
24 #include "msn.h" 24
25 #include "internal.h"
26 #include "debug.h"
27
25 #include "slp.h" 28 #include "slp.h"
26 #include "slpcall.h" 29 #include "slpcall.h"
27 #include "slpmsg.h" 30 #include "slpmsg.h"
28 #include "msnutils.h" 31 #include "msnutils.h"
29 32
30 #include "object.h" 33 #include "object.h"
31 #include "user.h" 34 #include "user.h"
32 #include "switchboard.h" 35 #include "sbconn.h"
33 #include "directconn.h" 36 #include "directconn.h"
34 37 #include "p2p.h"
35 #include "smiley.h" 38 #include "xfer.h"
36 39
37 /* seconds to delay between sending buddy icon requests to the server. */ 40 /* seconds to delay between sending buddy icon requests to the server. */
38 #define BUDDY_ICON_DELAY 20 41 #define BUDDY_ICON_DELAY 20
39 42
40 static void request_user_display(MsnUser *user);
41
42
43 /**************************************************************************
44 * Util
45 **************************************************************************/
46
47 static char *
48 get_token(const char *str, const char *start, const char *end)
49 {
50 const char *c, *c2;
51
52 if ((c = strstr(str, start)) == NULL)
53 return NULL;
54
55 c += strlen(start);
56
57 if (end != NULL)
58 {
59 if ((c2 = strstr(c, end)) == NULL)
60 return NULL;
61
62 return g_strndup(c, c2 - c);
63 }
64 else
65 {
66 /* This has to be changed */
67 return g_strdup(c);
68 }
69
70 }
71
72 /**************************************************************************
73 * Xfer
74 **************************************************************************/
75
76 static void
77 msn_xfer_init(PurpleXfer *xfer)
78 {
79 MsnSlpCall *slpcall;
80 /* MsnSlpLink *slplink; */
81 char *content;
82
83 purple_debug_info("msn", "xfer_init\n");
84
85 slpcall = xfer->data;
86
87 /* Send Ok */
88 content = g_strdup_printf("SessionID: %lu\r\n\r\n",
89 slpcall->session_id);
90
91 msn_slp_send_ok(slpcall, slpcall->branch, "application/x-msnmsgr-sessionreqbody",
92 content);
93
94 g_free(content);
95 msn_slplink_send_queued_slpmsgs(slpcall->slplink);
96 }
97
98 void
99 msn_xfer_cancel(PurpleXfer *xfer)
100 {
101 MsnSlpCall *slpcall;
102 char *content;
103
104 g_return_if_fail(xfer != NULL);
105 g_return_if_fail(xfer->data != NULL);
106
107 slpcall = xfer->data;
108
109 if (purple_xfer_get_status(xfer) == PURPLE_XFER_STATUS_CANCEL_LOCAL)
110 {
111 if (slpcall->started)
112 {
113 msn_slpcall_close(slpcall);
114 }
115 else
116 {
117 content = g_strdup_printf("SessionID: %lu\r\n\r\n",
118 slpcall->session_id);
119
120 msn_slp_send_decline(slpcall, slpcall->branch, "application/x-msnmsgr-sessionreqbody",
121 content);
122
123 g_free(content);
124 msn_slplink_send_queued_slpmsgs(slpcall->slplink);
125
126 if (purple_xfer_get_type(xfer) == PURPLE_XFER_SEND)
127 slpcall->wasted = TRUE;
128 else
129 msn_slpcall_destroy(slpcall);
130 }
131 }
132 }
133
134 gssize
135 msn_xfer_write(const guchar *data, gsize len, PurpleXfer *xfer)
136 {
137 MsnSlpCall *slpcall;
138
139 g_return_val_if_fail(xfer != NULL, -1);
140 g_return_val_if_fail(data != NULL, -1);
141 g_return_val_if_fail(len > 0, -1);
142
143 g_return_val_if_fail(purple_xfer_get_type(xfer) == PURPLE_XFER_SEND, -1);
144
145 slpcall = xfer->data;
146 /* Not sure I trust it'll be there */
147 g_return_val_if_fail(slpcall != NULL, -1);
148
149 g_return_val_if_fail(slpcall->xfer_msg != NULL, -1);
150
151 slpcall->u.outgoing.len = len;
152 slpcall->u.outgoing.data = data;
153 msn_slplink_send_msgpart(slpcall->slplink, slpcall->xfer_msg);
154 msn_message_unref(slpcall->xfer_msg->msg);
155 return MIN(1202, len);
156 }
157
158 gssize
159 msn_xfer_read(guchar **data, PurpleXfer *xfer)
160 {
161 MsnSlpCall *slpcall;
162 gsize len;
163
164 g_return_val_if_fail(xfer != NULL, -1);
165 g_return_val_if_fail(data != NULL, -1);
166
167 g_return_val_if_fail(purple_xfer_get_type(xfer) == PURPLE_XFER_RECEIVE, -1);
168
169 slpcall = xfer->data;
170 /* Not sure I trust it'll be there */
171 g_return_val_if_fail(slpcall != NULL, -1);
172
173 /* Just pass up the whole GByteArray. We'll make another. */
174 *data = slpcall->u.incoming_data->data;
175 len = slpcall->u.incoming_data->len;
176
177 g_byte_array_free(slpcall->u.incoming_data, FALSE);
178 slpcall->u.incoming_data = g_byte_array_new();
179
180 return len;
181 }
182
183 void
184 msn_xfer_end_cb(MsnSlpCall *slpcall, MsnSession *session)
185 {
186 if ((purple_xfer_get_status(slpcall->xfer) != PURPLE_XFER_STATUS_DONE) &&
187 (purple_xfer_get_status(slpcall->xfer) != PURPLE_XFER_STATUS_CANCEL_REMOTE) &&
188 (purple_xfer_get_status(slpcall->xfer) != PURPLE_XFER_STATUS_CANCEL_LOCAL))
189 {
190 purple_xfer_cancel_remote(slpcall->xfer);
191 }
192 }
193
194 void
195 msn_xfer_completed_cb(MsnSlpCall *slpcall, const guchar *body,
196 gsize size)
197 {
198 PurpleXfer *xfer = slpcall->xfer;
199
200 purple_xfer_set_completed(xfer, TRUE);
201 purple_xfer_end(xfer);
202 }
203 43
204 /************************************************************************** 44 /**************************************************************************
205 * SLP Control 45 * SLP Control
206 **************************************************************************/ 46 **************************************************************************/
207 47
243 slpmsg->text_body = TRUE; 83 slpmsg->text_body = TRUE;
244 84
245 msn_slplink_queue_slpmsg(slplink, slpmsg); 85 msn_slplink_queue_slpmsg(slplink, slpmsg);
246 } 86 }
247 87
248 /* XXX: this could be improved if we tracked custom smileys
249 * per-protocol, per-account, per-session or (ideally) per-conversation
250 */
251 static PurpleStoredImage *
252 find_valid_emoticon(PurpleAccount *account, const char *path)
253 {
254 GList *smileys;
255
256 if (!purple_account_get_bool(account, "custom_smileys", TRUE))
257 return NULL;
258
259 smileys = purple_smileys_get_all();
260
261 for (; smileys; smileys = g_list_delete_link(smileys, smileys)) {
262 PurpleSmiley *smiley;
263 PurpleStoredImage *img;
264
265 smiley = smileys->data;
266 img = purple_smiley_get_stored_image(smiley);
267
268 if (purple_strequal(path, purple_imgstore_get_filename(img))) {
269 g_list_free(smileys);
270 return img;
271 }
272
273 purple_imgstore_unref(img);
274 }
275
276 purple_debug_error("msn", "Received illegal request for file %s\n", path);
277 return NULL;
278 }
279
280 static char *
281 parse_dc_nonce(const char *content, MsnDirectConnNonceType *ntype)
282 {
283 char *nonce;
284
285 *ntype = DC_NONCE_UNKNOWN;
286
287 nonce = get_token(content, "Hashed-Nonce: {", "}\r\n");
288 if (nonce) {
289 *ntype = DC_NONCE_SHA1;
290 } else {
291 guint32 n1, n6;
292 guint16 n2, n3, n4, n5;
293 nonce = get_token(content, "Nonce: {", "}\r\n");
294 if (nonce
295 && sscanf(nonce, "%08x-%04hx-%04hx-%04hx-%04hx%08x",
296 &n1, &n2, &n3, &n4, &n5, &n6) == 6) {
297 *ntype = DC_NONCE_PLAIN;
298 g_free(nonce);
299 nonce = g_malloc(16);
300 *(guint32 *)(nonce + 0) = GUINT32_TO_LE(n1);
301 *(guint16 *)(nonce + 4) = GUINT16_TO_LE(n2);
302 *(guint16 *)(nonce + 6) = GUINT16_TO_LE(n3);
303 *(guint16 *)(nonce + 8) = GUINT16_TO_BE(n4);
304 *(guint16 *)(nonce + 10) = GUINT16_TO_BE(n5);
305 *(guint32 *)(nonce + 12) = GUINT32_TO_BE(n6);
306 } else {
307 /* Invalid nonce, so ignore request */
308 g_free(nonce);
309 nonce = NULL;
310 }
311 }
312
313 return nonce;
314 }
315
316 static void
317 msn_slp_process_transresp(MsnSlpCall *slpcall, const char *content)
318 {
319 /* A direct connection negotiation response */
320 char *bridge;
321 char *nonce;
322 char *listening;
323 MsnDirectConn *dc = slpcall->slplink->dc;
324 MsnDirectConnNonceType ntype;
325
326 purple_debug_info("msn", "process_transresp\n");
327
328 /* Direct connections are disabled. */
329 if (!purple_account_get_bool(slpcall->slplink->session->account, "direct_connect", TRUE))
330 return;
331
332 g_return_if_fail(dc != NULL);
333 g_return_if_fail(dc->state == DC_STATE_CLOSED);
334
335 bridge = get_token(content, "Bridge: ", "\r\n");
336 nonce = parse_dc_nonce(content, &ntype);
337 listening = get_token(content, "Listening: ", "\r\n");
338 if (listening && bridge && !strcmp(bridge, "TCPv1")) {
339 /* Ok, the client supports direct TCP connection */
340
341 /* We always need this. */
342 if (ntype == DC_NONCE_SHA1) {
343 strncpy(dc->remote_nonce, nonce, 36);
344 dc->remote_nonce[36] = '\0';
345 }
346
347 if (!strcasecmp(listening, "false")) {
348 if (dc->listen_data != NULL) {
349 /*
350 * We'll listen for incoming connections but
351 * the listening socket isn't ready yet so we cannot
352 * send the INVITE packet now. Put the slpcall into waiting mode
353 * and let the callback send the invite.
354 */
355 slpcall->wait_for_socket = TRUE;
356
357 } else if (dc->listenfd != -1) {
358 /* The listening socket is ready. Send the INVITE here. */
359 msn_dc_send_invite(dc);
360
361 } else {
362 /* We weren't able to create a listener either. Use SB. */
363 msn_dc_fallback_to_sb(dc);
364 }
365
366 } else {
367 /*
368 * We should connect to the client so parse
369 * IP/port from response.
370 */
371 char *ip, *port_str;
372 int port = 0;
373
374 if (ntype == DC_NONCE_PLAIN) {
375 /* Only needed for listening side. */
376 memcpy(dc->nonce, nonce, 16);
377 }
378
379 /* Cancel any listen attempts because we don't need them. */
380 if (dc->listenfd_handle != 0) {
381 purple_input_remove(dc->listenfd_handle);
382 dc->listenfd_handle = 0;
383 }
384 if (dc->connect_timeout_handle != 0) {
385 purple_timeout_remove(dc->connect_timeout_handle);
386 dc->connect_timeout_handle = 0;
387 }
388 if (dc->listenfd != -1) {
389 purple_network_remove_port_mapping(dc->listenfd);
390 close(dc->listenfd);
391 dc->listenfd = -1;
392 }
393 if (dc->listen_data != NULL) {
394 purple_network_listen_cancel(dc->listen_data);
395 dc->listen_data = NULL;
396 }
397
398 /* Save external IP/port for later use. We'll try local connection first. */
399 dc->ext_ip = get_token(content, "IPv4External-Addrs: ", "\r\n");
400 port_str = get_token(content, "IPv4External-Port: ", "\r\n");
401 if (port_str) {
402 dc->ext_port = atoi(port_str);
403 g_free(port_str);
404 }
405
406 ip = get_token(content, "IPv4Internal-Addrs: ", "\r\n");
407 port_str = get_token(content, "IPv4Internal-Port: ", "\r\n");
408 if (port_str) {
409 port = atoi(port_str);
410 g_free(port_str);
411 }
412
413 if (ip && port) {
414 /* Try internal address first */
415 dc->connect_data = purple_proxy_connect(
416 NULL,
417 slpcall->slplink->session->account,
418 ip,
419 port,
420 msn_dc_connected_to_peer_cb,
421 dc
422 );
423
424 if (dc->connect_data) {
425 /* Add connect timeout handle */
426 dc->connect_timeout_handle = purple_timeout_add_seconds(
427 DC_OUTGOING_TIMEOUT,
428 msn_dc_outgoing_connection_timeout_cb,
429 dc
430 );
431 } else {
432 /*
433 * Connection failed
434 * Try external IP/port (if specified)
435 */
436 msn_dc_outgoing_connection_timeout_cb(dc);
437 }
438
439 } else {
440 /*
441 * Omitted or invalid internal IP address / port
442 * Try external IP/port (if specified)
443 */
444 msn_dc_outgoing_connection_timeout_cb(dc);
445 }
446
447 g_free(ip);
448 }
449
450 } else {
451 /*
452 * Invalid direct connect invitation or
453 * TCP connection is not supported
454 */
455 }
456
457 g_free(listening);
458 g_free(nonce);
459 g_free(bridge);
460
461 return;
462 }
463
464 static void
465 got_sessionreq(MsnSlpCall *slpcall, const char *branch,
466 const char *euf_guid, const char *context)
467 {
468 gboolean accepted = FALSE;
469
470 if (!strcmp(euf_guid, MSN_OBJ_GUID))
471 {
472 /* Emoticon or UserDisplay */
473 char *content;
474 gsize len;
475 MsnSlpLink *slplink;
476 MsnSlpMessage *slpmsg;
477 MsnObject *obj;
478 char *msnobj_data;
479 PurpleStoredImage *img = NULL;
480 int type;
481
482 /* Send Ok */
483 content = g_strdup_printf("SessionID: %lu\r\n\r\n",
484 slpcall->session_id);
485
486 msn_slp_send_ok(slpcall, branch, "application/x-msnmsgr-sessionreqbody",
487 content);
488
489 g_free(content);
490
491 slplink = slpcall->slplink;
492
493 msnobj_data = (char *)purple_base64_decode(context, &len);
494 obj = msn_object_new_from_string(msnobj_data);
495 type = msn_object_get_type(obj);
496 g_free(msnobj_data);
497 if (type == MSN_OBJECT_EMOTICON) {
498 img = find_valid_emoticon(slplink->session->account, obj->location);
499 } else if (type == MSN_OBJECT_USERTILE) {
500 img = msn_object_get_image(obj);
501 if (img)
502 purple_imgstore_ref(img);
503 }
504 msn_object_destroy(obj);
505
506 if (img != NULL) {
507 /* DATA PREP */
508 slpmsg = msn_slpmsg_new(slplink);
509 slpmsg->slpcall = slpcall;
510 slpmsg->session_id = slpcall->session_id;
511 msn_slpmsg_set_body(slpmsg, NULL, 4);
512 slpmsg->info = "SLP DATA PREP";
513 msn_slplink_queue_slpmsg(slplink, slpmsg);
514
515 /* DATA */
516 slpmsg = msn_slpmsg_new(slplink);
517 slpmsg->slpcall = slpcall;
518 slpmsg->flags = 0x20;
519 slpmsg->info = "SLP DATA";
520 msn_slpmsg_set_image(slpmsg, img);
521 msn_slplink_queue_slpmsg(slplink, slpmsg);
522 purple_imgstore_unref(img);
523
524 accepted = TRUE;
525
526 } else {
527 purple_debug_error("msn", "Wrong object.\n");
528 }
529 }
530
531 else if (!strcmp(euf_guid, MSN_FT_GUID))
532 {
533 /* File Transfer */
534 PurpleAccount *account;
535 PurpleXfer *xfer;
536 MsnFileContext *header;
537 gsize bin_len;
538 guint32 file_size;
539 char *file_name;
540
541 account = slpcall->slplink->session->account;
542
543 slpcall->end_cb = msn_xfer_end_cb;
544 slpcall->branch = g_strdup(branch);
545
546 slpcall->pending = TRUE;
547
548 xfer = purple_xfer_new(account, PURPLE_XFER_RECEIVE,
549 slpcall->slplink->remote_user);
550
551 header = (MsnFileContext *)purple_base64_decode(context, &bin_len);
552 if (bin_len >= sizeof(MsnFileContext) - 1 &&
553 (header->version == 2 ||
554 (header->version == 3 && header->length == sizeof(MsnFileContext) + 63))) {
555 file_size = GUINT64_FROM_LE(header->file_size);
556
557 file_name = g_convert((const gchar *)&header->file_name,
558 MAX_FILE_NAME_LEN * 2,
559 "UTF-8", "UTF-16LE",
560 NULL, NULL, NULL);
561
562 purple_xfer_set_filename(xfer, file_name ? file_name : "");
563 g_free(file_name);
564 purple_xfer_set_size(xfer, file_size);
565 purple_xfer_set_init_fnc(xfer, msn_xfer_init);
566 purple_xfer_set_request_denied_fnc(xfer, msn_xfer_cancel);
567 purple_xfer_set_cancel_recv_fnc(xfer, msn_xfer_cancel);
568 purple_xfer_set_read_fnc(xfer, msn_xfer_read);
569 purple_xfer_set_write_fnc(xfer, msn_xfer_write);
570
571 slpcall->u.incoming_data = g_byte_array_new();
572
573 slpcall->xfer = xfer;
574 purple_xfer_ref(slpcall->xfer);
575
576 xfer->data = slpcall;
577
578 if (header->type == 0 && bin_len >= sizeof(MsnFileContext)) {
579 purple_xfer_set_thumbnail(xfer, &header->preview,
580 bin_len - sizeof(MsnFileContext),
581 "image/png");
582 }
583
584 purple_xfer_request(xfer);
585 }
586 g_free(header);
587
588 accepted = TRUE;
589
590 } else if (!strcmp(euf_guid, MSN_CAM_REQUEST_GUID)) {
591 purple_debug_info("msn", "Cam request.\n");
592 if (slpcall && slpcall->slplink &&
593 slpcall->slplink->session) {
594 PurpleConversation *conv;
595 gchar *from = slpcall->slplink->remote_user;
596 conv = purple_find_conversation_with_account(
597 PURPLE_CONV_TYPE_IM, from,
598 slpcall->slplink->session->account);
599 if (conv) {
600 char *buf;
601 buf = g_strdup_printf(
602 _("%s requests to view your "
603 "webcam, but this request is "
604 "not yet supported."), from);
605 purple_conversation_write(conv, NULL, buf,
606 PURPLE_MESSAGE_SYSTEM |
607 PURPLE_MESSAGE_NOTIFY,
608 time(NULL));
609 g_free(buf);
610 }
611 }
612
613 } else if (!strcmp(euf_guid, MSN_CAM_GUID)) {
614 purple_debug_info("msn", "Cam invite.\n");
615 if (slpcall && slpcall->slplink &&
616 slpcall->slplink->session) {
617 PurpleConversation *conv;
618 gchar *from = slpcall->slplink->remote_user;
619 conv = purple_find_conversation_with_account(
620 PURPLE_CONV_TYPE_IM, from,
621 slpcall->slplink->session->account);
622 if (conv) {
623 char *buf;
624 buf = g_strdup_printf(
625 _("%s invited you to view his/her webcam, but "
626 "this is not yet supported."), from);
627 purple_conversation_write(conv, NULL, buf,
628 PURPLE_MESSAGE_SYSTEM |
629 PURPLE_MESSAGE_NOTIFY,
630 time(NULL));
631 g_free(buf);
632 }
633 }
634
635 } else
636 purple_debug_warning("msn", "SLP SessionReq with unknown EUF-GUID: %s\n", euf_guid);
637
638 if (!accepted) {
639 char *content = g_strdup_printf("SessionID: %lu\r\n\r\n",
640 slpcall->session_id);
641 msn_slp_send_decline(slpcall, branch, "application/x-msnmsgr-sessionreqbody", content);
642 g_free(content);
643 }
644 }
645
646 void
647 send_bye(MsnSlpCall *slpcall, const char *type)
648 {
649 MsnSlpLink *slplink;
650 PurpleAccount *account;
651 MsnSlpMessage *slpmsg;
652 char *header;
653
654 slplink = slpcall->slplink;
655
656 g_return_if_fail(slplink != NULL);
657
658 account = slplink->session->account;
659
660 header = g_strdup_printf("BYE MSNMSGR:%s MSNSLP/1.0",
661 purple_account_get_username(account));
662
663 slpmsg = msn_slpmsg_sip_new(slpcall, 0, header,
664 "A0D624A6-6C0C-4283-A9E0-BC97B4B46D32",
665 type,
666 "\r\n");
667 g_free(header);
668
669 slpmsg->info = "SLP BYE";
670 slpmsg->text_body = TRUE;
671
672 msn_slplink_queue_slpmsg(slplink, slpmsg);
673 }
674
675 static void
676 got_invite(MsnSlpCall *slpcall,
677 const char *branch, const char *type, const char *content)
678 {
679 MsnSlpLink *slplink;
680
681 slplink = slpcall->slplink;
682
683 if (!strcmp(type, "application/x-msnmsgr-sessionreqbody"))
684 {
685 char *euf_guid, *context;
686 char *temp;
687
688 euf_guid = get_token(content, "EUF-GUID: {", "}\r\n");
689
690 temp = get_token(content, "SessionID: ", "\r\n");
691 if (temp != NULL)
692 slpcall->session_id = atoi(temp);
693 g_free(temp);
694
695 temp = get_token(content, "AppID: ", "\r\n");
696 if (temp != NULL)
697 slpcall->app_id = atoi(temp);
698 g_free(temp);
699
700 context = get_token(content, "Context: ", "\r\n");
701
702 if (context != NULL)
703 got_sessionreq(slpcall, branch, euf_guid, context);
704
705 g_free(context);
706 g_free(euf_guid);
707 }
708 else if (!strcmp(type, "application/x-msnmsgr-transreqbody"))
709 {
710 /* A direct connection negotiation request */
711 char *bridges;
712 char *nonce;
713 MsnDirectConnNonceType ntype;
714
715 purple_debug_info("msn", "got_invite: transreqbody received\n");
716
717 /* Direct connections may be disabled. */
718 if (!purple_account_get_bool(slplink->session->account, "direct_connect", TRUE)) {
719 msn_slp_send_ok(slpcall, branch,
720 "application/x-msnmsgr-transrespbody",
721 "Bridge: TCPv1\r\n"
722 "Listening: false\r\n"
723 "Nonce: {00000000-0000-0000-0000-000000000000}\r\n"
724 "\r\n");
725 msn_slpcall_session_init(slpcall);
726
727 return;
728 }
729
730 /* Don't do anything if we already have a direct connection */
731 if (slplink->dc != NULL)
732 return;
733
734 bridges = get_token(content, "Bridges: ", "\r\n");
735 nonce = parse_dc_nonce(content, &ntype);
736 if (bridges && strstr(bridges, "TCPv1") != NULL) {
737 /*
738 * Ok, the client supports direct TCP connection
739 * Try to create a listening port
740 */
741 MsnDirectConn *dc;
742
743 dc = msn_dc_new(slpcall);
744 if (ntype == DC_NONCE_PLAIN) {
745 /* There is only one nonce for plain auth. */
746 dc->nonce_type = ntype;
747 memcpy(dc->nonce, nonce, 16);
748 } else if (ntype == DC_NONCE_SHA1) {
749 /* Each side has a nonce in SHA1 auth. */
750 dc->nonce_type = ntype;
751 strncpy(dc->remote_nonce, nonce, 36);
752 dc->remote_nonce[36] = '\0';
753 }
754
755 dc->listen_data = purple_network_listen_range(
756 0, 0,
757 SOCK_STREAM,
758 msn_dc_listen_socket_created_cb,
759 dc
760 );
761
762 if (dc->listen_data == NULL) {
763 /* Listen socket creation failed */
764
765 purple_debug_info("msn", "got_invite: listening failed\n");
766
767 if (dc->nonce_type != DC_NONCE_PLAIN)
768 msn_slp_send_ok(slpcall, branch,
769 "application/x-msnmsgr-transrespbody",
770 "Bridge: TCPv1\r\n"
771 "Listening: false\r\n"
772 "Hashed-Nonce: {00000000-0000-0000-0000-000000000000}\r\n"
773 "\r\n");
774 else
775 msn_slp_send_ok(slpcall, branch,
776 "application/x-msnmsgr-transrespbody",
777 "Bridge: TCPv1\r\n"
778 "Listening: false\r\n"
779 "Nonce: {00000000-0000-0000-0000-000000000000}\r\n"
780 "\r\n");
781
782 } else {
783 /*
784 * Listen socket created successfully.
785 * Don't send anything here because we don't know the parameters
786 * of the created socket yet. msn_dc_send_ok will be called from
787 * the callback function: dc_listen_socket_created_cb
788 */
789 purple_debug_info("msn", "got_invite: listening socket created\n");
790
791 dc->send_connection_info_msg_cb = msn_dc_send_ok;
792 slpcall->wait_for_socket = TRUE;
793 }
794
795 } else {
796 /*
797 * Invalid direct connect invitation or
798 * TCP connection is not supported.
799 */
800 }
801
802 g_free(nonce);
803 g_free(bridges);
804 }
805 else if (!strcmp(type, "application/x-msnmsgr-transrespbody"))
806 {
807 /* A direct connection negotiation response */
808 msn_slp_process_transresp(slpcall, content);
809 }
810 }
811
812 static void
813 got_ok(MsnSlpCall *slpcall,
814 const char *type, const char *content)
815 {
816 g_return_if_fail(slpcall != NULL);
817 g_return_if_fail(type != NULL);
818
819 if (!strcmp(type, "application/x-msnmsgr-sessionreqbody"))
820 {
821 char *content;
822 char *header;
823 char *nonce = NULL;
824 MsnSession *session = slpcall->slplink->session;
825 MsnSlpMessage *msg;
826 MsnDirectConn *dc;
827 MsnUser *user;
828
829 if (!purple_account_get_bool(session->account, "direct_connect", TRUE)) {
830 /* Don't attempt a direct connection if disabled. */
831 msn_slpcall_session_init(slpcall);
832 return;
833 }
834
835 if (slpcall->slplink->dc != NULL) {
836 /* If we already have an established direct connection
837 * then just start the transfer.
838 */
839 msn_slpcall_session_init(slpcall);
840 return;
841 }
842
843 user = msn_userlist_find_user(session->userlist,
844 slpcall->slplink->remote_user);
845 if (!user || !(user->clientid & 0xF0000000)) {
846 /* Just start a normal SB transfer. */
847 msn_slpcall_session_init(slpcall);
848 return;
849 }
850
851 /* Try direct file transfer by sending a second INVITE */
852 dc = msn_dc_new(slpcall);
853 slpcall->branch = rand_guid();
854
855 dc->listen_data = purple_network_listen_range(
856 0, 0,
857 SOCK_STREAM,
858 msn_dc_listen_socket_created_cb,
859 dc
860 );
861
862 header = g_strdup_printf(
863 "INVITE MSNMSGR:%s MSNSLP/1.0",
864 slpcall->slplink->remote_user
865 );
866
867 if (dc->nonce_type == DC_NONCE_SHA1)
868 nonce = g_strdup_printf("Hashed-Nonce: {%s}\r\n", dc->nonce_hash);
869
870 if (dc->listen_data == NULL) {
871 /* Listen socket creation failed */
872 purple_debug_info("msn", "got_ok: listening failed\n");
873
874 content = g_strdup_printf(
875 "Bridges: TCPv1\r\n"
876 "NetID: %u\r\n"
877 "Conn-Type: IP-Restrict-NAT\r\n"
878 "UPnPNat: false\r\n"
879 "ICF: false\r\n"
880 "%s"
881 "\r\n",
882
883 rand() % G_MAXUINT32,
884 nonce ? nonce : ""
885 );
886
887 } else {
888 /* Listen socket created successfully. */
889 purple_debug_info("msn", "got_ok: listening socket created\n");
890
891 content = g_strdup_printf(
892 "Bridges: TCPv1\r\n"
893 "NetID: 0\r\n"
894 "Conn-Type: Direct-Connect\r\n"
895 "UPnPNat: false\r\n"
896 "ICF: false\r\n"
897 "%s"
898 "\r\n",
899
900 nonce ? nonce : ""
901 );
902 }
903
904 msg = msn_slpmsg_sip_new(
905 slpcall,
906 0,
907 header,
908 slpcall->branch,
909 "application/x-msnmsgr-transreqbody",
910 content
911 );
912 msg->info = "DC INVITE";
913 msg->text_body = TRUE;
914 g_free(nonce);
915 g_free(header);
916 g_free(content);
917
918 msn_slplink_queue_slpmsg(slpcall->slplink, msg);
919 }
920 else if (!strcmp(type, "application/x-msnmsgr-transreqbody"))
921 {
922 /* Do we get this? */
923 purple_debug_info("msn", "OK with transreqbody\n");
924 }
925 else if (!strcmp(type, "application/x-msnmsgr-transrespbody"))
926 {
927 msn_slp_process_transresp(slpcall, content);
928 }
929 }
930
931 static void
932 got_error(MsnSlpCall *slpcall,
933 const char *error, const char *type, const char *content)
934 {
935 /* It's not valid. Kill this off. */
936 purple_debug_error("msn", "Received non-OK result: %s\n",
937 error ? error : "Unknown");
938
939 if (type && !strcmp(type, "application/x-msnmsgr-transreqbody")) {
940 MsnDirectConn *dc = slpcall->slplink->dc;
941 if (dc) {
942 msn_dc_fallback_to_sb(dc);
943 return;
944 }
945 }
946
947 slpcall->wasted = TRUE;
948 }
949
950 MsnSlpCall *
951 msn_slp_sip_recv(MsnSlpLink *slplink, const char *body)
952 {
953 MsnSlpCall *slpcall;
954
955 if (body == NULL)
956 {
957 purple_debug_warning("msn", "received bogus message\n");
958 return NULL;
959 }
960
961 if (!strncmp(body, "INVITE", strlen("INVITE")))
962 {
963 char *branch;
964 char *call_id;
965 char *content;
966 char *content_type;
967
968 /* From: <msnmsgr:buddy@hotmail.com> */
969 #if 0
970 slpcall->remote_user = get_token(body, "From: <msnmsgr:", ">\r\n");
971 #endif
972
973 branch = get_token(body, ";branch={", "}");
974
975 call_id = get_token(body, "Call-ID: {", "}");
976
977 #if 0
978 long content_len = -1;
979
980 temp = get_token(body, "Content-Length: ", "\r\n");
981 if (temp != NULL)
982 content_len = atoi(temp);
983 g_free(temp);
984 #endif
985 content_type = get_token(body, "Content-Type: ", "\r\n");
986
987 content = get_token(body, "\r\n\r\n", NULL);
988
989 slpcall = NULL;
990 if (branch && call_id)
991 {
992 slpcall = msn_slplink_find_slp_call(slplink, call_id);
993 if (slpcall)
994 {
995 g_free(slpcall->branch);
996 slpcall->branch = g_strdup(branch);
997 got_invite(slpcall, branch, content_type, content);
998 }
999 else if (content_type && content)
1000 {
1001 slpcall = msn_slpcall_new(slplink);
1002 slpcall->id = g_strdup(call_id);
1003 got_invite(slpcall, branch, content_type, content);
1004 }
1005 }
1006
1007 g_free(call_id);
1008 g_free(branch);
1009 g_free(content_type);
1010 g_free(content);
1011 }
1012 else if (!strncmp(body, "MSNSLP/1.0 ", strlen("MSNSLP/1.0 ")))
1013 {
1014 char *content;
1015 char *content_type;
1016 /* Make sure this is "OK" */
1017 const char *status = body + strlen("MSNSLP/1.0 ");
1018 char *call_id;
1019
1020 call_id = get_token(body, "Call-ID: {", "}");
1021 slpcall = msn_slplink_find_slp_call(slplink, call_id);
1022 g_free(call_id);
1023
1024 g_return_val_if_fail(slpcall != NULL, NULL);
1025
1026 content_type = get_token(body, "Content-Type: ", "\r\n");
1027
1028 content = get_token(body, "\r\n\r\n", NULL);
1029
1030 if (strncmp(status, "200 OK", 6))
1031 {
1032 char *error = NULL;
1033 const char *c;
1034
1035 /* Eww */
1036 if ((c = strchr(status, '\r')) || (c = strchr(status, '\n')) ||
1037 (c = strchr(status, '\0')))
1038 {
1039 size_t len = c - status;
1040 error = g_strndup(status, len);
1041 }
1042
1043 got_error(slpcall, error, content_type, content);
1044 g_free(error);
1045
1046 } else {
1047 /* Everything's just dandy */
1048 got_ok(slpcall, content_type, content);
1049 }
1050
1051 g_free(content_type);
1052 g_free(content);
1053 }
1054 else if (!strncmp(body, "BYE", strlen("BYE")))
1055 {
1056 char *call_id;
1057
1058 call_id = get_token(body, "Call-ID: {", "}");
1059 slpcall = msn_slplink_find_slp_call(slplink, call_id);
1060 g_free(call_id);
1061
1062 if (slpcall != NULL)
1063 slpcall->wasted = TRUE;
1064
1065 /* msn_slpcall_destroy(slpcall); */
1066 }
1067 else
1068 slpcall = NULL;
1069
1070 return slpcall;
1071 }
1072
1073 /************************************************************************** 88 /**************************************************************************
1074 * Msg Callbacks 89 * Msg Callbacks
1075 **************************************************************************/ 90 **************************************************************************/
1076
1077 void
1078 msn_p2p_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
1079 {
1080 MsnSession *session;
1081 MsnSlpLink *slplink;
1082 const char *data;
1083 gsize len;
1084
1085 session = cmdproc->servconn->session;
1086 slplink = msn_session_get_slplink(session, msg->remote_user);
1087
1088 if (slplink->swboard == NULL)
1089 {
1090 /*
1091 * We will need swboard in order to change its flags. If its
1092 * NULL, something has probably gone wrong earlier on. I
1093 * didn't want to do this, but MSN 7 is somehow causing us
1094 * to crash here, I couldn't reproduce it to debug more,
1095 * and people are reporting bugs. Hopefully this doesn't
1096 * cause more crashes. Stu.
1097 */
1098 if (cmdproc->data == NULL)
1099 g_warning("msn_p2p_msg cmdproc->data was NULL\n");
1100 else {
1101 slplink->swboard = (MsnSwitchBoard *)cmdproc->data;
1102 slplink->swboard->slplinks = g_list_prepend(slplink->swboard->slplinks, slplink);
1103 }
1104 }
1105
1106 data = msn_message_get_bin_data(msg, &len);
1107
1108 msn_slplink_process_msg(slplink, &msg->msnslp_header, data, len);
1109 }
1110
1111 static void
1112 got_emoticon(MsnSlpCall *slpcall,
1113 const guchar *data, gsize size)
1114 {
1115 PurpleConversation *conv;
1116 MsnSwitchBoard *swboard;
1117
1118 swboard = slpcall->slplink->swboard;
1119 conv = swboard->conv;
1120
1121 if (conv) {
1122 /* FIXME: it would be better if we wrote the data as we received it
1123 instead of all at once, calling write multiple times and
1124 close once at the very end
1125 */
1126 purple_conv_custom_smiley_write(conv, slpcall->data_info, data, size);
1127 purple_conv_custom_smiley_close(conv, slpcall->data_info );
1128 }
1129 if (purple_debug_is_verbose())
1130 purple_debug_info("msn", "Got smiley: %s\n", slpcall->data_info);
1131 }
1132
1133 void
1134 msn_emoticon_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
1135 {
1136 MsnSession *session;
1137 MsnSlpLink *slplink;
1138 MsnSwitchBoard *swboard;
1139 MsnObject *obj;
1140 char **tokens;
1141 char *smile, *body_str;
1142 const char *body, *who, *sha1;
1143 guint tok;
1144 size_t body_len;
1145
1146 PurpleConversation *conv;
1147
1148 session = cmdproc->servconn->session;
1149
1150 if (!purple_account_get_bool(session->account, "custom_smileys", TRUE))
1151 return;
1152
1153 swboard = cmdproc->data;
1154 conv = swboard->conv;
1155
1156 body = msn_message_get_bin_data(msg, &body_len);
1157 if (!body || !body_len)
1158 return;
1159 body_str = g_strndup(body, body_len);
1160
1161 /* MSN Messenger 7 may send more than one MSNObject in a single message...
1162 * Maybe 10 tokens is a reasonable max value. */
1163 tokens = g_strsplit(body_str, "\t", 10);
1164
1165 g_free(body_str);
1166
1167 for (tok = 0; tok < 9; tok += 2) {
1168 if (tokens[tok] == NULL || tokens[tok + 1] == NULL) {
1169 break;
1170 }
1171
1172 smile = tokens[tok];
1173 obj = msn_object_new_from_string(purple_url_decode(tokens[tok + 1]));
1174
1175 if (obj == NULL)
1176 break;
1177
1178 who = msn_object_get_creator(obj);
1179 sha1 = msn_object_get_sha1(obj);
1180
1181 slplink = msn_session_get_slplink(session, who);
1182 if (slplink->swboard != swboard) {
1183 if (slplink->swboard != NULL)
1184 /*
1185 * Apparently we're using a different switchboard now or
1186 * something? I don't know if this is normal, but it
1187 * definitely happens. So make sure the old switchboard
1188 * doesn't still have a reference to us.
1189 */
1190 slplink->swboard->slplinks = g_list_remove(slplink->swboard->slplinks, slplink);
1191 slplink->swboard = swboard;
1192 slplink->swboard->slplinks = g_list_prepend(slplink->swboard->slplinks, slplink);
1193 }
1194
1195 /* If the conversation doesn't exist then this is a custom smiley
1196 * used in the first message in a MSN conversation: we need to create
1197 * the conversation now, otherwise the custom smiley won't be shown.
1198 * This happens because every GtkIMHtml has its own smiley tree: if
1199 * the conversation doesn't exist then we cannot associate the new
1200 * smiley with its GtkIMHtml widget. */
1201 if (!conv) {
1202 conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, session->account, who);
1203 }
1204
1205 if (purple_conv_custom_smiley_add(conv, smile, "sha1", sha1, TRUE)) {
1206 msn_slplink_request_object(slplink, smile, got_emoticon, NULL, obj);
1207 }
1208
1209 msn_object_destroy(obj);
1210 obj = NULL;
1211 who = NULL;
1212 sha1 = NULL;
1213 }
1214 g_strfreev(tokens);
1215 }
1216
1217 static gboolean
1218 buddy_icon_cached(PurpleConnection *gc, MsnObject *obj)
1219 {
1220 PurpleAccount *account;
1221 PurpleBuddy *buddy;
1222 const char *old;
1223 const char *new;
1224
1225 g_return_val_if_fail(obj != NULL, FALSE);
1226
1227 account = purple_connection_get_account(gc);
1228
1229 buddy = purple_find_buddy(account, msn_object_get_creator(obj));
1230 if (buddy == NULL)
1231 return FALSE;
1232
1233 old = purple_buddy_icons_get_checksum_for_user(buddy);
1234 new = msn_object_get_sha1(obj);
1235
1236 if (new == NULL)
1237 return FALSE;
1238
1239 /* If the old and new checksums are the same, and the file actually exists,
1240 * then return TRUE */
1241 if (old != NULL && !strcmp(old, new))
1242 return TRUE;
1243
1244 return FALSE;
1245 }
1246
1247 static void
1248 msn_release_buddy_icon_request(MsnUserList *userlist)
1249 {
1250 MsnUser *user;
1251
1252 g_return_if_fail(userlist != NULL);
1253
1254 if (purple_debug_is_verbose())
1255 purple_debug_info("msn", "Releasing buddy icon request\n");
1256
1257 if (userlist->buddy_icon_window > 0)
1258 {
1259 GQueue *queue;
1260 PurpleAccount *account;
1261 const char *username;
1262
1263 queue = userlist->buddy_icon_requests;
1264
1265 if (g_queue_is_empty(userlist->buddy_icon_requests))
1266 return;
1267
1268 user = g_queue_pop_head(queue);
1269
1270 account = userlist->session->account;
1271 username = user->passport;
1272
1273 userlist->buddy_icon_window--;
1274 request_user_display(user);
1275
1276 if (purple_debug_is_verbose())
1277 purple_debug_info("msn",
1278 "msn_release_buddy_icon_request(): buddy_icon_window-- yields =%d\n",
1279 userlist->buddy_icon_window);
1280 }
1281 }
1282 91
1283 /* 92 /*
1284 * Called on a timeout from end_user_display(). Frees a buddy icon window slow and dequeues the next 93 * Called on a timeout from end_user_display(). Frees a buddy icon window slow and dequeues the next
1285 * buddy icon request if there is one. 94 * buddy icon request if there is one.
1286 */ 95 */
1296 userlist->buddy_icon_request_timer = 0; 105 userlist->buddy_icon_request_timer = 0;
1297 106
1298 msn_release_buddy_icon_request(userlist); 107 msn_release_buddy_icon_request(userlist);
1299 108
1300 return FALSE; 109 return FALSE;
1301 }
1302
1303 void
1304 msn_queue_buddy_icon_request(MsnUser *user)
1305 {
1306 PurpleAccount *account;
1307 MsnObject *obj;
1308 GQueue *queue;
1309
1310 g_return_if_fail(user != NULL);
1311
1312 account = user->userlist->session->account;
1313
1314 obj = msn_user_get_object(user);
1315
1316 if (obj == NULL)
1317 {
1318 purple_buddy_icons_set_for_user(account, user->passport, NULL, 0, NULL);
1319 return;
1320 }
1321
1322 if (!buddy_icon_cached(account->gc, obj))
1323 {
1324 MsnUserList *userlist;
1325
1326 userlist = user->userlist;
1327 queue = userlist->buddy_icon_requests;
1328
1329 if (purple_debug_is_verbose())
1330 purple_debug_info("msn", "Queueing buddy icon request for %s (buddy_icon_window = %i)\n",
1331 user->passport, userlist->buddy_icon_window);
1332
1333 g_queue_push_tail(queue, user);
1334
1335 if (userlist->buddy_icon_window > 0)
1336 msn_release_buddy_icon_request(userlist);
1337 }
1338 } 110 }
1339 111
1340 static void 112 static void
1341 got_user_display(MsnSlpCall *slpcall, 113 got_user_display(MsnSlpCall *slpcall,
1342 const guchar *data, gsize size) 114 const guchar *data, gsize size)
1403 userlist->buddy_icon_request_timer = purple_timeout_add_seconds(BUDDY_ICON_DELAY, 175 userlist->buddy_icon_request_timer = purple_timeout_add_seconds(BUDDY_ICON_DELAY,
1404 msn_release_buddy_icon_request_timeout, userlist); 176 msn_release_buddy_icon_request_timeout, userlist);
1405 } 177 }
1406 178
1407 static void 179 static void
1408 request_user_display(MsnUser *user) 180 request_own_user_display(MsnUser *user)
181 {
182 PurpleAccount *account;
183 MsnSession *session;
184 MsnObject *my_obj = NULL;
185 gconstpointer data = NULL;
186 const char *info;
187 size_t len = 0;
188
189 if (purple_debug_is_verbose())
190 purple_debug_info("msn", "Requesting our own user display\n");
191
192 session = user->userlist->session;
193 account = session->account;
194 my_obj = msn_user_get_object(user);
195
196 if (my_obj != NULL) {
197 PurpleStoredImage *img = msn_object_get_image(my_obj);
198 data = purple_imgstore_get_data(img);
199 len = purple_imgstore_get_size(img);
200 info = msn_object_get_sha1(my_obj);
201 }
202
203 purple_buddy_icons_set_for_user(account, user->passport, g_memdup(data, len), len, info);
204
205 /* Free one window slot */
206 session->userlist->buddy_icon_window++;
207
208 if (purple_debug_is_verbose())
209 purple_debug_info("msn", "msn_request_user_display(): buddy_icon_window++ yields =%d\n",
210 session->userlist->buddy_icon_window);
211
212 msn_release_buddy_icon_request(session->userlist);
213 }
214
215 void
216 msn_request_user_display(MsnUser *user)
1409 { 217 {
1410 PurpleAccount *account; 218 PurpleAccount *account;
1411 MsnSession *session; 219 MsnSession *session;
1412 MsnSlpLink *slplink; 220 MsnSlpLink *slplink;
1413 MsnObject *obj; 221 MsnObject *obj;
1421 obj = msn_user_get_object(user); 229 obj = msn_user_get_object(user);
1422 230
1423 info = msn_object_get_sha1(obj); 231 info = msn_object_get_sha1(obj);
1424 232
1425 if (g_ascii_strcasecmp(user->passport, 233 if (g_ascii_strcasecmp(user->passport,
1426 purple_account_get_username(account))) 234 purple_account_get_username(account)))
1427 {
1428 msn_slplink_request_object(slplink, info, got_user_display, 235 msn_slplink_request_object(slplink, info, got_user_display,
1429 end_user_display, obj); 236 end_user_display, obj);
1430 }
1431 else 237 else
1432 { 238 request_own_user_display(user);
1433 MsnObject *my_obj = NULL; 239 }
1434 gconstpointer data = NULL; 240
1435 size_t len = 0; 241 static void
1436 242 send_file_cb(MsnSlpCall *slpcall)
1437 if (purple_debug_is_verbose()) 243 {
1438 purple_debug_info("msn", "Requesting our own user display\n"); 244 MsnSlpMessage *slpmsg;
1439 245 PurpleXfer *xfer;
1440 my_obj = msn_user_get_object(session->user); 246
1441 247 xfer = (PurpleXfer *)slpcall->xfer;
1442 if (my_obj != NULL) 248 if (purple_xfer_get_status(xfer) >= PURPLE_XFER_STATUS_STARTED)
1443 { 249 return;
1444 PurpleStoredImage *img = msn_object_get_image(my_obj); 250
1445 data = purple_imgstore_get_data(img); 251 purple_xfer_ref(xfer);
1446 len = purple_imgstore_get_size(img); 252 purple_xfer_start(xfer, -1, NULL, 0);
1447 } 253 if (purple_xfer_get_status(xfer) != PURPLE_XFER_STATUS_STARTED) {
1448 254 purple_xfer_unref(xfer);
1449 purple_buddy_icons_set_for_user(account, user->passport, g_memdup(data, len), len, info); 255 return;
1450 256 }
1451 /* Free one window slot */ 257 purple_xfer_unref(xfer);
1452 session->userlist->buddy_icon_window++; 258
1453 259 slpmsg = msn_slpmsg_file_new(slpcall, purple_xfer_get_size(xfer));
1454 if (purple_debug_is_verbose()) 260 msn_slpmsg_set_slplink(slpmsg, slpcall->slplink);
1455 purple_debug_info("msn", "request_user_display(): buddy_icon_window++ yields =%d\n", 261
1456 session->userlist->buddy_icon_window); 262 msn_slplink_send_slpmsg(slpcall->slplink, slpmsg);
1457 263 }
1458 msn_release_buddy_icon_request(session->userlist); 264
1459 } 265 static gchar *
1460 } 266 gen_context(PurpleXfer *xfer, const char *file_name, const char *file_path)
267 {
268 gsize size = 0;
269 MsnFileContext *header;
270 gchar *u8 = NULL;
271 gchar *ret;
272 gunichar2 *uni = NULL;
273 glong currentChar = 0;
274 glong len = 0;
275 const char *preview;
276 gsize preview_len;
277
278 size = purple_xfer_get_size(xfer);
279
280 purple_xfer_prepare_thumbnail(xfer, "png");
281
282 if (!file_name) {
283 gchar *basename = g_path_get_basename(file_path);
284 u8 = purple_utf8_try_convert(basename);
285 g_free(basename);
286 file_name = u8;
287 }
288
289 uni = g_utf8_to_utf16(file_name, -1, NULL, &len, NULL);
290
291 if (u8) {
292 g_free(u8);
293 file_name = NULL;
294 u8 = NULL;
295 }
296
297 preview = purple_xfer_get_thumbnail(xfer, &preview_len);
298 header = g_malloc(sizeof(MsnFileContext) + preview_len);
299
300 header->length = GUINT32_TO_LE(sizeof(MsnFileContext) - 1);
301 header->version = GUINT32_TO_LE(2); /* V.3 contains additional unnecessary data */
302 header->file_size = GUINT64_TO_LE(size);
303 if (preview)
304 header->type = GUINT32_TO_LE(0);
305 else
306 header->type = GUINT32_TO_LE(1);
307
308 len = MIN(len, MAX_FILE_NAME_LEN);
309 for (currentChar = 0; currentChar < len; currentChar++) {
310 header->file_name[currentChar] = GUINT16_TO_LE(uni[currentChar]);
311 }
312 memset(&header->file_name[currentChar], 0x00, (MAX_FILE_NAME_LEN - currentChar) * 2);
313
314 memset(&header->unknown1, 0, sizeof(header->unknown1));
315 header->unknown2 = GUINT32_TO_LE(0xffffffff);
316 if (preview) {
317 memcpy(&header->preview, preview, preview_len);
318 }
319 header->preview[preview_len] = '\0';
320
321 g_free(uni);
322 ret = purple_base64_encode((const guchar *)header, sizeof(MsnFileContext) + preview_len);
323 g_free(header);
324 return ret;
325 }
326
327 void
328 msn_request_ft(PurpleXfer *xfer)
329 {
330 MsnSlpCall *slpcall;
331 MsnSlpLink *slplink;
332 char *context;
333 const char *fn;
334 const char *fp;
335
336 fn = purple_xfer_get_filename(xfer);
337 fp = purple_xfer_get_local_filename(xfer);
338
339 slplink = xfer->data;
340
341 g_return_if_fail(slplink != NULL);
342 g_return_if_fail(fp != NULL);
343
344 slpcall = msn_slpcall_new(slplink);
345 msn_slpcall_init(slpcall, MSN_SLPCALL_DC);
346
347 slpcall->session_init_cb = send_file_cb;
348 slpcall->end_cb = msn_xfer_end_cb;
349 slpcall->cb = msn_xfer_completed_cb;
350 slpcall->xfer = xfer;
351 purple_xfer_ref(slpcall->xfer);
352
353 slpcall->pending = TRUE;
354
355 purple_xfer_set_cancel_send_fnc(xfer, msn_xfer_cancel);
356 purple_xfer_set_read_fnc(xfer, msn_xfer_read);
357 purple_xfer_set_write_fnc(xfer, msn_xfer_write);
358
359 xfer->data = slpcall;
360
361 context = gen_context(xfer, fn, fp);
362
363 msn_slpcall_invite(slpcall, MSN_FT_GUID, P2P_APPID_FILE, context);
364 msn_slplink_unref(slplink);
365
366 g_free(context);
367 }
368