Mercurial > pidgin
view libpurple/protocols/msn/msg.c @ 31549:506b77a27a53
This should fix TLV in from and out of the wire.
author | Elliott Sales de Andrade <qulogic@pidgin.im> |
---|---|
date | Thu, 05 May 2011 21:49:21 +0000 |
parents | 0656f6063cf2 |
children | 9a23c70bdb7a |
line wrap: on
line source
/** * @file msg.c Message functions * * purple * * Purple is the legal property of its developers, whose names are too numerous * to list here. Please refer to the COPYRIGHT file distributed with this * source distribution. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */ #include "internal.h" #include "debug.h" #include "msn.h" #include "msg.h" #include "msnutils.h" #include "slpmsg.h" #include "slpmsg_part.h" MsnMessage * msn_message_new(MsnMsgType type) { MsnMessage *msg; msg = g_new0(MsnMessage, 1); msg->type = type; if (purple_debug_is_verbose()) purple_debug_info("msn", "message new (%p)(%d)\n", msg, type); msg->header_table = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); msn_message_ref(msg); return msg; } /** * Destroys a message. * * @param msg The message to destroy. */ static void msn_message_destroy(MsnMessage *msg) { g_return_if_fail(msg != NULL); if (purple_debug_is_verbose()) purple_debug_info("msn", "message destroy (%p)\n", msg); g_free(msg->remote_user); g_free(msg->body); g_free(msg->content_type); g_free(msg->charset); g_hash_table_destroy(msg->header_table); g_list_free(msg->header_list); if (msg->part) msn_slpmsgpart_unref(msg->part); g_free(msg); } MsnMessage * msn_message_ref(MsnMessage *msg) { g_return_val_if_fail(msg != NULL, NULL); msg->ref_count++; if (purple_debug_is_verbose()) purple_debug_info("msn", "message ref (%p)[%u]\n", msg, msg->ref_count); return msg; } void msn_message_unref(MsnMessage *msg) { g_return_if_fail(msg != NULL); g_return_if_fail(msg->ref_count > 0); msg->ref_count--; if (purple_debug_is_verbose()) purple_debug_info("msn", "message unref (%p)[%u]\n", msg, msg->ref_count); if (msg->ref_count == 0) msn_message_destroy(msg); } MsnMessage * msn_message_new_plain(const char *message) { MsnMessage *msg; char *message_cr; msg = msn_message_new(MSN_MSG_TEXT); msg->retries = 1; msn_message_set_header(msg, "User-Agent", PACKAGE_NAME "/" VERSION); msn_message_set_content_type(msg, "text/plain"); msn_message_set_charset(msg, "UTF-8"); msn_message_set_flag(msg, 'A'); msn_message_set_header(msg, "X-MMS-IM-Format", "FN=Segoe%20UI; EF=; CO=0; CS=1;PF=0"); message_cr = purple_str_add_cr(message); msn_message_set_bin_data(msg, message_cr, strlen(message_cr)); g_free(message_cr); return msg; } MsnMessage * msn_message_new_msnslp(void) { MsnMessage *msg; msg = msn_message_new(MSN_MSG_SLP); msn_message_set_header(msg, "User-Agent", NULL); msg->msnslp_message = TRUE; msn_message_set_flag(msg, 'D'); msn_message_set_content_type(msg, "application/x-msnmsgrp2p"); return msg; } MsnMessage * msn_message_new_nudge(void) { MsnMessage *msg; msg = msn_message_new(MSN_MSG_NUDGE); msn_message_set_content_type(msg, "text/x-msnmsgr-datacast"); msn_message_set_flag(msg, 'N'); msn_message_set_bin_data(msg, "ID: 1\r\n", 7); return msg; } void msn_message_parse_payload(MsnMessage *msg, const char *payload, size_t payload_len, const char *line_dem,const char *body_dem) { char *tmp_base, *tmp; const char *content_type; char *end; char **elems, **cur, **tokens; g_return_if_fail(payload != NULL); tmp_base = tmp = g_malloc(payload_len + 1); memcpy(tmp_base, payload, payload_len); tmp_base[payload_len] = '\0'; /* Find the end of the headers */ end = strstr(tmp, body_dem); /* TODO? some clients use \r delimiters instead of \r\n, the official client * doesn't send such messages, but does handle receiving them. We'll just * avoid crashing for now */ if (end == NULL) { g_free(tmp_base); g_return_if_reached(); } *end = '\0'; /* Split the headers and parse each one */ elems = g_strsplit(tmp, line_dem, 0); for (cur = elems; *cur != NULL; cur++) { const char *key, *value; /* If this line starts with whitespace, it's been folded from the previous line and won't have ':'. */ if ((**cur == ' ') || (**cur == '\t')) { tokens = g_strsplit(g_strchug(*cur), "=\"", 2); key = tokens[0]; value = tokens[1]; /* The only one I care about is 'boundary' (which is folded from the key 'Content-Type'), so only process that. */ if (!strcmp(key, "boundary")) { char *end = strchr(value, '\"'); *end = '\0'; msn_message_set_header(msg, key, value); } g_strfreev(tokens); continue; } tokens = g_strsplit(*cur, ": ", 2); key = tokens[0]; value = tokens[1]; /*if not MIME content ,then return*/ if (!strcmp(key, "MIME-Version")) { g_strfreev(tokens); continue; } if (!strcmp(key, "Content-Type")) { char *charset, *c; if ((c = strchr(value, ';')) != NULL) { if ((charset = strchr(c, '=')) != NULL) { charset++; msn_message_set_charset(msg, charset); } *c = '\0'; } msn_message_set_content_type(msg, value); } else { msn_message_set_header(msg, key, value); } g_strfreev(tokens); } g_strfreev(elems); /* Proceed to the end of the "\r\n\r\n" */ tmp = end + strlen(body_dem); /* Now we *should* be at the body. */ content_type = msn_message_get_content_type(msg); if (content_type != NULL && !strcmp(content_type, "application/x-msnmsgrp2p")) { msg->msnslp_message = TRUE; msg->part = msn_slpmsgpart_new_from_data(tmp, payload_len - (tmp - tmp_base)); } if (payload_len - (tmp - tmp_base) > 0) { msg->body_len = payload_len - (tmp - tmp_base); g_free(msg->body); msg->body = g_malloc(msg->body_len + 1); memcpy(msg->body, tmp, msg->body_len); msg->body[msg->body_len] = '\0'; } if ((!content_type || !strcmp(content_type, "text/plain")) && msg->charset == NULL) { char *body = g_convert(msg->body, msg->body_len, "UTF-8", "ISO-8859-1", NULL, &msg->body_len, NULL); g_free(msg->body); msg->body = body; msg->charset = g_strdup("UTF-8"); } g_free(tmp_base); } MsnMessage * msn_message_new_from_cmd(MsnSession *session, MsnCommand *cmd) { MsnMessage *msg; g_return_val_if_fail(cmd != NULL, NULL); msg = msn_message_new(MSN_MSG_UNKNOWN); msg->remote_user = g_strdup(cmd->params[0]); /* msg->size = atoi(cmd->params[2]); */ msg->cmd = cmd; return msg; } char * msn_message_gen_payload(MsnMessage *msg, size_t *ret_size) { GList *l; char *n, *base, *end; int len; size_t body_len = 0; const void *body; g_return_val_if_fail(msg != NULL, NULL); len = MSN_BUF_LEN; base = n = end = g_malloc(len + 1); end += len; /* Standard header. */ if (msg->charset == NULL) { g_snprintf(n, len, "MIME-Version: 1.0\r\n" "Content-Type: %s\r\n", msg->content_type); } else { g_snprintf(n, len, "MIME-Version: 1.0\r\n" "Content-Type: %s; charset=%s\r\n", msg->content_type, msg->charset); } n += strlen(n); for (l = msg->header_list; l != NULL; l = l->next) { const char *key; const char *value; key = l->data; value = msn_message_get_header_value(msg, key); g_snprintf(n, end - n, "%s: %s\r\n", key, value); n += strlen(n); } n += g_strlcpy(n, "\r\n", end - n); body = msn_message_get_bin_data(msg, &body_len); if (msg->msnslp_message) { size_t siz; char *body; body = msn_slpmsgpart_serialize(msg->part, &siz); memcpy(n, body, siz); n += siz; g_free(body); } else { if (body != NULL) { memcpy(n, body, body_len); n += body_len; *n = '\0'; } } if (ret_size != NULL) { *ret_size = n - base; if (*ret_size > 1664) *ret_size = 1664; } return base; } void msn_message_set_flag(MsnMessage *msg, char flag) { g_return_if_fail(msg != NULL); g_return_if_fail(flag != 0); msg->flag = flag; } char msn_message_get_flag(const MsnMessage *msg) { g_return_val_if_fail(msg != NULL, 0); return msg->flag; } void msn_message_set_bin_data(MsnMessage *msg, const void *data, size_t len) { g_return_if_fail(msg != NULL); /* There is no need to waste memory on data we cannot send anyway */ if (len > 1664) len = 1664; if (msg->body != NULL) g_free(msg->body); if (data != NULL && len > 0) { msg->body = g_malloc(len + 1); memcpy(msg->body, data, len); msg->body[len] = '\0'; msg->body_len = len; } else { msg->body = NULL; msg->body_len = 0; } } const void * msn_message_get_bin_data(const MsnMessage *msg, size_t *len) { g_return_val_if_fail(msg != NULL, NULL); if (len) *len = msg->body_len; return msg->body; } void msn_message_set_content_type(MsnMessage *msg, const char *type) { g_return_if_fail(msg != NULL); g_free(msg->content_type); msg->content_type = g_strdup(type); } const char * msn_message_get_content_type(const MsnMessage *msg) { g_return_val_if_fail(msg != NULL, NULL); return msg->content_type; } void msn_message_set_charset(MsnMessage *msg, const char *charset) { g_return_if_fail(msg != NULL); g_free(msg->charset); msg->charset = g_strdup(charset); } const char * msn_message_get_charset(const MsnMessage *msg) { g_return_val_if_fail(msg != NULL, NULL); return msg->charset; } void msn_message_set_header(MsnMessage *msg, const char *name, const char *value) { const char *temp; char *new_name; g_return_if_fail(msg != NULL); g_return_if_fail(name != NULL); temp = msn_message_get_header_value(msg, name); if (value == NULL) { if (temp != NULL) { GList *l; for (l = msg->header_list; l != NULL; l = l->next) { if (!g_ascii_strcasecmp(l->data, name)) { msg->header_list = g_list_remove(msg->header_list, l->data); break; } } g_hash_table_remove(msg->header_table, name); } return; } new_name = g_strdup(name); g_hash_table_insert(msg->header_table, new_name, g_strdup(value)); if (temp == NULL) msg->header_list = g_list_append(msg->header_list, new_name); } const char * msn_message_get_header_value(const MsnMessage *msg, const char *name) { g_return_val_if_fail(msg != NULL, NULL); g_return_val_if_fail(name != NULL, NULL); return g_hash_table_lookup(msg->header_table, name); } GHashTable * msn_message_get_hashtable_from_body(const MsnMessage *msg) { GHashTable *table; size_t body_len; const char *body; char **elems, **cur, **tokens, *body_str; g_return_val_if_fail(msg != NULL, NULL); table = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); body = msn_message_get_bin_data(msg, &body_len); g_return_val_if_fail(body != NULL, NULL); body_str = g_strndup(body, body_len); elems = g_strsplit(body_str, "\r\n", 0); g_free(body_str); for (cur = elems; *cur != NULL; cur++) { if (**cur == '\0') break; tokens = g_strsplit(*cur, ": ", 2); if (tokens[0] != NULL && tokens[1] != NULL) { g_hash_table_insert(table, tokens[0], tokens[1]); g_free(tokens); } else g_strfreev(tokens); } g_strfreev(elems); return table; } char * msn_message_to_string(MsnMessage *msg) { size_t body_len; const char *body; g_return_val_if_fail(msg != NULL, NULL); g_return_val_if_fail(msg->type == MSN_MSG_TEXT, NULL); body = msn_message_get_bin_data(msg, &body_len); return g_strndup(body, body_len); } void msn_message_show_readable(MsnMessage *msg, const char *info, gboolean text_body) { GString *str; size_t body_len; const char *body; GList *l; g_return_if_fail(msg != NULL); str = g_string_new(NULL); /* Standard header. */ if (msg->charset == NULL) { g_string_append_printf(str, "MIME-Version: 1.0\r\n" "Content-Type: %s\r\n", msg->content_type); } else { g_string_append_printf(str, "MIME-Version: 1.0\r\n" "Content-Type: %s; charset=%s\r\n", msg->content_type, msg->charset); } for (l = msg->header_list; l; l = l->next) { char *key; const char *value; key = l->data; value = msn_message_get_header_value(msg, key); g_string_append_printf(str, "%s: %s\r\n", key, value); } g_string_append(str, "\r\n"); body = msn_message_get_bin_data(msg, &body_len); if (msg->msnslp_message) { if (msg->part) msn_slpmsgpart_to_string(msg->part, str); if (purple_debug_is_verbose() && body != NULL) { if (text_body) { g_string_append_len(str, body, body_len); if (body[body_len - 1] == '\0') { str->len--; g_string_append(str, " 0x00"); } g_string_append(str, "\r\n"); } else { int i; for (i = 0; i < body_len; i++) { g_string_append_printf(str, "%.2hhX ", body[i]); if ((i % 16) == 15) g_string_append(str, "\r\n"); } g_string_append(str, "\r\n"); } } } else { if (body != NULL) { g_string_append_len(str, body, body_len); g_string_append(str, "\r\n"); } } purple_debug_info("msn", "Message %s:\n{%s}\n", info, str->str); g_string_free(str, TRUE); } /************************************************************************** * Message Handlers **************************************************************************/ void msn_plain_msg(MsnCmdProc *cmdproc, MsnMessage *msg) { PurpleConnection *gc; const char *body; char *body_enc; char *body_final; size_t body_len; const char *passport; const char *value; gc = cmdproc->session->account->gc; body = msn_message_get_bin_data(msg, &body_len); body_enc = g_markup_escape_text(body, body_len); passport = msg->remote_user; if (!strcmp(passport, "messenger@microsoft.com") && strstr(body, "immediate security update")) { return; } #if 0 if ((value = msn_message_get_header_value(msg, "User-Agent")) != NULL) { purple_debug_misc("msn", "User-Agent = '%s'\n", value); } #endif if ((value = msn_message_get_header_value(msg, "X-MMS-IM-Format")) != NULL) { char *pre, *post; msn_parse_format(value, &pre, &post); body_final = g_strdup_printf("%s%s%s", pre ? pre : "", body_enc ? body_enc : "", post ? post : ""); g_free(pre); g_free(post); g_free(body_enc); } else { body_final = body_enc; } if (cmdproc->servconn->type == MSN_SERVCONN_SB) { MsnSwitchBoard *swboard = cmdproc->data; swboard->flag |= MSN_SB_FLAG_IM; if (swboard->current_users > 1 || ((swboard->conv != NULL) && purple_conversation_get_type(swboard->conv) == PURPLE_CONV_TYPE_CHAT)) { /* If current_users is always ok as it should then there is no need to * check if this is a chat. */ if (swboard->current_users <= 1) purple_debug_misc("msn", "plain_msg: current_users(%d)\n", swboard->current_users); serv_got_chat_in(gc, swboard->chat_id, passport, 0, body_final, time(NULL)); if (swboard->conv == NULL) { swboard->conv = purple_find_chat(gc, swboard->chat_id); swboard->flag |= MSN_SB_FLAG_IM; } } else if (!g_str_equal(passport, purple_account_get_username(gc->account))) { /* Don't im ourselves ... */ serv_got_im(gc, passport, body_final, 0, time(NULL)); if (swboard->conv == NULL) { swboard->conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, passport, purple_connection_get_account(gc)); swboard->flag |= MSN_SB_FLAG_IM; } } } else { serv_got_im(gc, passport, body_final, 0, time(NULL)); } g_free(body_final); } void msn_control_msg(MsnCmdProc *cmdproc, MsnMessage *msg) { PurpleConnection *gc; char *passport; gc = cmdproc->session->account->gc; passport = msg->remote_user; if (msn_message_get_header_value(msg, "TypingUser") == NULL) return; if (cmdproc->servconn->type == MSN_SERVCONN_SB) { MsnSwitchBoard *swboard = cmdproc->data; if (swboard->current_users == 1) { serv_got_typing(gc, passport, MSN_TYPING_RECV_TIMEOUT, PURPLE_TYPING); } } else { serv_got_typing(gc, passport, MSN_TYPING_RECV_TIMEOUT, PURPLE_TYPING); } } static void datacast_inform_user(MsnSwitchBoard *swboard, const char *who, const char *msg, const char *filename) { char *username, *str; PurpleAccount *account; PurpleBuddy *b; PurpleConnection *pc; gboolean chat; account = swboard->session->account; pc = purple_account_get_connection(account); if ((b = purple_find_buddy(account, who)) != NULL) username = g_markup_escape_text(purple_buddy_get_alias(b), -1); else username = g_markup_escape_text(who, -1); str = g_strdup_printf(msg, username, filename); g_free(username); swboard->flag |= MSN_SB_FLAG_IM; if (swboard->current_users > 1) chat = TRUE; else chat = FALSE; if (swboard->conv == NULL) { if (chat) swboard->conv = purple_find_chat(account->gc, swboard->chat_id); else { swboard->conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, who, account); if (swboard->conv == NULL) swboard->conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, who); } } if (chat) serv_got_chat_in(pc, purple_conv_chat_get_id(PURPLE_CONV_CHAT(swboard->conv)), who, PURPLE_MESSAGE_RECV|PURPLE_MESSAGE_SYSTEM, str, time(NULL)); else serv_got_im(pc, who, str, PURPLE_MESSAGE_RECV|PURPLE_MESSAGE_SYSTEM, time(NULL)); g_free(str); } /* TODO: Make these not be such duplicates of each other */ static void got_wink_cb(MsnSlpCall *slpcall, const guchar *data, gsize size) { FILE *f = NULL; char *path = NULL; const char *who = slpcall->slplink->remote_user; purple_debug_info("msn", "Received wink from %s\n", who); if ((f = purple_mkstemp(&path, TRUE)) && (fwrite(data, 1, size, f) == size)) { datacast_inform_user(slpcall->slplink->swboard, who, _("%s sent a wink. <a href='msn-wink://%s'>Click here to play it</a>"), path); } else { purple_debug_error("msn", "Couldn\'t create temp file to store wink\n"); datacast_inform_user(slpcall->slplink->swboard, who, _("%s sent a wink, but it could not be saved"), NULL); } if (f) fclose(f); g_free(path); } static void got_voiceclip_cb(MsnSlpCall *slpcall, const guchar *data, gsize size) { FILE *f = NULL; char *path = NULL; const char *who = slpcall->slplink->remote_user; purple_debug_info("msn", "Received voice clip from %s\n", who); if ((f = purple_mkstemp(&path, TRUE)) && (fwrite(data, 1, size, f) == size)) { datacast_inform_user(slpcall->slplink->swboard, who, _("%s sent a voice clip. <a href='audio://%s'>Click here to play it</a>"), path); } else { purple_debug_error("msn", "Couldn\'t create temp file to store sound\n"); datacast_inform_user(slpcall->slplink->swboard, who, _("%s sent a voice clip, but it could not be saved"), NULL); } if (f) fclose(f); g_free(path); } void msn_p2p_msg(MsnCmdProc *cmdproc, MsnMessage *msg) { MsnSession *session; MsnSlpLink *slplink; session = cmdproc->servconn->session; slplink = msn_session_get_slplink(session, msg->remote_user); if (slplink->swboard == NULL) { /* * We will need swboard in order to change its flags. If its * NULL, something has probably gone wrong earlier on. I * didn't want to do this, but MSN 7 is somehow causing us * to crash here, I couldn't reproduce it to debug more, * and people are reporting bugs. Hopefully this doesn't * cause more crashes. Stu. */ if (cmdproc->data == NULL) g_warning("msn_p2p_msg cmdproc->data was NULL\n"); else { slplink->swboard = (MsnSwitchBoard *)cmdproc->data; slplink->swboard->slplinks = g_list_prepend(slplink->swboard->slplinks, slplink); } } if (msg->part) { msn_slplink_process_msg(slplink, msg->part); } else /* This should never happen. */ purple_debug_fatal("msn", "P2P message without a Part.\n"); } static void got_emoticon(MsnSlpCall *slpcall, const guchar *data, gsize size) { PurpleConversation *conv; MsnSwitchBoard *swboard; swboard = slpcall->slplink->swboard; conv = swboard->conv; if (conv) { /* FIXME: it would be better if we wrote the data as we received it instead of all at once, calling write multiple times and close once at the very end */ purple_conv_custom_smiley_write(conv, slpcall->data_info, data, size); purple_conv_custom_smiley_close(conv, slpcall->data_info ); } if (purple_debug_is_verbose()) purple_debug_info("msn", "Got smiley: %s\n", slpcall->data_info); } void msn_emoticon_msg(MsnCmdProc *cmdproc, MsnMessage *msg) { MsnSession *session; MsnSlpLink *slplink; MsnSwitchBoard *swboard; MsnObject *obj; char **tokens; char *smile, *body_str; const char *body, *who, *sha1; guint tok; size_t body_len; PurpleConversation *conv; session = cmdproc->servconn->session; if (!purple_account_get_bool(session->account, "custom_smileys", TRUE)) return; swboard = cmdproc->data; conv = swboard->conv; body = msn_message_get_bin_data(msg, &body_len); if (!body || !body_len) return; body_str = g_strndup(body, body_len); /* MSN Messenger 7 may send more than one MSNObject in a single message... * Maybe 10 tokens is a reasonable max value. */ tokens = g_strsplit(body_str, "\t", 10); g_free(body_str); for (tok = 0; tok < 9; tok += 2) { if (tokens[tok] == NULL || tokens[tok + 1] == NULL) { break; } smile = tokens[tok]; obj = msn_object_new_from_string(purple_url_decode(tokens[tok + 1])); if (obj == NULL) break; who = msn_object_get_creator(obj); sha1 = msn_object_get_sha1(obj); slplink = msn_session_get_slplink(session, who); if (slplink->swboard != swboard) { if (slplink->swboard != NULL) /* * Apparently we're using a different switchboard now or * something? I don't know if this is normal, but it * definitely happens. So make sure the old switchboard * doesn't still have a reference to us. */ slplink->swboard->slplinks = g_list_remove(slplink->swboard->slplinks, slplink); slplink->swboard = swboard; slplink->swboard->slplinks = g_list_prepend(slplink->swboard->slplinks, slplink); } /* If the conversation doesn't exist then this is a custom smiley * used in the first message in a MSN conversation: we need to create * the conversation now, otherwise the custom smiley won't be shown. * This happens because every GtkIMHtml has its own smiley tree: if * the conversation doesn't exist then we cannot associate the new * smiley with its GtkIMHtml widget. */ if (!conv) { conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, session->account, who); } if (purple_conv_custom_smiley_add(conv, smile, "sha1", sha1, TRUE)) { msn_slplink_request_object(slplink, smile, got_emoticon, NULL, obj); } msn_object_destroy(obj); obj = NULL; who = NULL; sha1 = NULL; } g_strfreev(tokens); } void msn_datacast_msg(MsnCmdProc *cmdproc, MsnMessage *msg) { GHashTable *body; const char *id; body = msn_message_get_hashtable_from_body(msg); id = g_hash_table_lookup(body, "ID"); if (!strcmp(id, "1")) { /* Nudge */ PurpleAccount *account; const char *user; account = cmdproc->session->account; user = msg->remote_user; if (cmdproc->servconn->type == MSN_SERVCONN_SB) { MsnSwitchBoard *swboard = cmdproc->data; if (swboard->current_users > 1 || ((swboard->conv != NULL) && purple_conversation_get_type(swboard->conv) == PURPLE_CONV_TYPE_CHAT)) purple_prpl_got_attention_in_chat(account->gc, swboard->chat_id, user, MSN_NUDGE); else purple_prpl_got_attention(account->gc, user, MSN_NUDGE); } else { purple_prpl_got_attention(account->gc, user, MSN_NUDGE); } } else if (!strcmp(id, "2")) { /* Wink */ MsnSession *session; MsnSlpLink *slplink; MsnObject *obj; const char *who; const char *data; session = cmdproc->session; data = g_hash_table_lookup(body, "Data"); obj = msn_object_new_from_string(data); who = msn_object_get_creator(obj); slplink = msn_session_get_slplink(session, who); msn_slplink_request_object(slplink, data, got_wink_cb, NULL, obj); msn_object_destroy(obj); } else if (!strcmp(id, "3")) { /* Voiceclip */ MsnSession *session; MsnSlpLink *slplink; MsnObject *obj; const char *who; const char *data; session = cmdproc->session; data = g_hash_table_lookup(body, "Data"); obj = msn_object_new_from_string(data); who = msn_object_get_creator(obj); slplink = msn_session_get_slplink(session, who); msn_slplink_request_object(slplink, data, got_voiceclip_cb, NULL, obj); msn_object_destroy(obj); } else if (!strcmp(id, "4")) { /* Action */ } else { purple_debug_warning("msn", "Got unknown datacast with ID %s.\n", id); } g_hash_table_destroy(body); } void msn_invite_msg(MsnCmdProc *cmdproc, MsnMessage *msg) { GHashTable *body; const gchar *command; const gchar *cookie; gboolean accepted = FALSE; g_return_if_fail(cmdproc != NULL); g_return_if_fail(msg != NULL); body = msn_message_get_hashtable_from_body(msg); if (body == NULL) { purple_debug_warning("msn", "Unable to parse invite msg body.\n"); return; } /* * GUID is NOT always present but Invitation-Command and Invitation-Cookie * are mandatory. */ command = g_hash_table_lookup(body, "Invitation-Command"); cookie = g_hash_table_lookup(body, "Invitation-Cookie"); if (command == NULL || cookie == NULL) { purple_debug_warning("msn", "Invalid invitation message: either Invitation-Command " "or Invitation-Cookie is missing or invalid.\n" ); return; } else if (!strcmp(command, "INVITE")) { const gchar *guid = g_hash_table_lookup(body, "Application-GUID"); if (guid == NULL) { purple_debug_warning("msn", "Invite msg missing Application-GUID.\n"); accepted = TRUE; } else if (!strcmp(guid, MSN_FT_GUID)) { } else if (!strcmp(guid, "{02D3C01F-BF30-4825-A83A-DE7AF41648AA}")) { purple_debug_info("msn", "Computer call\n"); if (cmdproc->session) { PurpleConversation *conv = NULL; gchar *from = msg->remote_user; gchar *buf = NULL; if (from) conv = purple_find_conversation_with_account( PURPLE_CONV_TYPE_IM, from, cmdproc->session->account); if (conv) buf = g_strdup_printf( _("%s sent you a voice chat " "invite, which is not yet " "supported."), from); if (buf) { purple_conversation_write(conv, NULL, buf, PURPLE_MESSAGE_SYSTEM | PURPLE_MESSAGE_NOTIFY, time(NULL)); g_free(buf); } } } else { const gchar *application = g_hash_table_lookup(body, "Application-Name"); purple_debug_warning("msn", "Unhandled invite msg with GUID %s: %s.\n", guid, application ? application : "(null)"); } if (!accepted) { MsnSwitchBoard *swboard = cmdproc->data; char *text; MsnMessage *cancel; cancel = msn_message_new(MSN_MSG_TEXT); msn_message_set_content_type(cancel, "text/x-msmsgsinvite"); msn_message_set_charset(cancel, "UTF-8"); msn_message_set_flag(cancel, 'U'); text = g_strdup_printf("Invitation-Command: CANCEL\r\n" "Invitation-Cookie: %s\r\n" "Cancel-Code: REJECT_NOT_INSTALLED\r\n", cookie); msn_message_set_bin_data(cancel, text, strlen(text)); g_free(text); msn_switchboard_send_msg(swboard, cancel, TRUE); msn_message_unref(cancel); } } else if (!strcmp(command, "CANCEL")) { const gchar *code = g_hash_table_lookup(body, "Cancel-Code"); purple_debug_info("msn", "MSMSGS invitation cancelled: %s.\n", code ? code : "no reason given"); } else { /* * Some other already established invitation session. * Can be retrieved by Invitation-Cookie. */ } g_hash_table_destroy(body); } /* Only called from chats. Handwritten messages for IMs come as a SLP message */ void msn_handwritten_msg(MsnCmdProc *cmdproc, MsnMessage *msg) { const char *body; size_t body_len; body = msn_message_get_bin_data(msg, &body_len); msn_switchboard_show_ink(cmdproc->data, msg->remote_user, body); }