Mercurial > pidgin
changeset 30833:b0c8b85265b4
Move slp parsing and answering code down the stack to SlpCall. This simplify a lot slp.c.
author | masca@cpw.pidgin.im |
---|---|
date | Tue, 29 Jun 2010 22:24:05 +0000 |
parents | eabeba9c823f |
children | 25a8e3ad7e30 |
files | libpurple/protocols/msn/slp.c libpurple/protocols/msn/slpcall.c |
diffstat | 2 files changed, 857 insertions(+), 853 deletions(-) [+] |
line wrap: on
line diff
--- a/libpurple/protocols/msn/slp.c Tue Jun 29 22:18:22 2010 +0000 +++ b/libpurple/protocols/msn/slp.c Tue Jun 29 22:24:05 2010 +0000 @@ -37,8 +37,6 @@ #include "p2p.h" #include "xfer.h" -#include "smiley.h" - /* seconds to delay between sending buddy icon requests to the server. */ #define BUDDY_ICON_DELAY 20 @@ -46,35 +44,6 @@ /************************************************************************** - * Util - **************************************************************************/ - -static char * -get_token(const char *str, const char *start, const char *end) -{ - const char *c, *c2; - - if ((c = strstr(str, start)) == NULL) - return NULL; - - c += strlen(start); - - if (end != NULL) - { - if ((c2 = strstr(c, end)) == NULL) - return NULL; - - return g_strndup(c, c2 - c); - } - else - { - /* This has to be changed */ - return g_strdup(c); - } - -} - -/************************************************************************** * SLP Control **************************************************************************/ @@ -118,828 +87,6 @@ msn_slplink_queue_slpmsg(slplink, slpmsg); } -/* XXX: this could be improved if we tracked custom smileys - * per-protocol, per-account, per-session or (ideally) per-conversation - */ -static PurpleStoredImage * -find_valid_emoticon(PurpleAccount *account, const char *path) -{ - GList *smileys; - - if (!purple_account_get_bool(account, "custom_smileys", TRUE)) - return NULL; - - smileys = purple_smileys_get_all(); - - for (; smileys; smileys = g_list_delete_link(smileys, smileys)) { - PurpleSmiley *smiley; - PurpleStoredImage *img; - - smiley = smileys->data; - img = purple_smiley_get_stored_image(smiley); - - if (purple_strequal(path, purple_imgstore_get_filename(img))) { - g_list_free(smileys); - return img; - } - - purple_imgstore_unref(img); - } - - purple_debug_error("msn", "Received illegal request for file %s\n", path); - return NULL; -} - -static char * -parse_dc_nonce(const char *content, MsnDirectConnNonceType *ntype) -{ - char *nonce; - - *ntype = DC_NONCE_UNKNOWN; - - nonce = get_token(content, "Hashed-Nonce: {", "}\r\n"); - if (nonce) { - *ntype = DC_NONCE_SHA1; - } else { - guint32 n1, n6; - guint16 n2, n3, n4, n5; - nonce = get_token(content, "Nonce: {", "}\r\n"); - if (nonce - && sscanf(nonce, "%08x-%04hx-%04hx-%04hx-%04hx%08x", - &n1, &n2, &n3, &n4, &n5, &n6) == 6) { - *ntype = DC_NONCE_PLAIN; - g_free(nonce); - nonce = g_malloc(16); - *(guint32 *)(nonce + 0) = GUINT32_TO_LE(n1); - *(guint16 *)(nonce + 4) = GUINT16_TO_LE(n2); - *(guint16 *)(nonce + 6) = GUINT16_TO_LE(n3); - *(guint16 *)(nonce + 8) = GUINT16_TO_BE(n4); - *(guint16 *)(nonce + 10) = GUINT16_TO_BE(n5); - *(guint32 *)(nonce + 12) = GUINT32_TO_BE(n6); - } else { - /* Invalid nonce, so ignore request */ - g_free(nonce); - nonce = NULL; - } - } - - return nonce; -} - -static void -msn_slp_process_transresp(MsnSlpCall *slpcall, const char *content) -{ - /* A direct connection negotiation response */ - char *bridge; - char *nonce; - char *listening; - MsnDirectConn *dc = slpcall->slplink->dc; - MsnDirectConnNonceType ntype; - - purple_debug_info("msn", "process_transresp\n"); - - /* Direct connections are disabled. */ - if (!purple_account_get_bool(slpcall->slplink->session->account, "direct_connect", TRUE)) - return; - - g_return_if_fail(dc != NULL); - g_return_if_fail(dc->state == DC_STATE_CLOSED); - - bridge = get_token(content, "Bridge: ", "\r\n"); - nonce = parse_dc_nonce(content, &ntype); - listening = get_token(content, "Listening: ", "\r\n"); - if (listening && bridge && !strcmp(bridge, "TCPv1")) { - /* Ok, the client supports direct TCP connection */ - - /* We always need this. */ - if (ntype == DC_NONCE_SHA1) { - strncpy(dc->remote_nonce, nonce, 36); - dc->remote_nonce[36] = '\0'; - } - - if (!strcasecmp(listening, "false")) { - if (dc->listen_data != NULL) { - /* - * We'll listen for incoming connections but - * the listening socket isn't ready yet so we cannot - * send the INVITE packet now. Put the slpcall into waiting mode - * and let the callback send the invite. - */ - slpcall->wait_for_socket = TRUE; - - } else if (dc->listenfd != -1) { - /* The listening socket is ready. Send the INVITE here. */ - msn_dc_send_invite(dc); - - } else { - /* We weren't able to create a listener either. Use SB. */ - msn_dc_fallback_to_sb(dc); - } - - } else { - /* - * We should connect to the client so parse - * IP/port from response. - */ - char *ip, *port_str; - int port = 0; - - if (ntype == DC_NONCE_PLAIN) { - /* Only needed for listening side. */ - memcpy(dc->nonce, nonce, 16); - } - - /* Cancel any listen attempts because we don't need them. */ - if (dc->listenfd_handle != 0) { - purple_input_remove(dc->listenfd_handle); - dc->listenfd_handle = 0; - } - if (dc->connect_timeout_handle != 0) { - purple_timeout_remove(dc->connect_timeout_handle); - dc->connect_timeout_handle = 0; - } - if (dc->listenfd != -1) { - purple_network_remove_port_mapping(dc->listenfd); - close(dc->listenfd); - dc->listenfd = -1; - } - if (dc->listen_data != NULL) { - purple_network_listen_cancel(dc->listen_data); - dc->listen_data = NULL; - } - - /* Save external IP/port for later use. We'll try local connection first. */ - dc->ext_ip = get_token(content, "IPv4External-Addrs: ", "\r\n"); - port_str = get_token(content, "IPv4External-Port: ", "\r\n"); - if (port_str) { - dc->ext_port = atoi(port_str); - g_free(port_str); - } - - ip = get_token(content, "IPv4Internal-Addrs: ", "\r\n"); - port_str = get_token(content, "IPv4Internal-Port: ", "\r\n"); - if (port_str) { - port = atoi(port_str); - g_free(port_str); - } - - if (ip && port) { - /* Try internal address first */ - dc->connect_data = purple_proxy_connect( - NULL, - slpcall->slplink->session->account, - ip, - port, - msn_dc_connected_to_peer_cb, - dc - ); - - if (dc->connect_data) { - /* Add connect timeout handle */ - dc->connect_timeout_handle = purple_timeout_add_seconds( - DC_OUTGOING_TIMEOUT, - msn_dc_outgoing_connection_timeout_cb, - dc - ); - } else { - /* - * Connection failed - * Try external IP/port (if specified) - */ - msn_dc_outgoing_connection_timeout_cb(dc); - } - - } else { - /* - * Omitted or invalid internal IP address / port - * Try external IP/port (if specified) - */ - msn_dc_outgoing_connection_timeout_cb(dc); - } - - g_free(ip); - } - - } else { - /* - * Invalid direct connect invitation or - * TCP connection is not supported - */ - } - - g_free(listening); - g_free(nonce); - g_free(bridge); - - return; -} - -static void -got_sessionreq(MsnSlpCall *slpcall, const char *branch, - const char *euf_guid, const char *context) -{ - gboolean accepted = FALSE; - - if (!strcmp(euf_guid, MSN_OBJ_GUID)) - { - /* Emoticon or UserDisplay */ - char *content; - gsize len; - MsnSlpLink *slplink; - MsnSlpMessage *slpmsg; - MsnObject *obj; - char *msnobj_data; - PurpleStoredImage *img = NULL; - int type; - - /* Send Ok */ - content = g_strdup_printf("SessionID: %lu\r\n\r\n", - slpcall->session_id); - - msn_slp_send_ok(slpcall, branch, "application/x-msnmsgr-sessionreqbody", - content); - - g_free(content); - - slplink = slpcall->slplink; - - msnobj_data = (char *)purple_base64_decode(context, &len); - obj = msn_object_new_from_string(msnobj_data); - type = msn_object_get_type(obj); - g_free(msnobj_data); - if (type == MSN_OBJECT_EMOTICON) { - img = find_valid_emoticon(slplink->session->account, obj->location); - } else if (type == MSN_OBJECT_USERTILE) { - img = msn_object_get_image(obj); - if (img) - purple_imgstore_ref(img); - } - msn_object_destroy(obj); - - if (img != NULL) { - /* DATA PREP */ - slpmsg = msn_slpmsg_dataprep_new(slpcall); - msn_slpmsg_set_slplink(slpmsg, slplink); - msn_slplink_queue_slpmsg(slplink, slpmsg); - - /* DATA */ - slpmsg = msn_slpmsg_obj_new(slpcall, img); - msn_slpmsg_set_slplink(slpmsg, slplink); - msn_slplink_queue_slpmsg(slplink, slpmsg); - purple_imgstore_unref(img); - - accepted = TRUE; - - } else { - purple_debug_error("msn", "Wrong object.\n"); - } - } - - else if (!strcmp(euf_guid, MSN_FT_GUID)) - { - /* File Transfer */ - PurpleAccount *account; - PurpleXfer *xfer; - MsnFileContext *header; - gsize bin_len; - guint32 file_size; - char *file_name; - - account = slpcall->slplink->session->account; - - slpcall->end_cb = msn_xfer_end_cb; - slpcall->branch = g_strdup(branch); - - slpcall->pending = TRUE; - - xfer = purple_xfer_new(account, PURPLE_XFER_RECEIVE, - slpcall->slplink->remote_user); - - header = (MsnFileContext *)purple_base64_decode(context, &bin_len); - if (bin_len >= sizeof(MsnFileContext) - 1 && - (header->version == 2 || - (header->version == 3 && header->length == sizeof(MsnFileContext) + 63))) { - file_size = GUINT64_FROM_LE(header->file_size); - - file_name = g_convert((const gchar *)&header->file_name, - MAX_FILE_NAME_LEN * 2, - "UTF-8", "UTF-16LE", - NULL, NULL, NULL); - - purple_xfer_set_filename(xfer, file_name ? file_name : ""); - g_free(file_name); - purple_xfer_set_size(xfer, file_size); - purple_xfer_set_init_fnc(xfer, msn_xfer_init); - purple_xfer_set_request_denied_fnc(xfer, msn_xfer_cancel); - purple_xfer_set_cancel_recv_fnc(xfer, msn_xfer_cancel); - purple_xfer_set_read_fnc(xfer, msn_xfer_read); - purple_xfer_set_write_fnc(xfer, msn_xfer_write); - - slpcall->u.incoming_data = g_byte_array_new(); - - slpcall->xfer = xfer; - purple_xfer_ref(slpcall->xfer); - - xfer->data = slpcall; - - if (header->type == 0 && bin_len >= sizeof(MsnFileContext)) { - purple_xfer_set_thumbnail(xfer, &header->preview, - bin_len - sizeof(MsnFileContext), - "image/png"); - } - - purple_xfer_request(xfer); - } - g_free(header); - - accepted = TRUE; - - } else if (!strcmp(euf_guid, MSN_CAM_REQUEST_GUID)) { - purple_debug_info("msn", "Cam request.\n"); - if (slpcall && slpcall->slplink && - slpcall->slplink->session) { - PurpleConversation *conv; - gchar *from = slpcall->slplink->remote_user; - conv = purple_find_conversation_with_account( - PURPLE_CONV_TYPE_IM, from, - slpcall->slplink->session->account); - if (conv) { - char *buf; - buf = g_strdup_printf( - _("%s requests to view your " - "webcam, but this request is " - "not yet supported."), from); - purple_conversation_write(conv, NULL, buf, - PURPLE_MESSAGE_SYSTEM | - PURPLE_MESSAGE_NOTIFY, - time(NULL)); - g_free(buf); - } - } - - } else if (!strcmp(euf_guid, MSN_CAM_GUID)) { - purple_debug_info("msn", "Cam invite.\n"); - if (slpcall && slpcall->slplink && - slpcall->slplink->session) { - PurpleConversation *conv; - gchar *from = slpcall->slplink->remote_user; - conv = purple_find_conversation_with_account( - PURPLE_CONV_TYPE_IM, from, - slpcall->slplink->session->account); - if (conv) { - char *buf; - buf = g_strdup_printf( - _("%s invited you to view his/her webcam, but " - "this is not yet supported."), from); - purple_conversation_write(conv, NULL, buf, - PURPLE_MESSAGE_SYSTEM | - PURPLE_MESSAGE_NOTIFY, - time(NULL)); - g_free(buf); - } - } - - } else - purple_debug_warning("msn", "SLP SessionReq with unknown EUF-GUID: %s\n", euf_guid); - - if (!accepted) { - char *content = g_strdup_printf("SessionID: %lu\r\n\r\n", - slpcall->session_id); - msn_slp_send_decline(slpcall, branch, "application/x-msnmsgr-sessionreqbody", content); - g_free(content); - } -} - -void -send_bye(MsnSlpCall *slpcall, const char *type) -{ - MsnSlpLink *slplink; - PurpleAccount *account; - MsnSlpMessage *slpmsg; - char *header; - - slplink = slpcall->slplink; - - g_return_if_fail(slplink != NULL); - - account = slplink->session->account; - - header = g_strdup_printf("BYE MSNMSGR:%s MSNSLP/1.0", - purple_account_get_username(account)); - - slpmsg = msn_slpmsg_sip_new(slpcall, 0, header, - "A0D624A6-6C0C-4283-A9E0-BC97B4B46D32", - type, - "\r\n"); - g_free(header); - - slpmsg->info = "SLP BYE"; - slpmsg->text_body = TRUE; - - msn_slplink_queue_slpmsg(slplink, slpmsg); -} - -static void -got_invite(MsnSlpCall *slpcall, - const char *branch, const char *type, const char *content) -{ - MsnSlpLink *slplink; - - slplink = slpcall->slplink; - - if (!strcmp(type, "application/x-msnmsgr-sessionreqbody")) - { - char *euf_guid, *context; - char *temp; - - euf_guid = get_token(content, "EUF-GUID: {", "}\r\n"); - - temp = get_token(content, "SessionID: ", "\r\n"); - if (temp != NULL) - slpcall->session_id = atoi(temp); - g_free(temp); - - temp = get_token(content, "AppID: ", "\r\n"); - if (temp != NULL) - slpcall->app_id = atoi(temp); - g_free(temp); - - context = get_token(content, "Context: ", "\r\n"); - - if (context != NULL) - got_sessionreq(slpcall, branch, euf_guid, context); - - g_free(context); - g_free(euf_guid); - } - else if (!strcmp(type, "application/x-msnmsgr-transreqbody")) - { - /* A direct connection negotiation request */ - char *bridges; - char *nonce; - MsnDirectConnNonceType ntype; - - purple_debug_info("msn", "got_invite: transreqbody received\n"); - - /* Direct connections may be disabled. */ - if (!purple_account_get_bool(slplink->session->account, "direct_connect", TRUE)) { - msn_slp_send_ok(slpcall, branch, - "application/x-msnmsgr-transrespbody", - "Bridge: TCPv1\r\n" - "Listening: false\r\n" - "Nonce: {00000000-0000-0000-0000-000000000000}\r\n" - "\r\n"); - msn_slpcall_session_init(slpcall); - - return; - } - - /* Don't do anything if we already have a direct connection */ - if (slplink->dc != NULL) - return; - - bridges = get_token(content, "Bridges: ", "\r\n"); - nonce = parse_dc_nonce(content, &ntype); - if (bridges && strstr(bridges, "TCPv1") != NULL) { - /* - * Ok, the client supports direct TCP connection - * Try to create a listening port - */ - MsnDirectConn *dc; - - dc = msn_dc_new(slpcall); - if (ntype == DC_NONCE_PLAIN) { - /* There is only one nonce for plain auth. */ - dc->nonce_type = ntype; - memcpy(dc->nonce, nonce, 16); - } else if (ntype == DC_NONCE_SHA1) { - /* Each side has a nonce in SHA1 auth. */ - dc->nonce_type = ntype; - strncpy(dc->remote_nonce, nonce, 36); - dc->remote_nonce[36] = '\0'; - } - - dc->listen_data = purple_network_listen_range( - 0, 0, - SOCK_STREAM, - msn_dc_listen_socket_created_cb, - dc - ); - - if (dc->listen_data == NULL) { - /* Listen socket creation failed */ - - purple_debug_info("msn", "got_invite: listening failed\n"); - - if (dc->nonce_type != DC_NONCE_PLAIN) - msn_slp_send_ok(slpcall, branch, - "application/x-msnmsgr-transrespbody", - "Bridge: TCPv1\r\n" - "Listening: false\r\n" - "Hashed-Nonce: {00000000-0000-0000-0000-000000000000}\r\n" - "\r\n"); - else - msn_slp_send_ok(slpcall, branch, - "application/x-msnmsgr-transrespbody", - "Bridge: TCPv1\r\n" - "Listening: false\r\n" - "Nonce: {00000000-0000-0000-0000-000000000000}\r\n" - "\r\n"); - - } else { - /* - * Listen socket created successfully. - * Don't send anything here because we don't know the parameters - * of the created socket yet. msn_dc_send_ok will be called from - * the callback function: dc_listen_socket_created_cb - */ - purple_debug_info("msn", "got_invite: listening socket created\n"); - - dc->send_connection_info_msg_cb = msn_dc_send_ok; - slpcall->wait_for_socket = TRUE; - } - - } else { - /* - * Invalid direct connect invitation or - * TCP connection is not supported. - */ - } - - g_free(nonce); - g_free(bridges); - } - else if (!strcmp(type, "application/x-msnmsgr-transrespbody")) - { - /* A direct connection negotiation response */ - msn_slp_process_transresp(slpcall, content); - } -} - -static void -got_ok(MsnSlpCall *slpcall, - const char *type, const char *content) -{ - g_return_if_fail(slpcall != NULL); - g_return_if_fail(type != NULL); - - if (!strcmp(type, "application/x-msnmsgr-sessionreqbody")) - { - char *content; - char *header; - char *nonce = NULL; - MsnSession *session = slpcall->slplink->session; - MsnSlpMessage *msg; - MsnDirectConn *dc; - MsnUser *user; - - if (!purple_account_get_bool(session->account, "direct_connect", TRUE)) { - /* Don't attempt a direct connection if disabled. */ - msn_slpcall_session_init(slpcall); - return; - } - - if (slpcall->slplink->dc != NULL) { - /* If we already have an established direct connection - * then just start the transfer. - */ - msn_slpcall_session_init(slpcall); - return; - } - - user = msn_userlist_find_user(session->userlist, - slpcall->slplink->remote_user); - if (!user || !(user->clientid & 0xF0000000)) { - /* Just start a normal SB transfer. */ - msn_slpcall_session_init(slpcall); - return; - } - - /* Try direct file transfer by sending a second INVITE */ - dc = msn_dc_new(slpcall); - slpcall->branch = rand_guid(); - - dc->listen_data = purple_network_listen_range( - 0, 0, - SOCK_STREAM, - msn_dc_listen_socket_created_cb, - dc - ); - - header = g_strdup_printf( - "INVITE MSNMSGR:%s MSNSLP/1.0", - slpcall->slplink->remote_user - ); - - if (dc->nonce_type == DC_NONCE_SHA1) - nonce = g_strdup_printf("Hashed-Nonce: {%s}\r\n", dc->nonce_hash); - - if (dc->listen_data == NULL) { - /* Listen socket creation failed */ - purple_debug_info("msn", "got_ok: listening failed\n"); - - content = g_strdup_printf( - "Bridges: TCPv1\r\n" - "NetID: %u\r\n" - "Conn-Type: IP-Restrict-NAT\r\n" - "UPnPNat: false\r\n" - "ICF: false\r\n" - "%s" - "\r\n", - - rand() % G_MAXUINT32, - nonce ? nonce : "" - ); - - } else { - /* Listen socket created successfully. */ - purple_debug_info("msn", "got_ok: listening socket created\n"); - - content = g_strdup_printf( - "Bridges: TCPv1\r\n" - "NetID: 0\r\n" - "Conn-Type: Direct-Connect\r\n" - "UPnPNat: false\r\n" - "ICF: false\r\n" - "%s" - "\r\n", - - nonce ? nonce : "" - ); - } - - msg = msn_slpmsg_sip_new( - slpcall, - 0, - header, - slpcall->branch, - "application/x-msnmsgr-transreqbody", - content - ); - msg->info = "DC INVITE"; - msg->text_body = TRUE; - g_free(nonce); - g_free(header); - g_free(content); - - msn_slplink_queue_slpmsg(slpcall->slplink, msg); - } - else if (!strcmp(type, "application/x-msnmsgr-transreqbody")) - { - /* Do we get this? */ - purple_debug_info("msn", "OK with transreqbody\n"); - } - else if (!strcmp(type, "application/x-msnmsgr-transrespbody")) - { - msn_slp_process_transresp(slpcall, content); - } -} - -static void -got_error(MsnSlpCall *slpcall, - const char *error, const char *type, const char *content) -{ - /* It's not valid. Kill this off. */ - purple_debug_error("msn", "Received non-OK result: %s\n", - error ? error : "Unknown"); - - if (type && !strcmp(type, "application/x-msnmsgr-transreqbody")) { - MsnDirectConn *dc = slpcall->slplink->dc; - if (dc) { - msn_dc_fallback_to_sb(dc); - return; - } - } - - slpcall->wasted = TRUE; -} - -MsnSlpCall * -msn_slp_sip_recv(MsnSlpLink *slplink, const char *body) -{ - MsnSlpCall *slpcall; - - if (body == NULL) - { - purple_debug_warning("msn", "received bogus message\n"); - return NULL; - } - - if (!strncmp(body, "INVITE", strlen("INVITE"))) - { - /* This is an INVITE request */ - char *branch; - char *call_id; - char *content; - char *content_type; - - /* From: <msnmsgr:buddy@hotmail.com> */ -#if 0 - slpcall->remote_user = get_token(body, "From: <msnmsgr:", ">\r\n"); -#endif - - branch = get_token(body, ";branch={", "}"); - - call_id = get_token(body, "Call-ID: {", "}"); - -#if 0 - long content_len = -1; - - temp = get_token(body, "Content-Length: ", "\r\n"); - if (temp != NULL) - content_len = atoi(temp); - g_free(temp); -#endif - content_type = get_token(body, "Content-Type: ", "\r\n"); - - content = get_token(body, "\r\n\r\n", NULL); - - slpcall = NULL; - if (branch && call_id) - { - slpcall = msn_slplink_find_slp_call(slplink, call_id); - if (slpcall) - { - g_free(slpcall->branch); - slpcall->branch = g_strdup(branch); - got_invite(slpcall, branch, content_type, content); - } - else if (content_type && content) - { - slpcall = msn_slpcall_new(slplink); - slpcall->id = g_strdup(call_id); - got_invite(slpcall, branch, content_type, content); - } - } - - g_free(call_id); - g_free(branch); - g_free(content_type); - g_free(content); - } - else if (!strncmp(body, "MSNSLP/1.0 ", strlen("MSNSLP/1.0 "))) - { - /* This is a response */ - char *content; - char *content_type; - /* Make sure this is "OK" */ - const char *status = body + strlen("MSNSLP/1.0 "); - char *call_id; - - call_id = get_token(body, "Call-ID: {", "}"); - slpcall = msn_slplink_find_slp_call(slplink, call_id); - g_free(call_id); - - g_return_val_if_fail(slpcall != NULL, NULL); - - content_type = get_token(body, "Content-Type: ", "\r\n"); - - content = get_token(body, "\r\n\r\n", NULL); - - if (strncmp(status, "200 OK", 6)) - { - char *error = NULL; - const char *c; - - /* Eww */ - if ((c = strchr(status, '\r')) || (c = strchr(status, '\n')) || - (c = strchr(status, '\0'))) - { - size_t len = c - status; - error = g_strndup(status, len); - } - - got_error(slpcall, error, content_type, content); - g_free(error); - - } else { - /* Everything's just dandy */ - got_ok(slpcall, content_type, content); - } - - g_free(content_type); - g_free(content); - } - else if (!strncmp(body, "BYE", strlen("BYE"))) - { - /* This is a BYE request */ - char *call_id; - - call_id = get_token(body, "Call-ID: {", "}"); - slpcall = msn_slplink_find_slp_call(slplink, call_id); - g_free(call_id); - - if (slpcall != NULL) - slpcall->wasted = TRUE; - - /* msn_slpcall_destroy(slpcall); */ - } - else - slpcall = NULL; - - return slpcall; -} - /************************************************************************** * Msg Callbacks **************************************************************************/
--- a/libpurple/protocols/msn/slpcall.c Tue Jun 29 22:18:22 2010 +0000 +++ b/libpurple/protocols/msn/slpcall.c Tue Jun 29 22:24:05 2010 +0000 @@ -24,12 +24,14 @@ #include "internal.h" #include "debug.h" +#include "smiley.h" #include "msnutils.h" #include "slpcall.h" #include "slp.h" #include "p2p.h" +#include "xfer.h" /************************************************************************** * Main @@ -194,6 +196,861 @@ msn_slpcall_destroy(slpcall); } +/***************************************************************************** + * Parse received SLP messages + ****************************************************************************/ + +/************************************************************************** + *** Util + **************************************************************************/ + +static char * +get_token(const char *str, const char *start, const char *end) +{ + const char *c, *c2; + + if ((c = strstr(str, start)) == NULL) + return NULL; + + c += strlen(start); + + if (end != NULL) + { + if ((c2 = strstr(c, end)) == NULL) + return NULL; + + return g_strndup(c, c2 - c); + } + else + { + /* This has to be changed */ + return g_strdup(c); + } + +} + +/* XXX: this could be improved if we tracked custom smileys + * per-protocol, per-account, per-session or (ideally) per-conversation + */ +static PurpleStoredImage * +find_valid_emoticon(PurpleAccount *account, const char *path) +{ + GList *smileys; + + if (!purple_account_get_bool(account, "custom_smileys", TRUE)) + return NULL; + + smileys = purple_smileys_get_all(); + + for (; smileys; smileys = g_list_delete_link(smileys, smileys)) { + PurpleSmiley *smiley; + PurpleStoredImage *img; + + smiley = smileys->data; + img = purple_smiley_get_stored_image(smiley); + + if (purple_strequal(path, purple_imgstore_get_filename(img))) { + g_list_free(smileys); + return img; + } + + purple_imgstore_unref(img); + } + + purple_debug_error("msn", "Received illegal request for file %s\n", path); + return NULL; +} + +static char * +parse_dc_nonce(const char *content, MsnDirectConnNonceType *ntype) +{ + char *nonce; + + *ntype = DC_NONCE_UNKNOWN; + + nonce = get_token(content, "Hashed-Nonce: {", "}\r\n"); + if (nonce) { + *ntype = DC_NONCE_SHA1; + } else { + guint32 n1, n6; + guint16 n2, n3, n4, n5; + nonce = get_token(content, "Nonce: {", "}\r\n"); + if (nonce + && sscanf(nonce, "%08x-%04hx-%04hx-%04hx-%04hx%08x", + &n1, &n2, &n3, &n4, &n5, &n6) == 6) { + *ntype = DC_NONCE_PLAIN; + g_free(nonce); + nonce = g_malloc(16); + *(guint32 *)(nonce + 0) = GUINT32_TO_LE(n1); + *(guint16 *)(nonce + 4) = GUINT16_TO_LE(n2); + *(guint16 *)(nonce + 6) = GUINT16_TO_LE(n3); + *(guint16 *)(nonce + 8) = GUINT16_TO_BE(n4); + *(guint16 *)(nonce + 10) = GUINT16_TO_BE(n5); + *(guint32 *)(nonce + 12) = GUINT32_TO_BE(n6); + } else { + /* Invalid nonce, so ignore request */ + g_free(nonce); + nonce = NULL; + } + } + + return nonce; +} + +static void +msn_slp_process_transresp(MsnSlpCall *slpcall, const char *content) +{ + /* A direct connection negotiation response */ + char *bridge; + char *nonce; + char *listening; + MsnDirectConn *dc = slpcall->slplink->dc; + MsnDirectConnNonceType ntype; + + purple_debug_info("msn", "process_transresp\n"); + + /* Direct connections are disabled. */ + if (!purple_account_get_bool(slpcall->slplink->session->account, "direct_connect", TRUE)) + return; + + g_return_if_fail(dc != NULL); + g_return_if_fail(dc->state == DC_STATE_CLOSED); + + bridge = get_token(content, "Bridge: ", "\r\n"); + nonce = parse_dc_nonce(content, &ntype); + listening = get_token(content, "Listening: ", "\r\n"); + if (listening && bridge && !strcmp(bridge, "TCPv1")) { + /* Ok, the client supports direct TCP connection */ + + /* We always need this. */ + if (ntype == DC_NONCE_SHA1) { + strncpy(dc->remote_nonce, nonce, 36); + dc->remote_nonce[36] = '\0'; + } + + if (!strcasecmp(listening, "false")) { + if (dc->listen_data != NULL) { + /* + * We'll listen for incoming connections but + * the listening socket isn't ready yet so we cannot + * send the INVITE packet now. Put the slpcall into waiting mode + * and let the callback send the invite. + */ + slpcall->wait_for_socket = TRUE; + + } else if (dc->listenfd != -1) { + /* The listening socket is ready. Send the INVITE here. */ + msn_dc_send_invite(dc); + + } else { + /* We weren't able to create a listener either. Use SB. */ + msn_dc_fallback_to_sb(dc); + } + + } else { + /* + * We should connect to the client so parse + * IP/port from response. + */ + char *ip, *port_str; + int port = 0; + + if (ntype == DC_NONCE_PLAIN) { + /* Only needed for listening side. */ + memcpy(dc->nonce, nonce, 16); + } + + /* Cancel any listen attempts because we don't need them. */ + if (dc->listenfd_handle != 0) { + purple_input_remove(dc->listenfd_handle); + dc->listenfd_handle = 0; + } + if (dc->connect_timeout_handle != 0) { + purple_timeout_remove(dc->connect_timeout_handle); + dc->connect_timeout_handle = 0; + } + if (dc->listenfd != -1) { + purple_network_remove_port_mapping(dc->listenfd); + close(dc->listenfd); + dc->listenfd = -1; + } + if (dc->listen_data != NULL) { + purple_network_listen_cancel(dc->listen_data); + dc->listen_data = NULL; + } + + /* Save external IP/port for later use. We'll try local connection first. */ + dc->ext_ip = get_token(content, "IPv4External-Addrs: ", "\r\n"); + port_str = get_token(content, "IPv4External-Port: ", "\r\n"); + if (port_str) { + dc->ext_port = atoi(port_str); + g_free(port_str); + } + + ip = get_token(content, "IPv4Internal-Addrs: ", "\r\n"); + port_str = get_token(content, "IPv4Internal-Port: ", "\r\n"); + if (port_str) { + port = atoi(port_str); + g_free(port_str); + } + + if (ip && port) { + /* Try internal address first */ + dc->connect_data = purple_proxy_connect( + NULL, + slpcall->slplink->session->account, + ip, + port, + msn_dc_connected_to_peer_cb, + dc + ); + + if (dc->connect_data) { + /* Add connect timeout handle */ + dc->connect_timeout_handle = purple_timeout_add_seconds( + DC_OUTGOING_TIMEOUT, + msn_dc_outgoing_connection_timeout_cb, + dc + ); + } else { + /* + * Connection failed + * Try external IP/port (if specified) + */ + msn_dc_outgoing_connection_timeout_cb(dc); + } + + } else { + /* + * Omitted or invalid internal IP address / port + * Try external IP/port (if specified) + */ + msn_dc_outgoing_connection_timeout_cb(dc); + } + + g_free(ip); + } + + } else { + /* + * Invalid direct connect invitation or + * TCP connection is not supported + */ + } + + g_free(listening); + g_free(nonce); + g_free(bridge); + + return; +} + +static void +got_sessionreq(MsnSlpCall *slpcall, const char *branch, + const char *euf_guid, const char *context) +{ + gboolean accepted = FALSE; + + if (!strcmp(euf_guid, MSN_OBJ_GUID)) + { + /* Emoticon or UserDisplay */ + char *content; + gsize len; + MsnSlpLink *slplink; + MsnSlpMessage *slpmsg; + MsnObject *obj; + char *msnobj_data; + PurpleStoredImage *img = NULL; + int type; + + /* Send Ok */ + content = g_strdup_printf("SessionID: %lu\r\n\r\n", + slpcall->session_id); + + msn_slp_send_ok(slpcall, branch, "application/x-msnmsgr-sessionreqbody", + content); + + g_free(content); + + slplink = slpcall->slplink; + + msnobj_data = (char *)purple_base64_decode(context, &len); + obj = msn_object_new_from_string(msnobj_data); + type = msn_object_get_type(obj); + g_free(msnobj_data); + if (type == MSN_OBJECT_EMOTICON) { + img = find_valid_emoticon(slplink->session->account, obj->location); + } else if (type == MSN_OBJECT_USERTILE) { + img = msn_object_get_image(obj); + if (img) + purple_imgstore_ref(img); + } + msn_object_destroy(obj); + + if (img != NULL) { + /* DATA PREP */ + slpmsg = msn_slpmsg_dataprep_new(slpcall); + msn_slpmsg_set_slplink(slpmsg, slplink); + msn_slplink_queue_slpmsg(slplink, slpmsg); + + /* DATA */ + slpmsg = msn_slpmsg_obj_new(slpcall, img); + msn_slpmsg_set_slplink(slpmsg, slplink); + msn_slplink_queue_slpmsg(slplink, slpmsg); + purple_imgstore_unref(img); + + accepted = TRUE; + + } else { + purple_debug_error("msn", "Wrong object.\n"); + } + } + + else if (!strcmp(euf_guid, MSN_FT_GUID)) + { + /* File Transfer */ + PurpleAccount *account; + PurpleXfer *xfer; + MsnFileContext *header; + gsize bin_len; + guint32 file_size; + char *file_name; + + account = slpcall->slplink->session->account; + + slpcall->end_cb = msn_xfer_end_cb; + slpcall->branch = g_strdup(branch); + + slpcall->pending = TRUE; + + xfer = purple_xfer_new(account, PURPLE_XFER_RECEIVE, + slpcall->slplink->remote_user); + + header = (MsnFileContext *)purple_base64_decode(context, &bin_len); + if (bin_len >= sizeof(MsnFileContext) - 1 && + (header->version == 2 || + (header->version == 3 && header->length == sizeof(MsnFileContext) + 63))) { + file_size = GUINT64_FROM_LE(header->file_size); + + file_name = g_convert((const gchar *)&header->file_name, + MAX_FILE_NAME_LEN * 2, + "UTF-8", "UTF-16LE", + NULL, NULL, NULL); + + purple_xfer_set_filename(xfer, file_name ? file_name : ""); + g_free(file_name); + purple_xfer_set_size(xfer, file_size); + purple_xfer_set_init_fnc(xfer, msn_xfer_init); + purple_xfer_set_request_denied_fnc(xfer, msn_xfer_cancel); + purple_xfer_set_cancel_recv_fnc(xfer, msn_xfer_cancel); + purple_xfer_set_read_fnc(xfer, msn_xfer_read); + purple_xfer_set_write_fnc(xfer, msn_xfer_write); + + slpcall->u.incoming_data = g_byte_array_new(); + + slpcall->xfer = xfer; + purple_xfer_ref(slpcall->xfer); + + xfer->data = slpcall; + + if (header->type == 0 && bin_len >= sizeof(MsnFileContext)) { + purple_xfer_set_thumbnail(xfer, &header->preview, + bin_len - sizeof(MsnFileContext), + "image/png"); + } + + purple_xfer_request(xfer); + } + g_free(header); + + accepted = TRUE; + + } else if (!strcmp(euf_guid, MSN_CAM_REQUEST_GUID)) { + purple_debug_info("msn", "Cam request.\n"); + if (slpcall && slpcall->slplink && + slpcall->slplink->session) { + PurpleConversation *conv; + gchar *from = slpcall->slplink->remote_user; + conv = purple_find_conversation_with_account( + PURPLE_CONV_TYPE_IM, from, + slpcall->slplink->session->account); + if (conv) { + char *buf; + buf = g_strdup_printf( + _("%s requests to view your " + "webcam, but this request is " + "not yet supported."), from); + purple_conversation_write(conv, NULL, buf, + PURPLE_MESSAGE_SYSTEM | + PURPLE_MESSAGE_NOTIFY, + time(NULL)); + g_free(buf); + } + } + + } else if (!strcmp(euf_guid, MSN_CAM_GUID)) { + purple_debug_info("msn", "Cam invite.\n"); + if (slpcall && slpcall->slplink && + slpcall->slplink->session) { + PurpleConversation *conv; + gchar *from = slpcall->slplink->remote_user; + conv = purple_find_conversation_with_account( + PURPLE_CONV_TYPE_IM, from, + slpcall->slplink->session->account); + if (conv) { + char *buf; + buf = g_strdup_printf( + _("%s invited you to view his/her webcam, but " + "this is not yet supported."), from); + purple_conversation_write(conv, NULL, buf, + PURPLE_MESSAGE_SYSTEM | + PURPLE_MESSAGE_NOTIFY, + time(NULL)); + g_free(buf); + } + } + + } else + purple_debug_warning("msn", "SLP SessionReq with unknown EUF-GUID: %s\n", euf_guid); + + if (!accepted) { + char *content = g_strdup_printf("SessionID: %lu\r\n\r\n", + slpcall->session_id); + msn_slp_send_decline(slpcall, branch, "application/x-msnmsgr-sessionreqbody", content); + g_free(content); + } +} + +void +send_bye(MsnSlpCall *slpcall, const char *type) +{ + MsnSlpLink *slplink; + PurpleAccount *account; + MsnSlpMessage *slpmsg; + char *header; + + slplink = slpcall->slplink; + + g_return_if_fail(slplink != NULL); + + account = slplink->session->account; + + header = g_strdup_printf("BYE MSNMSGR:%s MSNSLP/1.0", + purple_account_get_username(account)); + + slpmsg = msn_slpmsg_sip_new(slpcall, 0, header, + "A0D624A6-6C0C-4283-A9E0-BC97B4B46D32", + type, + "\r\n"); + g_free(header); + + slpmsg->info = "SLP BYE"; + slpmsg->text_body = TRUE; + + msn_slplink_queue_slpmsg(slplink, slpmsg); +} + +static void +got_invite(MsnSlpCall *slpcall, + const char *branch, const char *type, const char *content) +{ + MsnSlpLink *slplink; + + slplink = slpcall->slplink; + + if (!strcmp(type, "application/x-msnmsgr-sessionreqbody")) + { + char *euf_guid, *context; + char *temp; + + euf_guid = get_token(content, "EUF-GUID: {", "}\r\n"); + + temp = get_token(content, "SessionID: ", "\r\n"); + if (temp != NULL) + slpcall->session_id = atoi(temp); + g_free(temp); + + temp = get_token(content, "AppID: ", "\r\n"); + if (temp != NULL) + slpcall->app_id = atoi(temp); + g_free(temp); + + context = get_token(content, "Context: ", "\r\n"); + + if (context != NULL) + got_sessionreq(slpcall, branch, euf_guid, context); + + g_free(context); + g_free(euf_guid); + } + else if (!strcmp(type, "application/x-msnmsgr-transreqbody")) + { + /* A direct connection negotiation request */ + char *bridges; + char *nonce; + MsnDirectConnNonceType ntype; + + purple_debug_info("msn", "got_invite: transreqbody received\n"); + + /* Direct connections may be disabled. */ + if (!purple_account_get_bool(slplink->session->account, "direct_connect", TRUE)) { + msn_slp_send_ok(slpcall, branch, + "application/x-msnmsgr-transrespbody", + "Bridge: TCPv1\r\n" + "Listening: false\r\n" + "Nonce: {00000000-0000-0000-0000-000000000000}\r\n" + "\r\n"); + msn_slpcall_session_init(slpcall); + + return; + } + + /* Don't do anything if we already have a direct connection */ + if (slplink->dc != NULL) + return; + + bridges = get_token(content, "Bridges: ", "\r\n"); + nonce = parse_dc_nonce(content, &ntype); + if (bridges && strstr(bridges, "TCPv1") != NULL) { + /* + * Ok, the client supports direct TCP connection + * Try to create a listening port + */ + MsnDirectConn *dc; + + dc = msn_dc_new(slpcall); + if (ntype == DC_NONCE_PLAIN) { + /* There is only one nonce for plain auth. */ + dc->nonce_type = ntype; + memcpy(dc->nonce, nonce, 16); + } else if (ntype == DC_NONCE_SHA1) { + /* Each side has a nonce in SHA1 auth. */ + dc->nonce_type = ntype; + strncpy(dc->remote_nonce, nonce, 36); + dc->remote_nonce[36] = '\0'; + } + + dc->listen_data = purple_network_listen_range( + 0, 0, + SOCK_STREAM, + msn_dc_listen_socket_created_cb, + dc + ); + + if (dc->listen_data == NULL) { + /* Listen socket creation failed */ + + purple_debug_info("msn", "got_invite: listening failed\n"); + + if (dc->nonce_type != DC_NONCE_PLAIN) + msn_slp_send_ok(slpcall, branch, + "application/x-msnmsgr-transrespbody", + "Bridge: TCPv1\r\n" + "Listening: false\r\n" + "Hashed-Nonce: {00000000-0000-0000-0000-000000000000}\r\n" + "\r\n"); + else + msn_slp_send_ok(slpcall, branch, + "application/x-msnmsgr-transrespbody", + "Bridge: TCPv1\r\n" + "Listening: false\r\n" + "Nonce: {00000000-0000-0000-0000-000000000000}\r\n" + "\r\n"); + + } else { + /* + * Listen socket created successfully. + * Don't send anything here because we don't know the parameters + * of the created socket yet. msn_dc_send_ok will be called from + * the callback function: dc_listen_socket_created_cb + */ + purple_debug_info("msn", "got_invite: listening socket created\n"); + + dc->send_connection_info_msg_cb = msn_dc_send_ok; + slpcall->wait_for_socket = TRUE; + } + + } else { + /* + * Invalid direct connect invitation or + * TCP connection is not supported. + */ + } + + g_free(nonce); + g_free(bridges); + } + else if (!strcmp(type, "application/x-msnmsgr-transrespbody")) + { + /* A direct connection negotiation response */ + msn_slp_process_transresp(slpcall, content); + } +} + +static void +got_ok(MsnSlpCall *slpcall, + const char *type, const char *content) +{ + g_return_if_fail(slpcall != NULL); + g_return_if_fail(type != NULL); + + if (!strcmp(type, "application/x-msnmsgr-sessionreqbody")) + { + char *content; + char *header; + char *nonce = NULL; + MsnSession *session = slpcall->slplink->session; + MsnSlpMessage *msg; + MsnDirectConn *dc; + MsnUser *user; + + if (!purple_account_get_bool(session->account, "direct_connect", TRUE)) { + /* Don't attempt a direct connection if disabled. */ + msn_slpcall_session_init(slpcall); + return; + } + + if (slpcall->slplink->dc != NULL) { + /* If we already have an established direct connection + * then just start the transfer. + */ + msn_slpcall_session_init(slpcall); + return; + } + + user = msn_userlist_find_user(session->userlist, + slpcall->slplink->remote_user); + if (!user || !(user->clientid & 0xF0000000)) { + /* Just start a normal SB transfer. */ + msn_slpcall_session_init(slpcall); + return; + } + + /* Try direct file transfer by sending a second INVITE */ + dc = msn_dc_new(slpcall); + slpcall->branch = rand_guid(); + + dc->listen_data = purple_network_listen_range( + 0, 0, + SOCK_STREAM, + msn_dc_listen_socket_created_cb, + dc + ); + + header = g_strdup_printf( + "INVITE MSNMSGR:%s MSNSLP/1.0", + slpcall->slplink->remote_user + ); + + if (dc->nonce_type == DC_NONCE_SHA1) + nonce = g_strdup_printf("Hashed-Nonce: {%s}\r\n", dc->nonce_hash); + + if (dc->listen_data == NULL) { + /* Listen socket creation failed */ + purple_debug_info("msn", "got_ok: listening failed\n"); + + content = g_strdup_printf( + "Bridges: TCPv1\r\n" + "NetID: %u\r\n" + "Conn-Type: IP-Restrict-NAT\r\n" + "UPnPNat: false\r\n" + "ICF: false\r\n" + "%s" + "\r\n", + + rand() % G_MAXUINT32, + nonce ? nonce : "" + ); + + } else { + /* Listen socket created successfully. */ + purple_debug_info("msn", "got_ok: listening socket created\n"); + + content = g_strdup_printf( + "Bridges: TCPv1\r\n" + "NetID: 0\r\n" + "Conn-Type: Direct-Connect\r\n" + "UPnPNat: false\r\n" + "ICF: false\r\n" + "%s" + "\r\n", + + nonce ? nonce : "" + ); + } + + msg = msn_slpmsg_sip_new( + slpcall, + 0, + header, + slpcall->branch, + "application/x-msnmsgr-transreqbody", + content + ); + msg->info = "DC INVITE"; + msg->text_body = TRUE; + g_free(nonce); + g_free(header); + g_free(content); + + msn_slplink_queue_slpmsg(slpcall->slplink, msg); + } + else if (!strcmp(type, "application/x-msnmsgr-transreqbody")) + { + /* Do we get this? */ + purple_debug_info("msn", "OK with transreqbody\n"); + } + else if (!strcmp(type, "application/x-msnmsgr-transrespbody")) + { + msn_slp_process_transresp(slpcall, content); + } +} + +static void +got_error(MsnSlpCall *slpcall, + const char *error, const char *type, const char *content) +{ + /* It's not valid. Kill this off. */ + purple_debug_error("msn", "Received non-OK result: %s\n", + error ? error : "Unknown"); + + if (type && !strcmp(type, "application/x-msnmsgr-transreqbody")) { + MsnDirectConn *dc = slpcall->slplink->dc; + if (dc) { + msn_dc_fallback_to_sb(dc); + return; + } + } + + slpcall->wasted = TRUE; +} + +MsnSlpCall * +msn_slp_sip_recv(MsnSlpLink *slplink, const char *body) +{ + MsnSlpCall *slpcall; + + if (body == NULL) + { + purple_debug_warning("msn", "received bogus message\n"); + return NULL; + } + + if (!strncmp(body, "INVITE", strlen("INVITE"))) + { + /* This is an INVITE request */ + char *branch; + char *call_id; + char *content; + char *content_type; + + /* From: <msnmsgr:buddy@hotmail.com> */ +#if 0 + slpcall->remote_user = get_token(body, "From: <msnmsgr:", ">\r\n"); +#endif + + branch = get_token(body, ";branch={", "}"); + + call_id = get_token(body, "Call-ID: {", "}"); + +#if 0 + long content_len = -1; + + temp = get_token(body, "Content-Length: ", "\r\n"); + if (temp != NULL) + content_len = atoi(temp); + g_free(temp); +#endif + content_type = get_token(body, "Content-Type: ", "\r\n"); + + content = get_token(body, "\r\n\r\n", NULL); + + slpcall = NULL; + if (branch && call_id) + { + slpcall = msn_slplink_find_slp_call(slplink, call_id); + if (slpcall) + { + g_free(slpcall->branch); + slpcall->branch = g_strdup(branch); + got_invite(slpcall, branch, content_type, content); + } + else if (content_type && content) + { + slpcall = msn_slpcall_new(slplink); + slpcall->id = g_strdup(call_id); + got_invite(slpcall, branch, content_type, content); + } + } + + g_free(call_id); + g_free(branch); + g_free(content_type); + g_free(content); + } + else if (!strncmp(body, "MSNSLP/1.0 ", strlen("MSNSLP/1.0 "))) + { + /* This is a response */ + char *content; + char *content_type; + /* Make sure this is "OK" */ + const char *status = body + strlen("MSNSLP/1.0 "); + char *call_id; + + call_id = get_token(body, "Call-ID: {", "}"); + slpcall = msn_slplink_find_slp_call(slplink, call_id); + g_free(call_id); + + g_return_val_if_fail(slpcall != NULL, NULL); + + content_type = get_token(body, "Content-Type: ", "\r\n"); + + content = get_token(body, "\r\n\r\n", NULL); + + if (strncmp(status, "200 OK", 6)) + { + char *error = NULL; + const char *c; + + /* Eww */ + if ((c = strchr(status, '\r')) || (c = strchr(status, '\n')) || + (c = strchr(status, '\0'))) + { + size_t len = c - status; + error = g_strndup(status, len); + } + + got_error(slpcall, error, content_type, content); + g_free(error); + + } else { + /* Everything's just dandy */ + got_ok(slpcall, content_type, content); + } + + g_free(content_type); + g_free(content); + } + else if (!strncmp(body, "BYE", strlen("BYE"))) + { + /* This is a BYE request */ + char *call_id; + + call_id = get_token(body, "Call-ID: {", "}"); + slpcall = msn_slplink_find_slp_call(slplink, call_id); + g_free(call_id); + + if (slpcall != NULL) + slpcall->wasted = TRUE; + + /* msn_slpcall_destroy(slpcall); */ + } + else + slpcall = NULL; + + return slpcall; +} + MsnSlpCall * msn_slp_process_msg(MsnSlpLink *slplink, MsnSlpMessage *slpmsg) {