pidgin/pidgin

Bump the version for release
release-2.x.y v2.10.12
2015-12-31, Gary Kramlich
2ac1dcb9eb2e
Bump the version for release
/**
* @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);
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();
}
/* NUL-terminate the end of the headers - it'll get skipped over below */
*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") && value) {
char *end = strchr(value, '\"');
if (end) {
*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 (!strcmp(key, "MIME-Version"))
{
/* Ignore MIME-Version header */
}
else if (!strcmp(key, "Content-Type"))
{
char *charset, *c;
if (value && (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 (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 (msg->body && content_type && purple_str_has_prefix(content_type, "text/")) {
char *body = NULL;
if (msg->charset == NULL || g_str_equal(msg->charset, "UTF-8")) {
/* Charset is UTF-8 */
if (!g_utf8_validate(msg->body, msg->body_len, NULL)) {
purple_debug_warning("msn", "Message contains invalid "
"UTF-8. Attempting to salvage.\n");
body = purple_utf8_salvage(msg->body);
payload_len = strlen(body);
}
} else {
/* Charset is something other than UTF-8 */
GError *err = NULL;
body = g_convert(msg->body, msg->body_len, "UTF-8",
msg->charset, NULL, &payload_len, &err);
if (!body || err) {
purple_debug_warning("msn", "Unable to convert message from "
"%s to UTF-8: %s\n", msg->charset,
err ? err->message : "Unknown error");
if (err)
g_error_free(err);
/* Fallback to ISO-8859-1 */
g_free(body);
body = g_convert(msg->body, msg->body_len, "UTF-8",
"ISO-8859-1", NULL, &payload_len, NULL);
if (!body) {
g_free(msg->body);
msg->body = NULL;
msg->body_len = 0;
}
}
}
if (body) {
g_free(msg->body);
msg->body = body;
msg->body_len = payload_len;
msn_message_set_charset(msg, "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);
}
if ((end - n) > 2)
n += g_strlcpy(n, "\r\n", end - n);
body = msn_message_get_bin_data(msg, &body_len);
if (body != NULL && (end - n) > (gssize)body_len)
{
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 (body != NULL)
{
if (msg->type == MSN_MSG_TEXT)
{
g_string_append_len(str, body, body_len);
g_string_append(str, "\r\n");
}
else
{
size_t i;
for (i = 0; i < body_len; i++, body++)
{
g_string_append_printf(str, "%02x ", (unsigned char)*body);
if (i % 16 == 0 && i != 0)
g_string_append_c(str, '\n');
}
g_string_append_c(str, '\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;
MsnP2PVersion p2p;
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);
}
}
p2p = msn_slplink_get_p2p_version(slplink);
msg->part = msn_slpmsgpart_new_from_data(p2p, msg->body, msg->body_len);
if (msg->part)
msn_slplink_process_msg(slplink, msg->part);
else
purple_debug_warning("msn", "P2P message failed to parse.\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);
}