Mercurial > pidgin.yaz
comparison libpurple/protocols/msn/slpcall.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 | 6814678f3c63 |
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 #include "smiley.h" | |
28 | |
25 #include "msnutils.h" | 29 #include "msnutils.h" |
26 #include "slpcall.h" | 30 #include "slpcall.h" |
27 | 31 |
28 #include "slp.h" | 32 #include "slp.h" |
33 #include "p2p.h" | |
34 #include "xfer.h" | |
29 | 35 |
30 /************************************************************************** | 36 /************************************************************************** |
31 * Main | 37 * Main |
32 **************************************************************************/ | 38 **************************************************************************/ |
33 | 39 |
138 slpcall->started = TRUE; | 144 slpcall->started = TRUE; |
139 } | 145 } |
140 | 146 |
141 void | 147 void |
142 msn_slpcall_invite(MsnSlpCall *slpcall, const char *euf_guid, | 148 msn_slpcall_invite(MsnSlpCall *slpcall, const char *euf_guid, |
143 int app_id, const char *context) | 149 MsnP2PAppId app_id, const char *context) |
144 { | 150 { |
145 MsnSlpLink *slplink; | 151 MsnSlpLink *slplink; |
146 MsnSlpMessage *slpmsg; | 152 MsnSlpMessage *slpmsg; |
147 char *header; | 153 char *header; |
148 char *content; | 154 char *content; |
188 send_bye(slpcall, "application/x-msnmsgr-sessionclosebody"); | 194 send_bye(slpcall, "application/x-msnmsgr-sessionclosebody"); |
189 msn_slplink_send_queued_slpmsgs(slpcall->slplink); | 195 msn_slplink_send_queued_slpmsgs(slpcall->slplink); |
190 msn_slpcall_destroy(slpcall); | 196 msn_slpcall_destroy(slpcall); |
191 } | 197 } |
192 | 198 |
199 /***************************************************************************** | |
200 * Parse received SLP messages | |
201 ****************************************************************************/ | |
202 | |
203 /************************************************************************** | |
204 *** Util | |
205 **************************************************************************/ | |
206 | |
207 static char * | |
208 get_token(const char *str, const char *start, const char *end) | |
209 { | |
210 const char *c, *c2; | |
211 | |
212 if ((c = strstr(str, start)) == NULL) | |
213 return NULL; | |
214 | |
215 c += strlen(start); | |
216 | |
217 if (end != NULL) | |
218 { | |
219 if ((c2 = strstr(c, end)) == NULL) | |
220 return NULL; | |
221 | |
222 return g_strndup(c, c2 - c); | |
223 } | |
224 else | |
225 { | |
226 /* This has to be changed */ | |
227 return g_strdup(c); | |
228 } | |
229 | |
230 } | |
231 | |
232 /* XXX: this could be improved if we tracked custom smileys | |
233 * per-protocol, per-account, per-session or (ideally) per-conversation | |
234 */ | |
235 static PurpleStoredImage * | |
236 find_valid_emoticon(PurpleAccount *account, const char *path) | |
237 { | |
238 GList *smileys; | |
239 | |
240 if (!purple_account_get_bool(account, "custom_smileys", TRUE)) | |
241 return NULL; | |
242 | |
243 smileys = purple_smileys_get_all(); | |
244 | |
245 for (; smileys; smileys = g_list_delete_link(smileys, smileys)) { | |
246 PurpleSmiley *smiley; | |
247 PurpleStoredImage *img; | |
248 | |
249 smiley = smileys->data; | |
250 img = purple_smiley_get_stored_image(smiley); | |
251 | |
252 if (purple_strequal(path, purple_imgstore_get_filename(img))) { | |
253 g_list_free(smileys); | |
254 return img; | |
255 } | |
256 | |
257 purple_imgstore_unref(img); | |
258 } | |
259 | |
260 purple_debug_error("msn", "Received illegal request for file %s\n", path); | |
261 return NULL; | |
262 } | |
263 | |
264 static char * | |
265 parse_dc_nonce(const char *content, MsnDirectConnNonceType *ntype) | |
266 { | |
267 char *nonce; | |
268 | |
269 *ntype = DC_NONCE_UNKNOWN; | |
270 | |
271 nonce = get_token(content, "Hashed-Nonce: {", "}\r\n"); | |
272 if (nonce) { | |
273 *ntype = DC_NONCE_SHA1; | |
274 } else { | |
275 guint32 n1, n6; | |
276 guint16 n2, n3, n4, n5; | |
277 nonce = get_token(content, "Nonce: {", "}\r\n"); | |
278 if (nonce | |
279 && sscanf(nonce, "%08x-%04hx-%04hx-%04hx-%04hx%08x", | |
280 &n1, &n2, &n3, &n4, &n5, &n6) == 6) { | |
281 *ntype = DC_NONCE_PLAIN; | |
282 g_free(nonce); | |
283 nonce = g_malloc(16); | |
284 *(guint32 *)(nonce + 0) = GUINT32_TO_LE(n1); | |
285 *(guint16 *)(nonce + 4) = GUINT16_TO_LE(n2); | |
286 *(guint16 *)(nonce + 6) = GUINT16_TO_LE(n3); | |
287 *(guint16 *)(nonce + 8) = GUINT16_TO_BE(n4); | |
288 *(guint16 *)(nonce + 10) = GUINT16_TO_BE(n5); | |
289 *(guint32 *)(nonce + 12) = GUINT32_TO_BE(n6); | |
290 } else { | |
291 /* Invalid nonce, so ignore request */ | |
292 g_free(nonce); | |
293 nonce = NULL; | |
294 } | |
295 } | |
296 | |
297 return nonce; | |
298 } | |
299 | |
300 static void | |
301 msn_slp_process_transresp(MsnSlpCall *slpcall, const char *content) | |
302 { | |
303 /* A direct connection negotiation response */ | |
304 char *bridge; | |
305 char *nonce; | |
306 char *listening; | |
307 MsnDirectConn *dc = slpcall->slplink->dc; | |
308 MsnDirectConnNonceType ntype; | |
309 | |
310 purple_debug_info("msn", "process_transresp\n"); | |
311 | |
312 /* Direct connections are disabled. */ | |
313 if (!purple_account_get_bool(slpcall->slplink->session->account, "direct_connect", TRUE)) | |
314 return; | |
315 | |
316 g_return_if_fail(dc != NULL); | |
317 g_return_if_fail(dc->state == DC_STATE_CLOSED); | |
318 | |
319 bridge = get_token(content, "Bridge: ", "\r\n"); | |
320 nonce = parse_dc_nonce(content, &ntype); | |
321 listening = get_token(content, "Listening: ", "\r\n"); | |
322 if (listening && bridge && !strcmp(bridge, "TCPv1")) { | |
323 /* Ok, the client supports direct TCP connection */ | |
324 | |
325 /* We always need this. */ | |
326 if (ntype == DC_NONCE_SHA1) { | |
327 strncpy(dc->remote_nonce, nonce, 36); | |
328 dc->remote_nonce[36] = '\0'; | |
329 } | |
330 | |
331 if (!strcasecmp(listening, "false")) { | |
332 if (dc->listen_data != NULL) { | |
333 /* | |
334 * We'll listen for incoming connections but | |
335 * the listening socket isn't ready yet so we cannot | |
336 * send the INVITE packet now. Put the slpcall into waiting mode | |
337 * and let the callback send the invite. | |
338 */ | |
339 slpcall->wait_for_socket = TRUE; | |
340 | |
341 } else if (dc->listenfd != -1) { | |
342 /* The listening socket is ready. Send the INVITE here. */ | |
343 msn_dc_send_invite(dc); | |
344 | |
345 } else { | |
346 /* We weren't able to create a listener either. Use SB. */ | |
347 msn_dc_fallback_to_sb(dc); | |
348 } | |
349 | |
350 } else { | |
351 /* | |
352 * We should connect to the client so parse | |
353 * IP/port from response. | |
354 */ | |
355 char *ip, *port_str; | |
356 int port = 0; | |
357 | |
358 if (ntype == DC_NONCE_PLAIN) { | |
359 /* Only needed for listening side. */ | |
360 memcpy(dc->nonce, nonce, 16); | |
361 } | |
362 | |
363 /* Cancel any listen attempts because we don't need them. */ | |
364 if (dc->listenfd_handle != 0) { | |
365 purple_input_remove(dc->listenfd_handle); | |
366 dc->listenfd_handle = 0; | |
367 } | |
368 if (dc->connect_timeout_handle != 0) { | |
369 purple_timeout_remove(dc->connect_timeout_handle); | |
370 dc->connect_timeout_handle = 0; | |
371 } | |
372 if (dc->listenfd != -1) { | |
373 purple_network_remove_port_mapping(dc->listenfd); | |
374 close(dc->listenfd); | |
375 dc->listenfd = -1; | |
376 } | |
377 if (dc->listen_data != NULL) { | |
378 purple_network_listen_cancel(dc->listen_data); | |
379 dc->listen_data = NULL; | |
380 } | |
381 | |
382 /* Save external IP/port for later use. We'll try local connection first. */ | |
383 dc->ext_ip = get_token(content, "IPv4External-Addrs: ", "\r\n"); | |
384 port_str = get_token(content, "IPv4External-Port: ", "\r\n"); | |
385 if (port_str) { | |
386 dc->ext_port = atoi(port_str); | |
387 g_free(port_str); | |
388 } | |
389 | |
390 ip = get_token(content, "IPv4Internal-Addrs: ", "\r\n"); | |
391 port_str = get_token(content, "IPv4Internal-Port: ", "\r\n"); | |
392 if (port_str) { | |
393 port = atoi(port_str); | |
394 g_free(port_str); | |
395 } | |
396 | |
397 if (ip && port) { | |
398 /* Try internal address first */ | |
399 dc->connect_data = purple_proxy_connect( | |
400 NULL, | |
401 slpcall->slplink->session->account, | |
402 ip, | |
403 port, | |
404 msn_dc_connected_to_peer_cb, | |
405 dc | |
406 ); | |
407 | |
408 if (dc->connect_data) { | |
409 /* Add connect timeout handle */ | |
410 dc->connect_timeout_handle = purple_timeout_add_seconds( | |
411 DC_OUTGOING_TIMEOUT, | |
412 msn_dc_outgoing_connection_timeout_cb, | |
413 dc | |
414 ); | |
415 } else { | |
416 /* | |
417 * Connection failed | |
418 * Try external IP/port (if specified) | |
419 */ | |
420 msn_dc_outgoing_connection_timeout_cb(dc); | |
421 } | |
422 | |
423 } else { | |
424 /* | |
425 * Omitted or invalid internal IP address / port | |
426 * Try external IP/port (if specified) | |
427 */ | |
428 msn_dc_outgoing_connection_timeout_cb(dc); | |
429 } | |
430 | |
431 g_free(ip); | |
432 } | |
433 | |
434 } else { | |
435 /* | |
436 * Invalid direct connect invitation or | |
437 * TCP connection is not supported | |
438 */ | |
439 } | |
440 | |
441 g_free(listening); | |
442 g_free(nonce); | |
443 g_free(bridge); | |
444 | |
445 return; | |
446 } | |
447 | |
448 static void | |
449 got_sessionreq(MsnSlpCall *slpcall, const char *branch, | |
450 const char *euf_guid, const char *context) | |
451 { | |
452 gboolean accepted = FALSE; | |
453 | |
454 if (!strcmp(euf_guid, MSN_OBJ_GUID)) | |
455 { | |
456 /* Emoticon or UserDisplay */ | |
457 char *content; | |
458 gsize len; | |
459 MsnSlpLink *slplink; | |
460 MsnSlpMessage *slpmsg; | |
461 MsnObject *obj; | |
462 char *msnobj_data; | |
463 PurpleStoredImage *img = NULL; | |
464 int type; | |
465 | |
466 /* Send Ok */ | |
467 content = g_strdup_printf("SessionID: %lu\r\n\r\n", | |
468 slpcall->session_id); | |
469 | |
470 msn_slp_send_ok(slpcall, branch, "application/x-msnmsgr-sessionreqbody", | |
471 content); | |
472 | |
473 g_free(content); | |
474 | |
475 slplink = slpcall->slplink; | |
476 | |
477 msnobj_data = (char *)purple_base64_decode(context, &len); | |
478 obj = msn_object_new_from_string(msnobj_data); | |
479 type = msn_object_get_type(obj); | |
480 g_free(msnobj_data); | |
481 if (type == MSN_OBJECT_EMOTICON) { | |
482 img = find_valid_emoticon(slplink->session->account, obj->location); | |
483 } else if (type == MSN_OBJECT_USERTILE) { | |
484 img = msn_object_get_image(obj); | |
485 if (img) | |
486 purple_imgstore_ref(img); | |
487 } | |
488 msn_object_destroy(obj); | |
489 | |
490 if (img != NULL) { | |
491 /* DATA PREP */ | |
492 slpmsg = msn_slpmsg_dataprep_new(slpcall); | |
493 msn_slpmsg_set_slplink(slpmsg, slplink); | |
494 msn_slplink_queue_slpmsg(slplink, slpmsg); | |
495 | |
496 /* DATA */ | |
497 slpmsg = msn_slpmsg_obj_new(slpcall, img); | |
498 msn_slpmsg_set_slplink(slpmsg, slplink); | |
499 msn_slplink_queue_slpmsg(slplink, slpmsg); | |
500 purple_imgstore_unref(img); | |
501 | |
502 accepted = TRUE; | |
503 | |
504 } else { | |
505 purple_debug_error("msn", "Wrong object.\n"); | |
506 } | |
507 } | |
508 | |
509 else if (!strcmp(euf_guid, MSN_FT_GUID)) | |
510 { | |
511 /* File Transfer */ | |
512 PurpleAccount *account; | |
513 PurpleXfer *xfer; | |
514 MsnFileContext *header; | |
515 gsize bin_len; | |
516 guint32 file_size; | |
517 char *file_name; | |
518 | |
519 account = slpcall->slplink->session->account; | |
520 | |
521 slpcall->end_cb = msn_xfer_end_cb; | |
522 slpcall->branch = g_strdup(branch); | |
523 | |
524 slpcall->pending = TRUE; | |
525 | |
526 xfer = purple_xfer_new(account, PURPLE_XFER_RECEIVE, | |
527 slpcall->slplink->remote_user); | |
528 | |
529 header = (MsnFileContext *)purple_base64_decode(context, &bin_len); | |
530 if (bin_len >= sizeof(MsnFileContext) - 1 && | |
531 (header->version == 2 || | |
532 (header->version == 3 && header->length == sizeof(MsnFileContext) + 63))) { | |
533 file_size = GUINT64_FROM_LE(header->file_size); | |
534 | |
535 file_name = g_convert((const gchar *)&header->file_name, | |
536 MAX_FILE_NAME_LEN * 2, | |
537 "UTF-8", "UTF-16LE", | |
538 NULL, NULL, NULL); | |
539 | |
540 purple_xfer_set_filename(xfer, file_name ? file_name : ""); | |
541 g_free(file_name); | |
542 purple_xfer_set_size(xfer, file_size); | |
543 purple_xfer_set_init_fnc(xfer, msn_xfer_init); | |
544 purple_xfer_set_request_denied_fnc(xfer, msn_xfer_cancel); | |
545 purple_xfer_set_cancel_recv_fnc(xfer, msn_xfer_cancel); | |
546 purple_xfer_set_read_fnc(xfer, msn_xfer_read); | |
547 purple_xfer_set_write_fnc(xfer, msn_xfer_write); | |
548 | |
549 slpcall->u.incoming_data = g_byte_array_new(); | |
550 | |
551 slpcall->xfer = xfer; | |
552 purple_xfer_ref(slpcall->xfer); | |
553 | |
554 xfer->data = slpcall; | |
555 | |
556 if (header->type == 0 && bin_len >= sizeof(MsnFileContext)) { | |
557 purple_xfer_set_thumbnail(xfer, &header->preview, | |
558 bin_len - sizeof(MsnFileContext), | |
559 "image/png"); | |
560 } | |
561 | |
562 purple_xfer_request(xfer); | |
563 } | |
564 g_free(header); | |
565 | |
566 accepted = TRUE; | |
567 | |
568 } else if (!strcmp(euf_guid, MSN_CAM_REQUEST_GUID)) { | |
569 purple_debug_info("msn", "Cam request.\n"); | |
570 if (slpcall && slpcall->slplink && | |
571 slpcall->slplink->session) { | |
572 PurpleConversation *conv; | |
573 gchar *from = slpcall->slplink->remote_user; | |
574 conv = purple_find_conversation_with_account( | |
575 PURPLE_CONV_TYPE_IM, from, | |
576 slpcall->slplink->session->account); | |
577 if (conv) { | |
578 char *buf; | |
579 buf = g_strdup_printf( | |
580 _("%s requests to view your " | |
581 "webcam, but this request is " | |
582 "not yet supported."), from); | |
583 purple_conversation_write(conv, NULL, buf, | |
584 PURPLE_MESSAGE_SYSTEM | | |
585 PURPLE_MESSAGE_NOTIFY, | |
586 time(NULL)); | |
587 g_free(buf); | |
588 } | |
589 } | |
590 | |
591 } else if (!strcmp(euf_guid, MSN_CAM_GUID)) { | |
592 purple_debug_info("msn", "Cam invite.\n"); | |
593 if (slpcall && slpcall->slplink && | |
594 slpcall->slplink->session) { | |
595 PurpleConversation *conv; | |
596 gchar *from = slpcall->slplink->remote_user; | |
597 conv = purple_find_conversation_with_account( | |
598 PURPLE_CONV_TYPE_IM, from, | |
599 slpcall->slplink->session->account); | |
600 if (conv) { | |
601 char *buf; | |
602 buf = g_strdup_printf( | |
603 _("%s invited you to view his/her webcam, but " | |
604 "this is 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 | |
614 purple_debug_warning("msn", "SLP SessionReq with unknown EUF-GUID: %s\n", euf_guid); | |
615 | |
616 if (!accepted) { | |
617 char *content = g_strdup_printf("SessionID: %lu\r\n\r\n", | |
618 slpcall->session_id); | |
619 msn_slp_send_decline(slpcall, branch, "application/x-msnmsgr-sessionreqbody", content); | |
620 g_free(content); | |
621 } | |
622 } | |
623 | |
624 void | |
625 send_bye(MsnSlpCall *slpcall, const char *type) | |
626 { | |
627 MsnSlpLink *slplink; | |
628 PurpleAccount *account; | |
629 MsnSlpMessage *slpmsg; | |
630 char *header; | |
631 | |
632 slplink = slpcall->slplink; | |
633 | |
634 g_return_if_fail(slplink != NULL); | |
635 | |
636 account = slplink->session->account; | |
637 | |
638 header = g_strdup_printf("BYE MSNMSGR:%s MSNSLP/1.0", | |
639 purple_account_get_username(account)); | |
640 | |
641 slpmsg = msn_slpmsg_sip_new(slpcall, 0, header, | |
642 "A0D624A6-6C0C-4283-A9E0-BC97B4B46D32", | |
643 type, | |
644 "\r\n"); | |
645 g_free(header); | |
646 | |
647 slpmsg->info = "SLP BYE"; | |
648 slpmsg->text_body = TRUE; | |
649 | |
650 msn_slplink_queue_slpmsg(slplink, slpmsg); | |
651 } | |
652 | |
653 static void | |
654 got_invite(MsnSlpCall *slpcall, | |
655 const char *branch, const char *type, const char *content) | |
656 { | |
657 MsnSlpLink *slplink; | |
658 | |
659 slplink = slpcall->slplink; | |
660 | |
661 if (!strcmp(type, "application/x-msnmsgr-sessionreqbody")) | |
662 { | |
663 char *euf_guid, *context; | |
664 char *temp; | |
665 | |
666 euf_guid = get_token(content, "EUF-GUID: {", "}\r\n"); | |
667 | |
668 temp = get_token(content, "SessionID: ", "\r\n"); | |
669 if (temp != NULL) | |
670 slpcall->session_id = atoi(temp); | |
671 g_free(temp); | |
672 | |
673 temp = get_token(content, "AppID: ", "\r\n"); | |
674 if (temp != NULL) | |
675 slpcall->app_id = atoi(temp); | |
676 g_free(temp); | |
677 | |
678 context = get_token(content, "Context: ", "\r\n"); | |
679 | |
680 if (context != NULL) | |
681 got_sessionreq(slpcall, branch, euf_guid, context); | |
682 | |
683 g_free(context); | |
684 g_free(euf_guid); | |
685 } | |
686 else if (!strcmp(type, "application/x-msnmsgr-transreqbody")) | |
687 { | |
688 /* A direct connection negotiation request */ | |
689 char *bridges; | |
690 char *nonce; | |
691 MsnDirectConnNonceType ntype; | |
692 | |
693 purple_debug_info("msn", "got_invite: transreqbody received\n"); | |
694 | |
695 /* Direct connections may be disabled. */ | |
696 if (!purple_account_get_bool(slplink->session->account, "direct_connect", TRUE)) { | |
697 msn_slp_send_ok(slpcall, branch, | |
698 "application/x-msnmsgr-transrespbody", | |
699 "Bridge: TCPv1\r\n" | |
700 "Listening: false\r\n" | |
701 "Nonce: {00000000-0000-0000-0000-000000000000}\r\n" | |
702 "\r\n"); | |
703 msn_slpcall_session_init(slpcall); | |
704 | |
705 return; | |
706 } | |
707 | |
708 /* Don't do anything if we already have a direct connection */ | |
709 if (slplink->dc != NULL) | |
710 return; | |
711 | |
712 bridges = get_token(content, "Bridges: ", "\r\n"); | |
713 nonce = parse_dc_nonce(content, &ntype); | |
714 if (bridges && strstr(bridges, "TCPv1") != NULL) { | |
715 /* | |
716 * Ok, the client supports direct TCP connection | |
717 * Try to create a listening port | |
718 */ | |
719 MsnDirectConn *dc; | |
720 | |
721 dc = msn_dc_new(slpcall); | |
722 if (ntype == DC_NONCE_PLAIN) { | |
723 /* There is only one nonce for plain auth. */ | |
724 dc->nonce_type = ntype; | |
725 memcpy(dc->nonce, nonce, 16); | |
726 } else if (ntype == DC_NONCE_SHA1) { | |
727 /* Each side has a nonce in SHA1 auth. */ | |
728 dc->nonce_type = ntype; | |
729 strncpy(dc->remote_nonce, nonce, 36); | |
730 dc->remote_nonce[36] = '\0'; | |
731 } | |
732 | |
733 dc->listen_data = purple_network_listen_range( | |
734 0, 0, | |
735 SOCK_STREAM, | |
736 msn_dc_listen_socket_created_cb, | |
737 dc | |
738 ); | |
739 | |
740 if (dc->listen_data == NULL) { | |
741 /* Listen socket creation failed */ | |
742 | |
743 purple_debug_info("msn", "got_invite: listening failed\n"); | |
744 | |
745 if (dc->nonce_type != DC_NONCE_PLAIN) | |
746 msn_slp_send_ok(slpcall, branch, | |
747 "application/x-msnmsgr-transrespbody", | |
748 "Bridge: TCPv1\r\n" | |
749 "Listening: false\r\n" | |
750 "Hashed-Nonce: {00000000-0000-0000-0000-000000000000}\r\n" | |
751 "\r\n"); | |
752 else | |
753 msn_slp_send_ok(slpcall, branch, | |
754 "application/x-msnmsgr-transrespbody", | |
755 "Bridge: TCPv1\r\n" | |
756 "Listening: false\r\n" | |
757 "Nonce: {00000000-0000-0000-0000-000000000000}\r\n" | |
758 "\r\n"); | |
759 | |
760 } else { | |
761 /* | |
762 * Listen socket created successfully. | |
763 * Don't send anything here because we don't know the parameters | |
764 * of the created socket yet. msn_dc_send_ok will be called from | |
765 * the callback function: dc_listen_socket_created_cb | |
766 */ | |
767 purple_debug_info("msn", "got_invite: listening socket created\n"); | |
768 | |
769 dc->send_connection_info_msg_cb = msn_dc_send_ok; | |
770 slpcall->wait_for_socket = TRUE; | |
771 } | |
772 | |
773 } else { | |
774 /* | |
775 * Invalid direct connect invitation or | |
776 * TCP connection is not supported. | |
777 */ | |
778 } | |
779 | |
780 g_free(nonce); | |
781 g_free(bridges); | |
782 } | |
783 else if (!strcmp(type, "application/x-msnmsgr-transrespbody")) | |
784 { | |
785 /* A direct connection negotiation response */ | |
786 msn_slp_process_transresp(slpcall, content); | |
787 } | |
788 } | |
789 | |
790 static void | |
791 got_ok(MsnSlpCall *slpcall, | |
792 const char *type, const char *content) | |
793 { | |
794 g_return_if_fail(slpcall != NULL); | |
795 g_return_if_fail(type != NULL); | |
796 | |
797 if (!strcmp(type, "application/x-msnmsgr-sessionreqbody")) | |
798 { | |
799 char *content; | |
800 char *header; | |
801 char *nonce = NULL; | |
802 MsnSession *session = slpcall->slplink->session; | |
803 MsnSlpMessage *msg; | |
804 MsnDirectConn *dc; | |
805 MsnUser *user; | |
806 | |
807 if (!purple_account_get_bool(session->account, "direct_connect", TRUE)) { | |
808 /* Don't attempt a direct connection if disabled. */ | |
809 msn_slpcall_session_init(slpcall); | |
810 return; | |
811 } | |
812 | |
813 if (slpcall->slplink->dc != NULL) { | |
814 /* If we already have an established direct connection | |
815 * then just start the transfer. | |
816 */ | |
817 msn_slpcall_session_init(slpcall); | |
818 return; | |
819 } | |
820 | |
821 user = msn_userlist_find_user(session->userlist, | |
822 slpcall->slplink->remote_user); | |
823 if (!user || !(user->clientid & 0xF0000000)) { | |
824 /* Just start a normal SB transfer. */ | |
825 msn_slpcall_session_init(slpcall); | |
826 return; | |
827 } | |
828 | |
829 /* Try direct file transfer by sending a second INVITE */ | |
830 dc = msn_dc_new(slpcall); | |
831 slpcall->branch = rand_guid(); | |
832 | |
833 dc->listen_data = purple_network_listen_range( | |
834 0, 0, | |
835 SOCK_STREAM, | |
836 msn_dc_listen_socket_created_cb, | |
837 dc | |
838 ); | |
839 | |
840 header = g_strdup_printf( | |
841 "INVITE MSNMSGR:%s MSNSLP/1.0", | |
842 slpcall->slplink->remote_user | |
843 ); | |
844 | |
845 if (dc->nonce_type == DC_NONCE_SHA1) | |
846 nonce = g_strdup_printf("Hashed-Nonce: {%s}\r\n", dc->nonce_hash); | |
847 | |
848 if (dc->listen_data == NULL) { | |
849 /* Listen socket creation failed */ | |
850 purple_debug_info("msn", "got_ok: listening failed\n"); | |
851 | |
852 content = g_strdup_printf( | |
853 "Bridges: TCPv1\r\n" | |
854 "NetID: %u\r\n" | |
855 "Conn-Type: IP-Restrict-NAT\r\n" | |
856 "UPnPNat: false\r\n" | |
857 "ICF: false\r\n" | |
858 "%s" | |
859 "\r\n", | |
860 | |
861 rand() % G_MAXUINT32, | |
862 nonce ? nonce : "" | |
863 ); | |
864 | |
865 } else { | |
866 /* Listen socket created successfully. */ | |
867 purple_debug_info("msn", "got_ok: listening socket created\n"); | |
868 | |
869 content = g_strdup_printf( | |
870 "Bridges: TCPv1\r\n" | |
871 "NetID: 0\r\n" | |
872 "Conn-Type: Direct-Connect\r\n" | |
873 "UPnPNat: false\r\n" | |
874 "ICF: false\r\n" | |
875 "%s" | |
876 "\r\n", | |
877 | |
878 nonce ? nonce : "" | |
879 ); | |
880 } | |
881 | |
882 msg = msn_slpmsg_sip_new( | |
883 slpcall, | |
884 0, | |
885 header, | |
886 slpcall->branch, | |
887 "application/x-msnmsgr-transreqbody", | |
888 content | |
889 ); | |
890 msg->info = "DC INVITE"; | |
891 msg->text_body = TRUE; | |
892 g_free(nonce); | |
893 g_free(header); | |
894 g_free(content); | |
895 | |
896 msn_slplink_queue_slpmsg(slpcall->slplink, msg); | |
897 } | |
898 else if (!strcmp(type, "application/x-msnmsgr-transreqbody")) | |
899 { | |
900 /* Do we get this? */ | |
901 purple_debug_info("msn", "OK with transreqbody\n"); | |
902 } | |
903 else if (!strcmp(type, "application/x-msnmsgr-transrespbody")) | |
904 { | |
905 msn_slp_process_transresp(slpcall, content); | |
906 } | |
907 } | |
908 | |
909 static void | |
910 got_error(MsnSlpCall *slpcall, | |
911 const char *error, const char *type, const char *content) | |
912 { | |
913 /* It's not valid. Kill this off. */ | |
914 purple_debug_error("msn", "Received non-OK result: %s\n", | |
915 error ? error : "Unknown"); | |
916 | |
917 if (type && !strcmp(type, "application/x-msnmsgr-transreqbody")) { | |
918 MsnDirectConn *dc = slpcall->slplink->dc; | |
919 if (dc) { | |
920 msn_dc_fallback_to_sb(dc); | |
921 return; | |
922 } | |
923 } | |
924 | |
925 slpcall->wasted = TRUE; | |
926 } | |
927 | |
928 static MsnSlpCall * | |
929 msn_slp_sip_recv(MsnSlpLink *slplink, const char *body) | |
930 { | |
931 MsnSlpCall *slpcall; | |
932 | |
933 if (body == NULL) | |
934 { | |
935 purple_debug_warning("msn", "received bogus message\n"); | |
936 return NULL; | |
937 } | |
938 | |
939 if (!strncmp(body, "INVITE", strlen("INVITE"))) | |
940 { | |
941 /* This is an INVITE request */ | |
942 char *branch; | |
943 char *call_id; | |
944 char *content; | |
945 char *content_type; | |
946 | |
947 /* From: <msnmsgr:buddy@hotmail.com> */ | |
948 #if 0 | |
949 slpcall->remote_user = get_token(body, "From: <msnmsgr:", ">\r\n"); | |
950 #endif | |
951 | |
952 branch = get_token(body, ";branch={", "}"); | |
953 | |
954 call_id = get_token(body, "Call-ID: {", "}"); | |
955 | |
956 #if 0 | |
957 long content_len = -1; | |
958 | |
959 temp = get_token(body, "Content-Length: ", "\r\n"); | |
960 if (temp != NULL) | |
961 content_len = atoi(temp); | |
962 g_free(temp); | |
963 #endif | |
964 content_type = get_token(body, "Content-Type: ", "\r\n"); | |
965 | |
966 content = get_token(body, "\r\n\r\n", NULL); | |
967 | |
968 slpcall = NULL; | |
969 if (branch && call_id) | |
970 { | |
971 slpcall = msn_slplink_find_slp_call(slplink, call_id); | |
972 if (slpcall) | |
973 { | |
974 g_free(slpcall->branch); | |
975 slpcall->branch = g_strdup(branch); | |
976 got_invite(slpcall, branch, content_type, content); | |
977 } | |
978 else if (content_type && content) | |
979 { | |
980 slpcall = msn_slpcall_new(slplink); | |
981 slpcall->id = g_strdup(call_id); | |
982 got_invite(slpcall, branch, content_type, content); | |
983 } | |
984 } | |
985 | |
986 g_free(call_id); | |
987 g_free(branch); | |
988 g_free(content_type); | |
989 g_free(content); | |
990 } | |
991 else if (!strncmp(body, "MSNSLP/1.0 ", strlen("MSNSLP/1.0 "))) | |
992 { | |
993 /* This is a response */ | |
994 char *content; | |
995 char *content_type; | |
996 /* Make sure this is "OK" */ | |
997 const char *status = body + strlen("MSNSLP/1.0 "); | |
998 char *call_id; | |
999 | |
1000 call_id = get_token(body, "Call-ID: {", "}"); | |
1001 slpcall = msn_slplink_find_slp_call(slplink, call_id); | |
1002 g_free(call_id); | |
1003 | |
1004 g_return_val_if_fail(slpcall != NULL, NULL); | |
1005 | |
1006 content_type = get_token(body, "Content-Type: ", "\r\n"); | |
1007 | |
1008 content = get_token(body, "\r\n\r\n", NULL); | |
1009 | |
1010 if (strncmp(status, "200 OK", 6)) | |
1011 { | |
1012 char *error = NULL; | |
1013 const char *c; | |
1014 | |
1015 /* Eww */ | |
1016 if ((c = strchr(status, '\r')) || (c = strchr(status, '\n')) || | |
1017 (c = strchr(status, '\0'))) | |
1018 { | |
1019 size_t len = c - status; | |
1020 error = g_strndup(status, len); | |
1021 } | |
1022 | |
1023 got_error(slpcall, error, content_type, content); | |
1024 g_free(error); | |
1025 | |
1026 } else { | |
1027 /* Everything's just dandy */ | |
1028 got_ok(slpcall, content_type, content); | |
1029 } | |
1030 | |
1031 g_free(content_type); | |
1032 g_free(content); | |
1033 } | |
1034 else if (!strncmp(body, "BYE", strlen("BYE"))) | |
1035 { | |
1036 /* This is a BYE request */ | |
1037 char *call_id; | |
1038 | |
1039 call_id = get_token(body, "Call-ID: {", "}"); | |
1040 slpcall = msn_slplink_find_slp_call(slplink, call_id); | |
1041 g_free(call_id); | |
1042 | |
1043 if (slpcall != NULL) | |
1044 slpcall->wasted = TRUE; | |
1045 | |
1046 /* msn_slpcall_destroy(slpcall); */ | |
1047 } | |
1048 else | |
1049 slpcall = NULL; | |
1050 | |
1051 return slpcall; | |
1052 } | |
1053 | |
193 MsnSlpCall * | 1054 MsnSlpCall * |
194 msn_slp_process_msg(MsnSlpLink *slplink, MsnSlpMessage *slpmsg) | 1055 msn_slp_process_msg(MsnSlpLink *slplink, MsnSlpMessage *slpmsg) |
195 { | 1056 { |
196 MsnSlpCall *slpcall; | 1057 MsnSlpCall *slpcall; |
197 const guchar *body; | 1058 const guchar *body; |
199 | 1060 |
200 slpcall = NULL; | 1061 slpcall = NULL; |
201 body = slpmsg->buffer; | 1062 body = slpmsg->buffer; |
202 body_len = slpmsg->offset; | 1063 body_len = slpmsg->offset; |
203 | 1064 |
204 if (slpmsg->flags == 0x0 || slpmsg->flags == 0x1000000) | 1065 if (slpmsg->flags == P2P_NO_FLAG || slpmsg->flags == P2P_WML2009_COMP) |
205 { | 1066 { |
206 char *body_str; | 1067 char *body_str; |
207 | 1068 |
208 if (slpmsg->session_id == 64) | 1069 if (slpmsg->session_id == 64) |
209 { | 1070 { |
260 body_str = g_strndup((const char *)body, body_len); | 1121 body_str = g_strndup((const char *)body, body_len); |
261 slpcall = msn_slp_sip_recv(slplink, body_str); | 1122 slpcall = msn_slp_sip_recv(slplink, body_str); |
262 } | 1123 } |
263 g_free(body_str); | 1124 g_free(body_str); |
264 } | 1125 } |
265 else if (slpmsg->flags == 0x20 || | 1126 else if (slpmsg->flags == P2P_MSN_OBJ_DATA || |
266 slpmsg->flags == 0x1000020 || | 1127 slpmsg->flags == (P2P_WML2009_COMP | P2P_MSN_OBJ_DATA) || |
267 slpmsg->flags == 0x1000030) | 1128 slpmsg->flags == P2P_FILE_DATA) |
268 { | 1129 { |
269 slpcall = msn_slplink_find_slp_call_with_session_id(slplink, slpmsg->session_id); | 1130 slpcall = msn_slplink_find_slp_call_with_session_id(slplink, slpmsg->session_id); |
270 | 1131 |
271 if (slpcall != NULL) | 1132 if (slpcall != NULL) |
272 { | 1133 { |
288 | 1149 |
289 if (slpcall != NULL) | 1150 if (slpcall != NULL) |
290 msn_slpcall_session_init(slpcall); | 1151 msn_slpcall_session_init(slpcall); |
291 } | 1152 } |
292 #endif | 1153 #endif |
293 else if (slpmsg->flags == 0x2) | 1154 else if (slpmsg->flags == P2P_ACK) |
294 { | 1155 { |
295 /* Acknowledgement of previous message. Don't do anything currently. */ | 1156 /* Acknowledgement of previous message. Don't do anything currently. */ |
296 } | 1157 } |
297 else | 1158 else |
298 purple_debug_warning("msn", "Unprocessed SLP message with flags 0x%08lx\n", | 1159 purple_debug_warning("msn", "Unprocessed SLP message with flags 0x%08lx\n", |