Mercurial > pidgin
view libpurple/protocols/myspace/message.c @ 17264:ddcf9ef2ccec
Add MsimMessage implementation (sending only).
author | Jeffrey Connelly <jaconnel@calpoly.edu> |
---|---|
date | Thu, 31 May 2007 02:29:50 +0000 |
parents | |
children | 793301c04e3a |
line wrap: on
line source
/** MySpaceIM protocol messages * * \author Jeff Connelly * * Copyright (C) 2007, Jeff Connelly <jeff2@homing.pidgin.im> * * 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 "myspace.h" #include "message.h" static void msim_msg_free_element(gpointer data, gpointer user_data); static void msim_msg_debug_string_element(gpointer data, gpointer user_data); static gchar *msim_msg_pack_using(MsimMessage *msg, GFunc gf, gchar *sep, gchar *begin, gchar *end); MsimMessage *msim_msg_new(void) { /* Just an empty list. */ return NULL; } /** Free an individual message element. */ static void msim_msg_free_element(gpointer data, gpointer user_data) { MsimMessageElement *elem; elem = (MsimMessageElement *)data; switch (elem->type) { case MSIM_TYPE_BOOLEAN: /* Fall through */ case MSIM_TYPE_INTEGER: /* Integer value stored in gpointer - no need to free(). */ break; case MSIM_TYPE_STRING: /* Always free strings - caller should have g_strdup()'d if * string was static or temporary and not to be freed. */ g_free(elem->data); break; case MSIM_TYPE_BINARY: /* Free the GString itself and the binary data. */ g_string_free((GString *)elem->data, TRUE); break; case MSIM_TYPE_DICTIONARY: /* TODO: free dictionary */ break; case MSIM_TYPE_LIST: /* TODO: free list */ break; default: purple_debug_info("msim", "msim_msg_free_element: not freeing unknown type %d\n", elem->type); break; } g_free(elem); } /** Free a complete message. */ void msim_msg_free(MsimMessage *msg) { if (!msg) { /* already free as can be */ return; } g_list_foreach(msg, msim_msg_free_element, NULL); g_list_free(msg); } /** Send an existing MsimMessage. */ gboolean msim_msg_send(MsimSession *session, MsimMessage *msg) { gchar *raw; gboolean success; raw = msim_msg_pack(msg); success = msim_send_raw(session, raw); g_free(raw); return success; } /** * * Send a message to the server, whose contents is specified using * variable arguments. * * @param session * @param ... A sequence of gchar* key/type/value triplets, terminated with NULL. * * This function exists for coding convenience: it allows a message to be created * and sent in one line of code. Internally it calls msim_msg_send(). * * IMPORTANT: See msim_msg_append() documentation for details on element types. * */ gboolean msim_send(MsimSession *session, ...) { va_list argp; gchar *key, *value; MsimMessageType type; gboolean success; MsimMessage *msg; GString *gs; msg = msim_msg_new(); /* Read key, type, value triplets until NULL. */ va_start(argp, session); do { key = va_arg(argp, gchar *); if (!key) { break; } type = va_arg(argp, int); switch (type) { case MSIM_TYPE_INTEGER: msg = msim_msg_append(msg, key, type, GUINT_TO_POINTER(va_arg(argp, int))); break; case MSIM_TYPE_STRING: value = va_arg(argp, char *); g_return_val_if_fail(value != NULL, FALSE); msg = msim_msg_append(msg, key, type, value); break; case MSIM_TYPE_BINARY: gs = va_arg(argp, GString *); g_return_val_if_fail(gs != NULL, FALSE); /* msim_msg_free() will free this GString the caller created. */ msg = msim_msg_append(msg, key, type, gs); break; default: purple_debug_info("msim", "msim_send: unknown type %d\n", type); break; } } while(key); /* Actually send the message. */ success = msim_msg_send(session, msg); /* Cleanup. */ va_end(argp); msim_msg_free(msg); return success; } /** Append a new element to a message. * * @param name Textual name of element (static string, neither copied nor freed). * @param type An MSIM_TYPE_* code. * @param data Pointer to data, see below. * * @return The new message - must be assigned to as with GList*. For example: * * msg = msim_msg_append(msg, ...) * * The data parameter depends on the type given: * * * MSIM_TYPE_INTEGER: Use GUINT_TO_POINTER(x). * * * MSIM_TYPE_BINARY: Same as integer, non-zero is TRUE and zero is FALSE. * * * MSIM_TYPE_STRING: gchar *. The data WILL BE FREED - use g_strdup() if needed. * * * MSIM_TYPE_BINARY: g_string_new_len(data, length). The data and GString will be freed. * * * MSIM_TYPE_DICTIONARY: TODO * * * MSIM_TYPE_LIST: TODO * * */ MsimMessage *msim_msg_append(MsimMessage *msg, gchar *name, MsimMessageType type, gpointer data) { MsimMessageElement *elem; elem = g_new0(MsimMessageElement, 1); elem->name = name; elem->type = type; elem->data = data; return g_list_append(msg, elem); } /** Pack a string using the given GFunc and seperator. * Used by msim_msg_debug_string() and msim_msg_pack(). */ static gchar *msim_msg_pack_using(MsimMessage *msg, GFunc gf, gchar *sep, gchar *begin, gchar *end) { gchar **strings; gchar **strings_tmp; gchar *joined; gchar *final; int i; g_return_val_if_fail(msg != NULL, NULL); /* Add one for NULL terminator for g_strjoinv(). */ strings = (gchar **)g_new0(gchar *, g_list_length(msg) + 1); strings_tmp = strings; g_list_foreach(msg, gf, &strings_tmp); joined = g_strjoinv(sep, strings); final = g_strconcat(begin, joined, end, NULL); g_free(joined); /* Clean up. */ for (i = 0; i < g_list_length(msg); ++i) { g_free(strings[i]); } g_free(strings); return final; } /** Store a human-readable string describing the element. * * @param data Pointer to an MsimMessageElement. * @param user_data */ static void msim_msg_debug_string_element(gpointer data, gpointer user_data) { MsimMessageElement *elem; gchar *string; GString *gs; gchar *binary; gchar ***items; /* wow, a pointer to a pointer to a pointer */ elem = (MsimMessageElement *)data; items = user_data; switch (elem->type) { case MSIM_TYPE_INTEGER: string = g_strdup_printf("%s(integer): %d", elem->name, GPOINTER_TO_UINT(elem->data)); break; case MSIM_TYPE_STRING: string = g_strdup_printf("%s(string): %s", elem->name, (gchar *)elem->data); break; case MSIM_TYPE_BINARY: gs = (GString *)elem->data; binary = purple_base64_encode((guchar*)gs->str, gs->len); string = g_strdup_printf("%s(binary, %d bytes): %s", elem->name, (int)gs->len, binary); g_free(binary); break; case MSIM_TYPE_BOOLEAN: string = g_strdup_printf("%s(boolean): %s", elem->name, GPOINTER_TO_UINT(elem->data) ? "TRUE" : "FALSE"); break; case MSIM_TYPE_DICTIONARY: /* TODO: provide human-readable output of dictionary. */ string = g_strdup_printf("%s(dict): TODO", elem->name); break; case MSIM_TYPE_LIST: /* TODO: provide human-readable output of list. */ string = g_strdup_printf("%s(list): TODO", elem->name); break; default: string = g_strdup_printf("%s(unknown type %d)", elem->name, elem->type); break; } **items = string; ++(*items); } /** Return a human-readable string of the message. * * @return A string. Caller must g_free(). */ gchar *msim_msg_debug_string(MsimMessage *msg) { if (!msg) { return g_strdup("<MsimMessage: empty>"); } return msim_msg_pack_using(msg, msim_msg_debug_string_element, "\n", "<MsimMessage: \n", ">"); } /** Pack an element into its protocol representation. * * @param data Pointer to an MsimMessageElement. * @param user_data */ static void msim_msg_pack_element(gpointer data, gpointer user_data) { MsimMessageElement *elem; gchar *string; gchar ***items; gchar *binary; gchar *escaped; GString *gs; elem = (MsimMessageElement *)data; items = user_data; switch (elem->type) { case MSIM_TYPE_INTEGER: string = g_strdup_printf("%s\\%d", elem->name, GPOINTER_TO_UINT(elem->data)); break; case MSIM_TYPE_STRING: /* Strings get escaped. */ escaped = msim_escape((gchar *)elem->data); string = g_strdup_printf("%s\\%s", elem->name, escaped); g_free(escaped); break; case MSIM_TYPE_BINARY: gs = (GString *)elem->data; binary = purple_base64_encode((guchar*)gs->str, gs->len); /* Do not escape! */ string = g_strdup_printf("%s\\%s", elem->name, binary); g_free(binary); break; case MSIM_TYPE_BOOLEAN: if (GPOINTER_TO_UINT(elem->data)) { string = g_strdup_printf("%s\\\\", elem->name); } else { /* False - leave out. */ string = g_strdup(""); } break; case MSIM_TYPE_DICTIONARY: /* TODO: pack using k=v\034k2=v2\034... */ string = g_strdup_printf("%s\\TODO", elem->name); break; case MSIM_TYPE_LIST: /* TODO: pack using a|b|c|d|... */ string = g_strdup_printf("%s\\TODO", elem->name); break; default: g_return_if_fail(FALSE); break; } **items = string; ++(*items); } /** Return a packed string suitable for sending over the wire. * * @return A string. Caller must g_free(). */ gchar *msim_msg_pack(MsimMessage *msg) { g_return_val_if_fail(msg != NULL, NULL); return msim_msg_pack_using(msg, msim_msg_pack_element, "\\", "\\", "\\final\\"); }