Mercurial > pidgin.yaz
diff libpurple/protocols/msn/msg.c @ 15374:5fe8042783c1
Rename gtk/ and libgaim/ to pidgin/ and libpurple/
author | Sean Egan <seanegan@gmail.com> |
---|---|
date | Sat, 20 Jan 2007 02:32:10 +0000 |
parents | |
children | 32c366eeeb99 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/msn/msg.c Sat Jan 20 02:32:10 2007 +0000 @@ -0,0 +1,787 @@ +/** + * @file msg.c Message functions + * + * gaim + * + * Gaim 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include "msn.h" +#include "msg.h" + +MsnMessage * +msn_message_new(MsnMsgType type) +{ + MsnMessage *msg; + + msg = g_new0(MsnMessage, 1); + msg->type = type; + +#ifdef MSN_DEBUG_MSG + gaim_debug_info("msn", "message new (%p)(%d)\n", msg, type); +#endif + + msg->attr_table = g_hash_table_new_full(g_str_hash, g_str_equal, + g_free, g_free); + + msn_message_ref(msg); + + return msg; +} + +void +msn_message_destroy(MsnMessage *msg) +{ + g_return_if_fail(msg != NULL); + + if (msg->ref_count > 0) + { + msn_message_unref(msg); + + return; + } + +#ifdef MSN_DEBUG_MSG + gaim_debug_info("msn", "message destroy (%p)\n", msg); +#endif + + if (msg->remote_user != NULL) + g_free(msg->remote_user); + + if (msg->body != NULL) + g_free(msg->body); + + if (msg->content_type != NULL) + g_free(msg->content_type); + + if (msg->charset != NULL) + g_free(msg->charset); + + g_hash_table_destroy(msg->attr_table); + g_list_free(msg->attr_list); + + g_free(msg); +} + +MsnMessage * +msn_message_ref(MsnMessage *msg) +{ + g_return_val_if_fail(msg != NULL, NULL); + + msg->ref_count++; + +#ifdef MSN_DEBUG_MSG + gaim_debug_info("msn", "message ref (%p)[%d]\n", msg, msg->ref_count); +#endif + + return msg; +} + +MsnMessage * +msn_message_unref(MsnMessage *msg) +{ + g_return_val_if_fail(msg != NULL, NULL); + g_return_val_if_fail(msg->ref_count > 0, NULL); + + msg->ref_count--; + +#ifdef MSN_DEBUG_MSG + gaim_debug_info("msn", "message unref (%p)[%d]\n", msg, msg->ref_count); +#endif + + if (msg->ref_count == 0) + { + msn_message_destroy(msg); + + return NULL; + } + + return msg; +} + +MsnMessage * +msn_message_new_plain(const char *message) +{ + MsnMessage *msg; + char *message_cr; + + msg = msn_message_new(MSN_MSG_TEXT); + msn_message_set_attr(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_attr(msg, "X-MMS-IM-Format", + "FN=MS%20Sans%20Serif; EF=; CO=0; PF=0"); + + message_cr = gaim_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_attr(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\r\n"); + msn_message_set_flag(msg, 'N'); + msn_message_set_attr(msg,"ID","1\r\n"); + + return msg; +} + +void +msn_message_parse_slp_body(MsnMessage *msg, const char *body, size_t len) +{ + MsnSlpHeader header; + const char *tmp; + int body_len; + + tmp = body; + + if (len < sizeof(header)) { + g_return_if_reached(); + } + + /* Import the header. */ + memcpy(&header, tmp, sizeof(header)); + tmp += sizeof(header); + + msg->msnslp_header.session_id = GUINT32_FROM_LE(header.session_id); + msg->msnslp_header.id = GUINT32_FROM_LE(header.id); + msg->msnslp_header.offset = GUINT64_FROM_LE(header.offset); + msg->msnslp_header.total_size = GUINT64_FROM_LE(header.total_size); + msg->msnslp_header.length = GUINT32_FROM_LE(header.length); + msg->msnslp_header.flags = GUINT32_FROM_LE(header.flags); + msg->msnslp_header.ack_id = GUINT32_FROM_LE(header.ack_id); + msg->msnslp_header.ack_sub_id = GUINT32_FROM_LE(header.ack_sub_id); + msg->msnslp_header.ack_size = GUINT64_FROM_LE(header.ack_size); + + /* Import the body. */ + body_len = len - (tmp - body); + /* msg->body_len = msg->msnslp_header.length; */ + + if (body_len > 0) { + msg->body_len = len - (tmp - body); + msg->body = g_malloc0(msg->body_len + 1); + memcpy(msg->body, tmp, msg->body_len); + tmp += body_len; + } +} + +void +msn_message_parse_payload(MsnMessage *msg, + const char *payload, size_t payload_len) +{ + char *tmp_base, *tmp; + const char *content_type; + char *end; + char **elems, **cur, **tokens; + + g_return_if_fail(payload != NULL); + + tmp_base = tmp = g_malloc0(payload_len + 1); + memcpy(tmp_base, payload, payload_len); + + /* Parse the attributes. */ + end = strstr(tmp, "\r\n\r\n"); + /* 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'; + + elems = g_strsplit(tmp, "\r\n", 0); + + for (cur = elems; *cur != NULL; cur++) + { + const char *key, *value; + + tokens = g_strsplit(*cur, ": ", 2); + + key = tokens[0]; + value = tokens[1]; + + 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_attr(msg, key, value); + } + + g_strfreev(tokens); + } + + g_strfreev(elems); + + /* Proceed to the end of the "\r\n\r\n" */ + tmp = end + 4; + + /* 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")) + { + MsnSlpHeader header; + MsnSlpFooter footer; + int body_len; + + if (payload_len - (tmp - tmp_base) < sizeof(header)) { + g_free(tmp_base); + g_return_if_reached(); + } + + msg->msnslp_message = TRUE; + + /* Import the header. */ + memcpy(&header, tmp, sizeof(header)); + tmp += sizeof(header); + + msg->msnslp_header.session_id = GUINT32_FROM_LE(header.session_id); + msg->msnslp_header.id = GUINT32_FROM_LE(header.id); + msg->msnslp_header.offset = GUINT64_FROM_LE(header.offset); + msg->msnslp_header.total_size = GUINT64_FROM_LE(header.total_size); + msg->msnslp_header.length = GUINT32_FROM_LE(header.length); + msg->msnslp_header.flags = GUINT32_FROM_LE(header.flags); + msg->msnslp_header.ack_id = GUINT32_FROM_LE(header.ack_id); + msg->msnslp_header.ack_sub_id = GUINT32_FROM_LE(header.ack_sub_id); + msg->msnslp_header.ack_size = GUINT64_FROM_LE(header.ack_size); + + body_len = payload_len - (tmp - tmp_base) - sizeof(footer); + + /* Import the body. */ + if (body_len > 0) { + msg->body_len = body_len; + msg->body = g_malloc0(msg->body_len + 1); + memcpy(msg->body, tmp, msg->body_len); + tmp += body_len; + } + + /* Import the footer. */ + if (body_len >= 0) { + memcpy(&footer, tmp, sizeof(footer)); + tmp += sizeof(footer); + msg->msnslp_footer.value = GUINT32_FROM_BE(footer.value); + } + } + else + { + if (payload_len - (tmp - tmp_base) > 0) { + msg->body_len = payload_len - (tmp - tmp_base); + msg->body = g_malloc0(msg->body_len + 1); + memcpy(msg->body, tmp, msg->body_len); + } + } + + 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_slp_body(MsnMessage *msg, size_t *ret_size) +{ + MsnSlpHeader header; + + char *tmp, *base; + const void *body; + size_t len, body_len; + + g_return_val_if_fail(msg != NULL, NULL); + + len = MSN_BUF_LEN; + + base = tmp = g_malloc(len + 1); + + body = msn_message_get_bin_data(msg, &body_len); + + header.session_id = GUINT32_TO_LE(msg->msnslp_header.session_id); + header.id = GUINT32_TO_LE(msg->msnslp_header.id); + header.offset = GUINT64_TO_LE(msg->msnslp_header.offset); + header.total_size = GUINT64_TO_LE(msg->msnslp_header.total_size); + header.length = GUINT32_TO_LE(msg->msnslp_header.length); + header.flags = GUINT32_TO_LE(msg->msnslp_header.flags); + header.ack_id = GUINT32_TO_LE(msg->msnslp_header.ack_id); + header.ack_sub_id = GUINT32_TO_LE(msg->msnslp_header.ack_sub_id); + header.ack_size = GUINT64_TO_LE(msg->msnslp_header.ack_size); + + memcpy(tmp, &header, 48); + tmp += 48; + + if (body != NULL) + { + memcpy(tmp, body, body_len); + tmp += body_len; + } + + if (ret_size != NULL) + *ret_size = tmp - base; + + return base; +} + +char * +msn_message_gen_payload(MsnMessage *msg, size_t *ret_size) +{ + GList *l; + char *n, *base, *end; + int len; + size_t body_len; + 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->attr_list; l != NULL; l = l->next) + { + const char *key; + const char *value; + + key = l->data; + value = msn_message_get_attr(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) + { + MsnSlpHeader header; + MsnSlpFooter footer; + + header.session_id = GUINT32_TO_LE(msg->msnslp_header.session_id); + header.id = GUINT32_TO_LE(msg->msnslp_header.id); + header.offset = GUINT64_TO_LE(msg->msnslp_header.offset); + header.total_size = GUINT64_TO_LE(msg->msnslp_header.total_size); + header.length = GUINT32_TO_LE(msg->msnslp_header.length); + header.flags = GUINT32_TO_LE(msg->msnslp_header.flags); + header.ack_id = GUINT32_TO_LE(msg->msnslp_header.ack_id); + header.ack_sub_id = GUINT32_TO_LE(msg->msnslp_header.ack_sub_id); + header.ack_size = GUINT64_TO_LE(msg->msnslp_header.ack_size); + + memcpy(n, &header, 48); + n += 48; + + if (body != NULL) + { + memcpy(n, body, body_len); + + n += body_len; + } + + footer.value = GUINT32_TO_BE(msg->msnslp_footer.value); + + memcpy(n, &footer, 4); + n += 4; + } + else + { + if (body != NULL) + { + memcpy(n, body, body_len); + n += body_len; + } + } + + 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_malloc0(len + 1); + memcpy(msg->body, data, len); + 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); + + if (msg->content_type != NULL) + g_free(msg->content_type); + + msg->content_type = (type != NULL) ? g_strdup(type) : NULL; +} + +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); + + if (msg->charset != NULL) + g_free(msg->charset); + + msg->charset = (charset != NULL) ? g_strdup(charset) : NULL; +} + +const char * +msn_message_get_charset(const MsnMessage *msg) +{ + g_return_val_if_fail(msg != NULL, NULL); + + return msg->charset; +} + +void +msn_message_set_attr(MsnMessage *msg, const char *attr, const char *value) +{ + const char *temp; + char *new_attr; + + g_return_if_fail(msg != NULL); + g_return_if_fail(attr != NULL); + + temp = msn_message_get_attr(msg, attr); + + if (value == NULL) + { + if (temp != NULL) + { + GList *l; + + for (l = msg->attr_list; l != NULL; l = l->next) + { + if (!g_ascii_strcasecmp(l->data, attr)) + { + msg->attr_list = g_list_remove(msg->attr_list, l->data); + + break; + } + } + + g_hash_table_remove(msg->attr_table, attr); + } + + return; + } + + new_attr = g_strdup(attr); + + g_hash_table_insert(msg->attr_table, new_attr, g_strdup(value)); + + if (temp == NULL) + msg->attr_list = g_list_append(msg->attr_list, new_attr); +} + +const char * +msn_message_get_attr(const MsnMessage *msg, const char *attr) +{ + g_return_val_if_fail(msg != NULL, NULL); + g_return_val_if_fail(attr != NULL, NULL); + + return g_hash_table_lookup(msg->attr_table, attr); +} + +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); + } + + 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->attr_list; l; l = l->next) + { + char *key; + const char *value; + + key = l->data; + value = msn_message_get_attr(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) + { + g_string_append_printf(str, "Session ID: %u\r\n", msg->msnslp_header.session_id); + g_string_append_printf(str, "ID: %u\r\n", msg->msnslp_header.id); + g_string_append_printf(str, "Offset: %" G_GUINT64_FORMAT "\r\n", msg->msnslp_header.offset); + g_string_append_printf(str, "Total size: %" G_GUINT64_FORMAT "\r\n", msg->msnslp_header.total_size); + g_string_append_printf(str, "Length: %u\r\n", msg->msnslp_header.length); + g_string_append_printf(str, "Flags: 0x%x\r\n", msg->msnslp_header.flags); + g_string_append_printf(str, "ACK ID: %u\r\n", msg->msnslp_header.ack_id); + g_string_append_printf(str, "SUB ID: %u\r\n", msg->msnslp_header.ack_sub_id); + g_string_append_printf(str, "ACK Size: %" G_GUINT64_FORMAT "\r\n", msg->msnslp_header.ack_size); + +#ifdef MSN_DEBUG_SLP_VERBOSE + if (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 < msg->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"); + } + } +#endif + + g_string_append_printf(str, "Footer: %u\r\n", msg->msnslp_footer.value); + } + else + { + if (body != NULL) + { + g_string_append_len(str, body, body_len); + g_string_append(str, "\r\n"); + } + } + + gaim_debug_info("msn", "Message %s:\n{%s}\n", info, str->str); + + g_string_free(str, TRUE); +}