--- 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 **************************************************************************/