# HG changeset patch # User Jeffrey Connelly # Date 1190076471 0 # Node ID 36ba89a2b7dacf7ef154655803b927abca001ab1 # Parent 191fa8a8c0bc0ffb14ac186fd94d977a6b5b7f81 Replace all CRLF line-endings with LF line endings (Unix-style). This fixes some compilation errors on Solaris. Closes #3096. diff -r 191fa8a8c0bc -r 36ba89a2b7da libpurple/protocols/myspace/markup.c --- a/libpurple/protocols/myspace/markup.c Mon Sep 17 03:34:11 2007 +0000 +++ b/libpurple/protocols/myspace/markup.c Tue Sep 18 00:47:51 2007 +0000 @@ -1,698 +1,698 @@ -/* MySpaceIM Protocol Plugin - markup - * - * Copyright (C) 2007, Jeff Connelly - * - * 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" - -typedef void (*MSIM_XMLNODE_CONVERT)(MsimSession *, xmlnode *, gchar **, gchar **); - -/* Internal functions */ - -static guint msim_point_to_purple_size(MsimSession *session, guint point); -static guint msim_purple_size_to_point(MsimSession *session, guint size); -static guint msim_height_to_point(MsimSession *session, guint height); -static guint msim_point_to_height(MsimSession *session, guint point); - -static void msim_markup_tag_to_html(MsimSession *, xmlnode *root, gchar **begin, gchar **end); -static void html_tag_to_msim_markup(MsimSession *, xmlnode *root, gchar **begin, gchar **end); -static gchar *msim_convert_xml(MsimSession *, const gchar *raw, MSIM_XMLNODE_CONVERT f); -static gchar *msim_convert_smileys_to_markup(gchar *before); -static double msim_round(double round); - - -/* Globals */ - -/* The names in in emoticon_names (for ) map to corresponding - * entries in emoticon_symbols (for the ASCII representation of the emoticon). - * - * Multiple emoticon symbols in Pidgin can map to one name. List the - * canonical form, as inserted by the "Smile!" dialog, first. For example, - * :) comes before :-), because although both are recognized as 'happy', - * the first is inserted by the smiley button (first symbol in theme). - * - * Note that symbols are case-sensitive in Pidgin -- :-X is not :-x. */ -static struct MSIM_EMOTICON -{ - gchar *name; - gchar *symbol; -} msim_emoticons[] = { - /* Unfortunately, this list duplicates much of the file - * pidgin/pidgin/pixmaps/emotes/default/22/default.theme.in, because - * that file is part of Pidgin, but we're part of libpurple. - */ - { "bigsmile", ":D" }, - { "bigsmile", ":-D" }, - { "devil", "}:)" }, - { "frazzled", ":Z" }, - { "geek", "B)" }, - { "googles", "%)" }, - { "growl", ":E" }, - { "laugh", ":))" }, /* Must be before ':)' */ - { "happy", ":)" }, - { "happy", ":-)" }, - { "happi", ":)" }, - { "heart", ":X" }, - { "mohawk", "-:" }, - { "mad", "X(" }, - { "messed", "X)" }, - { "nerd", "Q)" }, - { "oops", ":G" }, - { "pirate", "P)" }, - { "scared", ":O" }, - { "sidefrown", ":{" }, - { "sinister", ":B" }, - { "smirk", ":," }, - { "straight", ":|" }, - { "tongue", ":P" }, - { "tongue", ":p" }, - { "tongy", ":P" }, - { "upset", "B|" }, - { "wink", ";-)" }, - { "wink", ";)" }, - { "winc", ";)" }, - { "worried", ":[" }, - { "kiss", ":x" }, - { NULL, NULL } -}; - - - -/* Indexes of this array + 1 map HTML font size to scale of normal font size. * - * Based on _point_sizes from libpurple/gtkimhtml.c - * 1 2 3 4 5 6 7 */ -static gdouble _font_scale[] = { .85, .95, 1, 1.2, 1.44, 1.728, 2.0736 }; - -#define MAX_FONT_SIZE 7 /* Purple maximum font size */ -#define POINTS_PER_INCH 72 /* How many pt's in an inch */ - -/* Text formatting bits for */ -#define MSIM_TEXT_BOLD 1 -#define MSIM_TEXT_ITALIC 2 -#define MSIM_TEXT_UNDERLINE 4 - -/* Default baseline size of purple's fonts, in points. What is size 3 in points. - * _font_scale specifies scaling factor relative to this point size. Note this - * is only the default; it is configurable in account options. */ -#define MSIM_BASE_FONT_POINT_SIZE 8 - -/* Default display's DPI. 96 is common but it can differ. Also configurable - * in account options. */ -#define MSIM_DEFAULT_DPI 96 - - -/* round is part of C99, but sometimes is unavailable before then. - * Based on http://forums.belution.com/en/cpp/000/050/13.shtml - */ -double msim_round(double value) -{ - if (value < 0) { - return -(floor(-value + 0.5)); - } else { - return floor( value + 0.5); - } -} - - -/** Convert typographical font point size to HTML font size. - * Based on libpurple/gtkimhtml.c */ -static guint -msim_point_to_purple_size(MsimSession *session, guint point) -{ - guint size, this_point, base; - gdouble scale; - - base = purple_account_get_int(session->account, "base_font_size", MSIM_BASE_FONT_POINT_SIZE); - - for (size = 0; - size < sizeof(_font_scale) / sizeof(_font_scale[0]); - ++size) { - scale = _font_scale[CLAMP(size, 1, MAX_FONT_SIZE) - 1]; - this_point = (guint)msim_round(scale * base); - - if (this_point >= point) { - purple_debug_info("msim", "msim_point_to_purple_size: %d pt -> size=%d\n", - point, size); - return size; - } - } - - /* No HTML font size was this big; return largest possible. */ - return this_point; -} - -/** Convert HTML font size to point size. */ -static guint -msim_purple_size_to_point(MsimSession *session, guint size) -{ - gdouble scale; - guint point; - guint base; - - scale = _font_scale[CLAMP(size, 1, MAX_FONT_SIZE) - 1]; - - base = purple_account_get_int(session->account, "base_font_size", MSIM_BASE_FONT_POINT_SIZE); - - point = (guint)msim_round(scale * base); - - purple_debug_info("msim", "msim_purple_size_to_point: size=%d -> %d pt\n", - size, point); - - return point; -} - -/** Convert a msim markup font pixel height to the more usual point size, for incoming messages. */ -static guint -msim_height_to_point(MsimSession *session, guint height) -{ - guint dpi; - - dpi = purple_account_get_int(session->account, "port", MSIM_DEFAULT_DPI); - - return (guint)msim_round((POINTS_PER_INCH * 1. / dpi) * height); - - /* See also: libpurple/protocols/bonjour/jabber.c - * _font_size_ichat_to_purple */ -} - -/** Convert point size to msim pixel height font size specification, for outgoing messages. */ -static guint -msim_point_to_height(MsimSession *session, guint point) -{ - guint dpi; - - dpi = purple_account_get_int(session->account, "port", MSIM_DEFAULT_DPI); - - return (guint)msim_round((dpi * 1. / POINTS_PER_INCH) * point); -} - -/** Convert the msim markup (font) tag into HTML. */ -static void -msim_markup_f_to_html(MsimSession *session, xmlnode *root, gchar **begin, gchar **end) -{ - const gchar *face, *height_str, *decor_str; - GString *gs_end, *gs_begin; - guint decor, height; - - face = xmlnode_get_attrib(root, "f"); - height_str = xmlnode_get_attrib(root, "h"); - decor_str = xmlnode_get_attrib(root, "s"); - - if (height_str) { - height = atol(height_str); - } else { - height = 12; - } - - if (decor_str) { - decor = atol(decor_str); - } else { - decor = 0; - } - - gs_begin = g_string_new(""); - /* TODO: get font size working */ - if (height && !face) { - g_string_printf(gs_begin, "", - msim_point_to_purple_size(session, msim_height_to_point(session, height))); - } else if (height && face) { - g_string_printf(gs_begin, "", face, - msim_point_to_purple_size(session, msim_height_to_point(session, height))); - } else { - g_string_printf(gs_begin, ""); - } - - /* No support for font-size CSS? */ - /* g_string_printf(gs_begin, "", face, - msim_height_to_point(height)); */ - - gs_end = g_string_new(""); - - if (decor & MSIM_TEXT_BOLD) { - g_string_append(gs_begin, ""); - g_string_prepend(gs_end, ""); - } - - if (decor & MSIM_TEXT_ITALIC) { - g_string_append(gs_begin, ""); - g_string_append(gs_end, ""); - } - - if (decor & MSIM_TEXT_UNDERLINE) { - g_string_append(gs_begin, ""); - g_string_append(gs_end, ""); - } - - - *begin = gs_begin->str; - *end = gs_end->str; -} - -/** Convert a msim markup color to a color suitable for libpurple. - * - * @param msim Either a color name, or an rgb(x,y,z) code. - * - * @return A new string, either a color name or #rrggbb code. Must g_free(). - */ -static char * -msim_color_to_purple(const char *msim) -{ - guint red, green, blue; - - if (!msim) { - return g_strdup("black"); - } - - if (sscanf(msim, "rgb(%d,%d,%d)", &red, &green, &blue) != 3) { - /* Color name. */ - return g_strdup(msim); - } - /* TODO: rgba (alpha). */ - - return g_strdup_printf("#%.2x%.2x%.2x", red, green, blue); -} - -/** Convert the msim markup (anchor) tag into HTML. */ -static void -msim_markup_a_to_html(MsimSession *session, xmlnode *root, gchar **begin, gchar **end) -{ - const gchar *href; - - href = xmlnode_get_attrib(root, "h"); - if (!href) { - href = ""; - } - - *begin = g_strdup_printf("%s", href, href); - *end = g_strdup(""); -} - -/** Convert the msim markup

(paragraph) tag into HTML. */ -static void -msim_markup_p_to_html(MsimSession *session, xmlnode *root, gchar **begin, gchar **end) -{ - /* Just pass through unchanged. - * - * Note: attributes currently aren't passed, if there are any. */ - *begin = g_strdup("

"); - *end = g_strdup("

"); -} - -/** Convert the msim markup tag (text color) into HTML. TODO: Test */ -static void -msim_markup_c_to_html(MsimSession *session, xmlnode *root, gchar **begin, gchar **end) -{ - const gchar *color; - gchar *purple_color; - - color = xmlnode_get_attrib(root, "v"); - if (!color) { - purple_debug_info("msim", "msim_markup_c_to_html: tag w/o v attr"); - *begin = g_strdup(""); - *end = g_strdup(""); - /* TODO: log as unrecognized */ - return; - } - - purple_color = msim_color_to_purple(color); - - *begin = g_strdup_printf("", purple_color); - - g_free(purple_color); - - /* *begin = g_strdup_printf("", color); */ - *end = g_strdup(""); -} - -/** Convert the msim markup tag (background color) into HTML. TODO: Test */ -static void -msim_markup_b_to_html(MsimSession *session, xmlnode *root, gchar **begin, gchar **end) -{ - const gchar *color; - gchar *purple_color; - - color = xmlnode_get_attrib(root, "v"); - if (!color) { - *begin = g_strdup(""); - *end = g_strdup(""); - purple_debug_info("msim", "msim_markup_b_to_html: w/o v attr"); - /* TODO: log as unrecognized. */ - return; - } - - purple_color = msim_color_to_purple(color); - - /* TODO: find out how to set background color. */ - *begin = g_strdup_printf("", - purple_color); - g_free(purple_color); - - *end = g_strdup("

"); -} - -/** Convert the msim markup tag (emoticon image) into HTML. */ -static void -msim_markup_i_to_html(MsimSession *session, xmlnode *root, gchar **begin, gchar **end) -{ - const gchar *name; - guint i; - struct MSIM_EMOTICON *emote; - - name = xmlnode_get_attrib(root, "n"); - if (!name) { - purple_debug_info("msim", "msim_markup_i_to_html: w/o n"); - *begin = g_strdup(""); - *end = g_strdup(""); - /* TODO: log as unrecognized */ - return; - } - - /* Find and use canonical form of smiley symbol. */ - for (i = 0; (emote = &msim_emoticons[i]) && emote->name != NULL; ++i) { - if (g_str_equal(name, emote->name)) { - *begin = g_strdup(emote->symbol); - *end = g_strdup(""); - return; - } - } - - /* Couldn't find it, sorry. Try to degrade gracefully. */ - *begin = g_strdup_printf("**%s**", name); - *end = g_strdup(""); -} - -/** Convert an individual msim markup tag to HTML. */ -static void -msim_markup_tag_to_html(MsimSession *session, xmlnode *root, gchar **begin, - gchar **end) -{ - if (g_str_equal(root->name, "f")) { - msim_markup_f_to_html(session, root, begin, end); - } else if (g_str_equal(root->name, "a")) { - msim_markup_a_to_html(session, root, begin, end); - } else if (g_str_equal(root->name, "p")) { - msim_markup_p_to_html(session, root, begin, end); - } else if (g_str_equal(root->name, "c")) { - msim_markup_c_to_html(session, root, begin, end); - } else if (g_str_equal(root->name, "b")) { - msim_markup_b_to_html(session, root, begin, end); - } else if (g_str_equal(root->name, "i")) { - msim_markup_i_to_html(session, root, begin, end); - } else { - purple_debug_info("msim", "msim_markup_tag_to_html: " - "unknown tag name=%s, ignoring", - (root && root->name) ? root->name : "(NULL)"); - *begin = g_strdup(""); - *end = g_strdup(""); - } -} - -/** Convert an individual HTML tag to msim markup. */ -static void -html_tag_to_msim_markup(MsimSession *session, xmlnode *root, gchar **begin, - gchar **end) -{ - if (!purple_utf8_strcasecmp(root->name, "root") || - !purple_utf8_strcasecmp(root->name, "html")) { - *begin = g_strdup(""); - *end = g_strdup(""); - /* TODO: Coalesce nested tags into one tag! - * Currently, the 's' value will be overwritten when b/i/u is nested - * within another one, and only the inner-most formatting will be - * applied to the text. */ - } else if (!purple_utf8_strcasecmp(root->name, "b")) { - *begin = g_strdup_printf("", MSIM_TEXT_BOLD); - *end = g_strdup(""); - } else if (!purple_utf8_strcasecmp(root->name, "i")) { - *begin = g_strdup_printf("", MSIM_TEXT_ITALIC); - *end = g_strdup(""); - } else if (!purple_utf8_strcasecmp(root->name, "u")) { - *begin = g_strdup_printf("", MSIM_TEXT_UNDERLINE); - *end = g_strdup(""); - } else if (!purple_utf8_strcasecmp(root->name, "a")) { - const gchar *href, *link_text; - - href = xmlnode_get_attrib(root, "href"); - - if (!href) { - href = xmlnode_get_attrib(root, "HREF"); - } - - link_text = xmlnode_get_data(root); - - if (href) { - if (g_str_equal(link_text, href)) { - /* Purple gives us: URL - * Translate to - * Displayed as text of URL with link to URL - */ - *begin = g_strdup_printf("", href); - } else { - /* But if we get: text - * Translate to: text: - * - * Because official client only supports self-closed - * tags; you can't change the link text. - */ - *begin = g_strdup_printf("%s: ", link_text, href); - } - } else { - *begin = g_strdup(""); - } - - /* Sorry, kid. MySpace doesn't support you within tags. */ - xmlnode_free(root->child); - root->child = NULL; - - *end = g_strdup(""); - } else if (!purple_utf8_strcasecmp(root->name, "font")) { - const gchar *size; - const gchar *face; - - size = xmlnode_get_attrib(root, "size"); - face = xmlnode_get_attrib(root, "face"); - - if (face && size) { - *begin = g_strdup_printf("", face, - msim_point_to_height(session, - msim_purple_size_to_point(session, atoi(size)))); - } else if (face) { - *begin = g_strdup_printf("", face); - } else if (size) { - *begin = g_strdup_printf("", - msim_point_to_height(session, - msim_purple_size_to_point(session, atoi(size)))); - } else { - *begin = g_strdup(""); - } - - *end = g_strdup(""); - - /* TODO: color (bg uses ), emoticons */ - } else { - -#ifdef MSIM_MARKUP_SHOW_UNKNOWN_TAGS - *begin = g_strdup_printf("[%s]", root->name); - *end = g_strdup_printf("[/%s]", root->name); -#else - *begin = g_strdup(""); - *end = g_strdup(""); -#endif - } -} - -/** Convert an xmlnode of msim markup or HTML to an HTML string or msim markup. - * - * @param f Function to convert tags. - * - * @return An HTML string. Caller frees. - */ -static gchar * -msim_convert_xmlnode(MsimSession *session, xmlnode *root, MSIM_XMLNODE_CONVERT f) -{ - xmlnode *node; - gchar *begin, *inner, *end; - GString *final; - - if (!root || !root->name) { - return g_strdup(""); - } - - purple_debug_info("msim", "msim_convert_xmlnode: got root=%s\n", - root->name); - - begin = inner = end = NULL; - - final = g_string_new(""); - - f(session, root, &begin, &end); - - g_string_append(final, begin); - - /* Loop over all child nodes. */ - for (node = root->child; node != NULL; node = node->next) { - switch (node->type) { - case XMLNODE_TYPE_ATTRIB: - /* Attributes handled above. */ - break; - - case XMLNODE_TYPE_TAG: - /* A tag or tag with attributes. Recursively descend. */ - inner = msim_convert_xmlnode(session, node, f); - g_return_val_if_fail(inner != NULL, NULL); - - purple_debug_info("msim", " ** node name=%s\n", - (node && node->name) ? node->name : "(NULL)"); - break; - - case XMLNODE_TYPE_DATA: - /* Literal text. */ - inner = g_new0(char, node->data_sz + 1); - strncpy(inner, node->data, node->data_sz); - inner[node->data_sz] = 0; - - purple_debug_info("msim", " ** node data=%s\n", - inner ? inner : "(NULL)"); - break; - - default: - purple_debug_info("msim", - "msim_convert_xmlnode: strange node\n"); - inner = g_strdup(""); - } - - if (inner) { - g_string_append(final, inner); - } - } - - /* TODO: Note that msim counts each piece of text enclosed by as - * a paragraph and will display each on its own line. You actually have - * to _nest_ tags to intersperse different text in one paragraph! - * Comment out this line below to see. */ - g_string_append(final, end); - - purple_debug_info("msim", "msim_markup_xmlnode_to_gtkhtml: RETURNING %s\n", - (final && final->str) ? final->str : "(NULL)"); - - return final->str; -} - -/** Convert XML to something based on MSIM_XMLNODE_CONVERT. */ -static gchar * -msim_convert_xml(MsimSession *session, const gchar *raw, MSIM_XMLNODE_CONVERT f) -{ - xmlnode *root; - gchar *str; - gchar *enclosed_raw; - - g_return_val_if_fail(raw != NULL, NULL); - - /* Enclose text in one root tag, to try to make it valid XML for parsing. */ - enclosed_raw = g_strconcat("", raw, "", NULL); - - root = xmlnode_from_str(enclosed_raw, -1); - - if (!root) { - purple_debug_info("msim", "msim_markup_to_html: couldn't parse " - "%s as XML, returning raw: %s\n", enclosed_raw, raw); - /* TODO: msim_unrecognized */ - g_free(enclosed_raw); - return g_strdup(raw); - } - - g_free(enclosed_raw); - - str = msim_convert_xmlnode(session, root, f); - g_return_val_if_fail(str != NULL, NULL); - purple_debug_info("msim", "msim_markup_to_html: returning %s\n", str); - - xmlnode_free(root); - - return str; -} - -/** Convert plaintext smileys to markup tags. - * - * @param before Original text with ASCII smileys. Will be freed. - * @return A new string with tags, if applicable. Must be g_free()'d. - */ -static gchar * -msim_convert_smileys_to_markup(gchar *before) -{ - gchar *old, *new, *replacement; - guint i; - struct MSIM_EMOTICON *emote; - - old = before; - new = NULL; - - for (i = 0; (emote = &msim_emoticons[i]) && emote->name != NULL; ++i) { - gchar *name, *symbol; - - name = emote->name; - symbol = emote->symbol; - - replacement = g_strdup_printf("", name); - - purple_debug_info("msim", "msim_convert_smileys_to_markup: %s->%s\n", - symbol ? symbol : "(NULL)", - replacement ? replacement : "(NULL)"); - new = purple_strreplace(old, symbol, replacement); - - g_free(replacement); - g_free(old); - - old = new; - } - - return new; -} - - -/** High-level function to convert MySpaceIM markup to Purple (HTML) markup. - * - * @return Purple markup string, must be g_free()'d. */ -gchar * -msim_markup_to_html(MsimSession *session, const gchar *raw) -{ - return msim_convert_xml(session, raw, - (MSIM_XMLNODE_CONVERT)(msim_markup_tag_to_html)); -} - -/** High-level function to convert Purple (HTML) to MySpaceIM markup. - * - * TODO: consider using purple_markup_html_to_xhtml() to make valid XML. - * - * @return HTML markup string, must be g_free()'d. */ -gchar * -html_to_msim_markup(MsimSession *session, const gchar *raw) -{ - gchar *markup; - - markup = msim_convert_xml(session, raw, - (MSIM_XMLNODE_CONVERT)(html_tag_to_msim_markup)); - - if (purple_account_get_bool(session->account, "emoticons", TRUE)) { - /* Frees markup and allocates a new one. */ - markup = msim_convert_smileys_to_markup(markup); - } - - return markup; -} - - +/* MySpaceIM Protocol Plugin - markup + * + * Copyright (C) 2007, Jeff Connelly + * + * 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" + +typedef void (*MSIM_XMLNODE_CONVERT)(MsimSession *, xmlnode *, gchar **, gchar **); + +/* Internal functions */ + +static guint msim_point_to_purple_size(MsimSession *session, guint point); +static guint msim_purple_size_to_point(MsimSession *session, guint size); +static guint msim_height_to_point(MsimSession *session, guint height); +static guint msim_point_to_height(MsimSession *session, guint point); + +static void msim_markup_tag_to_html(MsimSession *, xmlnode *root, gchar **begin, gchar **end); +static void html_tag_to_msim_markup(MsimSession *, xmlnode *root, gchar **begin, gchar **end); +static gchar *msim_convert_xml(MsimSession *, const gchar *raw, MSIM_XMLNODE_CONVERT f); +static gchar *msim_convert_smileys_to_markup(gchar *before); +static double msim_round(double round); + + +/* Globals */ + +/* The names in in emoticon_names (for ) map to corresponding + * entries in emoticon_symbols (for the ASCII representation of the emoticon). + * + * Multiple emoticon symbols in Pidgin can map to one name. List the + * canonical form, as inserted by the "Smile!" dialog, first. For example, + * :) comes before :-), because although both are recognized as 'happy', + * the first is inserted by the smiley button (first symbol in theme). + * + * Note that symbols are case-sensitive in Pidgin -- :-X is not :-x. */ +static struct MSIM_EMOTICON +{ + gchar *name; + gchar *symbol; +} msim_emoticons[] = { + /* Unfortunately, this list duplicates much of the file + * pidgin/pidgin/pixmaps/emotes/default/22/default.theme.in, because + * that file is part of Pidgin, but we're part of libpurple. + */ + { "bigsmile", ":D" }, + { "bigsmile", ":-D" }, + { "devil", "}:)" }, + { "frazzled", ":Z" }, + { "geek", "B)" }, + { "googles", "%)" }, + { "growl", ":E" }, + { "laugh", ":))" }, /* Must be before ':)' */ + { "happy", ":)" }, + { "happy", ":-)" }, + { "happi", ":)" }, + { "heart", ":X" }, + { "mohawk", "-:" }, + { "mad", "X(" }, + { "messed", "X)" }, + { "nerd", "Q)" }, + { "oops", ":G" }, + { "pirate", "P)" }, + { "scared", ":O" }, + { "sidefrown", ":{" }, + { "sinister", ":B" }, + { "smirk", ":," }, + { "straight", ":|" }, + { "tongue", ":P" }, + { "tongue", ":p" }, + { "tongy", ":P" }, + { "upset", "B|" }, + { "wink", ";-)" }, + { "wink", ";)" }, + { "winc", ";)" }, + { "worried", ":[" }, + { "kiss", ":x" }, + { NULL, NULL } +}; + + + +/* Indexes of this array + 1 map HTML font size to scale of normal font size. * + * Based on _point_sizes from libpurple/gtkimhtml.c + * 1 2 3 4 5 6 7 */ +static gdouble _font_scale[] = { .85, .95, 1, 1.2, 1.44, 1.728, 2.0736 }; + +#define MAX_FONT_SIZE 7 /* Purple maximum font size */ +#define POINTS_PER_INCH 72 /* How many pt's in an inch */ + +/* Text formatting bits for */ +#define MSIM_TEXT_BOLD 1 +#define MSIM_TEXT_ITALIC 2 +#define MSIM_TEXT_UNDERLINE 4 + +/* Default baseline size of purple's fonts, in points. What is size 3 in points. + * _font_scale specifies scaling factor relative to this point size. Note this + * is only the default; it is configurable in account options. */ +#define MSIM_BASE_FONT_POINT_SIZE 8 + +/* Default display's DPI. 96 is common but it can differ. Also configurable + * in account options. */ +#define MSIM_DEFAULT_DPI 96 + + +/* round is part of C99, but sometimes is unavailable before then. + * Based on http://forums.belution.com/en/cpp/000/050/13.shtml + */ +double msim_round(double value) +{ + if (value < 0) { + return -(floor(-value + 0.5)); + } else { + return floor( value + 0.5); + } +} + + +/** Convert typographical font point size to HTML font size. + * Based on libpurple/gtkimhtml.c */ +static guint +msim_point_to_purple_size(MsimSession *session, guint point) +{ + guint size, this_point, base; + gdouble scale; + + base = purple_account_get_int(session->account, "base_font_size", MSIM_BASE_FONT_POINT_SIZE); + + for (size = 0; + size < sizeof(_font_scale) / sizeof(_font_scale[0]); + ++size) { + scale = _font_scale[CLAMP(size, 1, MAX_FONT_SIZE) - 1]; + this_point = (guint)msim_round(scale * base); + + if (this_point >= point) { + purple_debug_info("msim", "msim_point_to_purple_size: %d pt -> size=%d\n", + point, size); + return size; + } + } + + /* No HTML font size was this big; return largest possible. */ + return this_point; +} + +/** Convert HTML font size to point size. */ +static guint +msim_purple_size_to_point(MsimSession *session, guint size) +{ + gdouble scale; + guint point; + guint base; + + scale = _font_scale[CLAMP(size, 1, MAX_FONT_SIZE) - 1]; + + base = purple_account_get_int(session->account, "base_font_size", MSIM_BASE_FONT_POINT_SIZE); + + point = (guint)msim_round(scale * base); + + purple_debug_info("msim", "msim_purple_size_to_point: size=%d -> %d pt\n", + size, point); + + return point; +} + +/** Convert a msim markup font pixel height to the more usual point size, for incoming messages. */ +static guint +msim_height_to_point(MsimSession *session, guint height) +{ + guint dpi; + + dpi = purple_account_get_int(session->account, "port", MSIM_DEFAULT_DPI); + + return (guint)msim_round((POINTS_PER_INCH * 1. / dpi) * height); + + /* See also: libpurple/protocols/bonjour/jabber.c + * _font_size_ichat_to_purple */ +} + +/** Convert point size to msim pixel height font size specification, for outgoing messages. */ +static guint +msim_point_to_height(MsimSession *session, guint point) +{ + guint dpi; + + dpi = purple_account_get_int(session->account, "port", MSIM_DEFAULT_DPI); + + return (guint)msim_round((dpi * 1. / POINTS_PER_INCH) * point); +} + +/** Convert the msim markup (font) tag into HTML. */ +static void +msim_markup_f_to_html(MsimSession *session, xmlnode *root, gchar **begin, gchar **end) +{ + const gchar *face, *height_str, *decor_str; + GString *gs_end, *gs_begin; + guint decor, height; + + face = xmlnode_get_attrib(root, "f"); + height_str = xmlnode_get_attrib(root, "h"); + decor_str = xmlnode_get_attrib(root, "s"); + + if (height_str) { + height = atol(height_str); + } else { + height = 12; + } + + if (decor_str) { + decor = atol(decor_str); + } else { + decor = 0; + } + + gs_begin = g_string_new(""); + /* TODO: get font size working */ + if (height && !face) { + g_string_printf(gs_begin, "", + msim_point_to_purple_size(session, msim_height_to_point(session, height))); + } else if (height && face) { + g_string_printf(gs_begin, "", face, + msim_point_to_purple_size(session, msim_height_to_point(session, height))); + } else { + g_string_printf(gs_begin, ""); + } + + /* No support for font-size CSS? */ + /* g_string_printf(gs_begin, "", face, + msim_height_to_point(height)); */ + + gs_end = g_string_new(""); + + if (decor & MSIM_TEXT_BOLD) { + g_string_append(gs_begin, ""); + g_string_prepend(gs_end, ""); + } + + if (decor & MSIM_TEXT_ITALIC) { + g_string_append(gs_begin, ""); + g_string_append(gs_end, ""); + } + + if (decor & MSIM_TEXT_UNDERLINE) { + g_string_append(gs_begin, ""); + g_string_append(gs_end, ""); + } + + + *begin = gs_begin->str; + *end = gs_end->str; +} + +/** Convert a msim markup color to a color suitable for libpurple. + * + * @param msim Either a color name, or an rgb(x,y,z) code. + * + * @return A new string, either a color name or #rrggbb code. Must g_free(). + */ +static char * +msim_color_to_purple(const char *msim) +{ + guint red, green, blue; + + if (!msim) { + return g_strdup("black"); + } + + if (sscanf(msim, "rgb(%d,%d,%d)", &red, &green, &blue) != 3) { + /* Color name. */ + return g_strdup(msim); + } + /* TODO: rgba (alpha). */ + + return g_strdup_printf("#%.2x%.2x%.2x", red, green, blue); +} + +/** Convert the msim markup (anchor) tag into HTML. */ +static void +msim_markup_a_to_html(MsimSession *session, xmlnode *root, gchar **begin, gchar **end) +{ + const gchar *href; + + href = xmlnode_get_attrib(root, "h"); + if (!href) { + href = ""; + } + + *begin = g_strdup_printf("%s", href, href); + *end = g_strdup(""); +} + +/** Convert the msim markup

(paragraph) tag into HTML. */ +static void +msim_markup_p_to_html(MsimSession *session, xmlnode *root, gchar **begin, gchar **end) +{ + /* Just pass through unchanged. + * + * Note: attributes currently aren't passed, if there are any. */ + *begin = g_strdup("

"); + *end = g_strdup("

"); +} + +/** Convert the msim markup tag (text color) into HTML. TODO: Test */ +static void +msim_markup_c_to_html(MsimSession *session, xmlnode *root, gchar **begin, gchar **end) +{ + const gchar *color; + gchar *purple_color; + + color = xmlnode_get_attrib(root, "v"); + if (!color) { + purple_debug_info("msim", "msim_markup_c_to_html: tag w/o v attr"); + *begin = g_strdup(""); + *end = g_strdup(""); + /* TODO: log as unrecognized */ + return; + } + + purple_color = msim_color_to_purple(color); + + *begin = g_strdup_printf("", purple_color); + + g_free(purple_color); + + /* *begin = g_strdup_printf("", color); */ + *end = g_strdup(""); +} + +/** Convert the msim markup tag (background color) into HTML. TODO: Test */ +static void +msim_markup_b_to_html(MsimSession *session, xmlnode *root, gchar **begin, gchar **end) +{ + const gchar *color; + gchar *purple_color; + + color = xmlnode_get_attrib(root, "v"); + if (!color) { + *begin = g_strdup(""); + *end = g_strdup(""); + purple_debug_info("msim", "msim_markup_b_to_html: w/o v attr"); + /* TODO: log as unrecognized. */ + return; + } + + purple_color = msim_color_to_purple(color); + + /* TODO: find out how to set background color. */ + *begin = g_strdup_printf("", + purple_color); + g_free(purple_color); + + *end = g_strdup("

"); +} + +/** Convert the msim markup tag (emoticon image) into HTML. */ +static void +msim_markup_i_to_html(MsimSession *session, xmlnode *root, gchar **begin, gchar **end) +{ + const gchar *name; + guint i; + struct MSIM_EMOTICON *emote; + + name = xmlnode_get_attrib(root, "n"); + if (!name) { + purple_debug_info("msim", "msim_markup_i_to_html: w/o n"); + *begin = g_strdup(""); + *end = g_strdup(""); + /* TODO: log as unrecognized */ + return; + } + + /* Find and use canonical form of smiley symbol. */ + for (i = 0; (emote = &msim_emoticons[i]) && emote->name != NULL; ++i) { + if (g_str_equal(name, emote->name)) { + *begin = g_strdup(emote->symbol); + *end = g_strdup(""); + return; + } + } + + /* Couldn't find it, sorry. Try to degrade gracefully. */ + *begin = g_strdup_printf("**%s**", name); + *end = g_strdup(""); +} + +/** Convert an individual msim markup tag to HTML. */ +static void +msim_markup_tag_to_html(MsimSession *session, xmlnode *root, gchar **begin, + gchar **end) +{ + if (g_str_equal(root->name, "f")) { + msim_markup_f_to_html(session, root, begin, end); + } else if (g_str_equal(root->name, "a")) { + msim_markup_a_to_html(session, root, begin, end); + } else if (g_str_equal(root->name, "p")) { + msim_markup_p_to_html(session, root, begin, end); + } else if (g_str_equal(root->name, "c")) { + msim_markup_c_to_html(session, root, begin, end); + } else if (g_str_equal(root->name, "b")) { + msim_markup_b_to_html(session, root, begin, end); + } else if (g_str_equal(root->name, "i")) { + msim_markup_i_to_html(session, root, begin, end); + } else { + purple_debug_info("msim", "msim_markup_tag_to_html: " + "unknown tag name=%s, ignoring", + (root && root->name) ? root->name : "(NULL)"); + *begin = g_strdup(""); + *end = g_strdup(""); + } +} + +/** Convert an individual HTML tag to msim markup. */ +static void +html_tag_to_msim_markup(MsimSession *session, xmlnode *root, gchar **begin, + gchar **end) +{ + if (!purple_utf8_strcasecmp(root->name, "root") || + !purple_utf8_strcasecmp(root->name, "html")) { + *begin = g_strdup(""); + *end = g_strdup(""); + /* TODO: Coalesce nested tags into one tag! + * Currently, the 's' value will be overwritten when b/i/u is nested + * within another one, and only the inner-most formatting will be + * applied to the text. */ + } else if (!purple_utf8_strcasecmp(root->name, "b")) { + *begin = g_strdup_printf("", MSIM_TEXT_BOLD); + *end = g_strdup(""); + } else if (!purple_utf8_strcasecmp(root->name, "i")) { + *begin = g_strdup_printf("", MSIM_TEXT_ITALIC); + *end = g_strdup(""); + } else if (!purple_utf8_strcasecmp(root->name, "u")) { + *begin = g_strdup_printf("", MSIM_TEXT_UNDERLINE); + *end = g_strdup(""); + } else if (!purple_utf8_strcasecmp(root->name, "a")) { + const gchar *href, *link_text; + + href = xmlnode_get_attrib(root, "href"); + + if (!href) { + href = xmlnode_get_attrib(root, "HREF"); + } + + link_text = xmlnode_get_data(root); + + if (href) { + if (g_str_equal(link_text, href)) { + /* Purple gives us: URL + * Translate to + * Displayed as text of URL with link to URL + */ + *begin = g_strdup_printf("", href); + } else { + /* But if we get: text + * Translate to: text: + * + * Because official client only supports self-closed + * tags; you can't change the link text. + */ + *begin = g_strdup_printf("%s: ", link_text, href); + } + } else { + *begin = g_strdup(""); + } + + /* Sorry, kid. MySpace doesn't support you within tags. */ + xmlnode_free(root->child); + root->child = NULL; + + *end = g_strdup(""); + } else if (!purple_utf8_strcasecmp(root->name, "font")) { + const gchar *size; + const gchar *face; + + size = xmlnode_get_attrib(root, "size"); + face = xmlnode_get_attrib(root, "face"); + + if (face && size) { + *begin = g_strdup_printf("", face, + msim_point_to_height(session, + msim_purple_size_to_point(session, atoi(size)))); + } else if (face) { + *begin = g_strdup_printf("", face); + } else if (size) { + *begin = g_strdup_printf("", + msim_point_to_height(session, + msim_purple_size_to_point(session, atoi(size)))); + } else { + *begin = g_strdup(""); + } + + *end = g_strdup(""); + + /* TODO: color (bg uses ), emoticons */ + } else { + +#ifdef MSIM_MARKUP_SHOW_UNKNOWN_TAGS + *begin = g_strdup_printf("[%s]", root->name); + *end = g_strdup_printf("[/%s]", root->name); +#else + *begin = g_strdup(""); + *end = g_strdup(""); +#endif + } +} + +/** Convert an xmlnode of msim markup or HTML to an HTML string or msim markup. + * + * @param f Function to convert tags. + * + * @return An HTML string. Caller frees. + */ +static gchar * +msim_convert_xmlnode(MsimSession *session, xmlnode *root, MSIM_XMLNODE_CONVERT f) +{ + xmlnode *node; + gchar *begin, *inner, *end; + GString *final; + + if (!root || !root->name) { + return g_strdup(""); + } + + purple_debug_info("msim", "msim_convert_xmlnode: got root=%s\n", + root->name); + + begin = inner = end = NULL; + + final = g_string_new(""); + + f(session, root, &begin, &end); + + g_string_append(final, begin); + + /* Loop over all child nodes. */ + for (node = root->child; node != NULL; node = node->next) { + switch (node->type) { + case XMLNODE_TYPE_ATTRIB: + /* Attributes handled above. */ + break; + + case XMLNODE_TYPE_TAG: + /* A tag or tag with attributes. Recursively descend. */ + inner = msim_convert_xmlnode(session, node, f); + g_return_val_if_fail(inner != NULL, NULL); + + purple_debug_info("msim", " ** node name=%s\n", + (node && node->name) ? node->name : "(NULL)"); + break; + + case XMLNODE_TYPE_DATA: + /* Literal text. */ + inner = g_new0(char, node->data_sz + 1); + strncpy(inner, node->data, node->data_sz); + inner[node->data_sz] = 0; + + purple_debug_info("msim", " ** node data=%s\n", + inner ? inner : "(NULL)"); + break; + + default: + purple_debug_info("msim", + "msim_convert_xmlnode: strange node\n"); + inner = g_strdup(""); + } + + if (inner) { + g_string_append(final, inner); + } + } + + /* TODO: Note that msim counts each piece of text enclosed by as + * a paragraph and will display each on its own line. You actually have + * to _nest_ tags to intersperse different text in one paragraph! + * Comment out this line below to see. */ + g_string_append(final, end); + + purple_debug_info("msim", "msim_markup_xmlnode_to_gtkhtml: RETURNING %s\n", + (final && final->str) ? final->str : "(NULL)"); + + return final->str; +} + +/** Convert XML to something based on MSIM_XMLNODE_CONVERT. */ +static gchar * +msim_convert_xml(MsimSession *session, const gchar *raw, MSIM_XMLNODE_CONVERT f) +{ + xmlnode *root; + gchar *str; + gchar *enclosed_raw; + + g_return_val_if_fail(raw != NULL, NULL); + + /* Enclose text in one root tag, to try to make it valid XML for parsing. */ + enclosed_raw = g_strconcat("", raw, "", NULL); + + root = xmlnode_from_str(enclosed_raw, -1); + + if (!root) { + purple_debug_info("msim", "msim_markup_to_html: couldn't parse " + "%s as XML, returning raw: %s\n", enclosed_raw, raw); + /* TODO: msim_unrecognized */ + g_free(enclosed_raw); + return g_strdup(raw); + } + + g_free(enclosed_raw); + + str = msim_convert_xmlnode(session, root, f); + g_return_val_if_fail(str != NULL, NULL); + purple_debug_info("msim", "msim_markup_to_html: returning %s\n", str); + + xmlnode_free(root); + + return str; +} + +/** Convert plaintext smileys to markup tags. + * + * @param before Original text with ASCII smileys. Will be freed. + * @return A new string with tags, if applicable. Must be g_free()'d. + */ +static gchar * +msim_convert_smileys_to_markup(gchar *before) +{ + gchar *old, *new, *replacement; + guint i; + struct MSIM_EMOTICON *emote; + + old = before; + new = NULL; + + for (i = 0; (emote = &msim_emoticons[i]) && emote->name != NULL; ++i) { + gchar *name, *symbol; + + name = emote->name; + symbol = emote->symbol; + + replacement = g_strdup_printf("", name); + + purple_debug_info("msim", "msim_convert_smileys_to_markup: %s->%s\n", + symbol ? symbol : "(NULL)", + replacement ? replacement : "(NULL)"); + new = purple_strreplace(old, symbol, replacement); + + g_free(replacement); + g_free(old); + + old = new; + } + + return new; +} + + +/** High-level function to convert MySpaceIM markup to Purple (HTML) markup. + * + * @return Purple markup string, must be g_free()'d. */ +gchar * +msim_markup_to_html(MsimSession *session, const gchar *raw) +{ + return msim_convert_xml(session, raw, + (MSIM_XMLNODE_CONVERT)(msim_markup_tag_to_html)); +} + +/** High-level function to convert Purple (HTML) to MySpaceIM markup. + * + * TODO: consider using purple_markup_html_to_xhtml() to make valid XML. + * + * @return HTML markup string, must be g_free()'d. */ +gchar * +html_to_msim_markup(MsimSession *session, const gchar *raw) +{ + gchar *markup; + + markup = msim_convert_xml(session, raw, + (MSIM_XMLNODE_CONVERT)(html_tag_to_msim_markup)); + + if (purple_account_get_bool(session->account, "emoticons", TRUE)) { + /* Frees markup and allocates a new one. */ + markup = msim_convert_smileys_to_markup(markup); + } + + return markup; +} + + diff -r 191fa8a8c0bc -r 36ba89a2b7da libpurple/protocols/myspace/markup.h --- a/libpurple/protocols/myspace/markup.h Mon Sep 17 03:34:11 2007 +0000 +++ b/libpurple/protocols/myspace/markup.h Tue Sep 18 00:47:51 2007 +0000 @@ -1,27 +1,27 @@ -/* MySpaceIM Protocol Plugin - markup - * - * Copyright (C) 2007, Jeff Connelly - * - * 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 - */ - -#ifndef _MYSPACE_MARKUP_H -#define _MYSPACE_MARKUP_H - -/* High-level msim markup <=> Purple html conversion functions. */ -gchar *msim_markup_to_html(MsimSession *, const gchar *raw); -gchar *html_to_msim_markup(MsimSession *, const gchar *raw); - -#endif /* !_MYSPACE_MARKUP_H */ +/* MySpaceIM Protocol Plugin - markup + * + * Copyright (C) 2007, Jeff Connelly + * + * 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 + */ + +#ifndef _MYSPACE_MARKUP_H +#define _MYSPACE_MARKUP_H + +/* High-level msim markup <=> Purple html conversion functions. */ +gchar *msim_markup_to_html(MsimSession *, const gchar *raw); +gchar *html_to_msim_markup(MsimSession *, const gchar *raw); + +#endif /* !_MYSPACE_MARKUP_H */ diff -r 191fa8a8c0bc -r 36ba89a2b7da libpurple/protocols/myspace/session.c --- a/libpurple/protocols/myspace/session.c Mon Sep 17 03:34:11 2007 +0000 +++ b/libpurple/protocols/myspace/session.c Tue Sep 18 00:47:51 2007 +0000 @@ -1,95 +1,95 @@ -/* MySpaceIM Protocol Plugin, session - * - * Copyright (C) 2007, Jeff Connelly - * - * 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" - -/* Session methods */ - -/** - * Create a new MSIM session. - * - * @param acct The account to create the session from. - * - * @return Pointer to a new session. Free with msim_session_destroy. - */ -MsimSession * -msim_session_new(PurpleAccount *acct) -{ - MsimSession *session; - - g_return_val_if_fail(acct != NULL, NULL); - - session = g_new0(MsimSession, 1); - - session->magic = MSIM_SESSION_STRUCT_MAGIC; - session->account = acct; - session->gc = purple_account_get_connection(acct); - session->sesskey = 0; - session->userid = 0; - session->username = NULL; - session->fd = -1; - - /* TODO: Remove. */ - session->user_lookup_cb = g_hash_table_new_full(g_direct_hash, - g_direct_equal, NULL, NULL); /* do NOT free function pointers! (values) */ - session->user_lookup_cb_data = g_hash_table_new_full(g_direct_hash, - g_direct_equal, NULL, NULL);/* TODO: we don't know what the values are, - they could be integers inside gpointers - or strings, so I don't freed them. - Figure this out, once free cache. */ - - /* Created in msim_process_server_info() */ - session->server_info = NULL; - - session->rxoff = 0; - session->rxbuf = g_new0(gchar, MSIM_READ_BUF_SIZE); - session->next_rid = 1; - session->last_comm = time(NULL); - session->inbox_status = 0; - - return session; -} - -/** - * Free a session. - * - * @param session The session to destroy. - */ -void -msim_session_destroy(MsimSession *session) -{ - g_return_if_fail(MSIM_SESSION_VALID(session)); - - session->magic = -1; - - g_free(session->rxbuf); - g_free(session->username); - - /* TODO: Remove. */ - g_hash_table_destroy(session->user_lookup_cb); - g_hash_table_destroy(session->user_lookup_cb_data); - - if (session->server_info) { - msim_msg_free(session->server_info); - } - - g_free(session); -} - +/* MySpaceIM Protocol Plugin, session + * + * Copyright (C) 2007, Jeff Connelly + * + * 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" + +/* Session methods */ + +/** + * Create a new MSIM session. + * + * @param acct The account to create the session from. + * + * @return Pointer to a new session. Free with msim_session_destroy. + */ +MsimSession * +msim_session_new(PurpleAccount *acct) +{ + MsimSession *session; + + g_return_val_if_fail(acct != NULL, NULL); + + session = g_new0(MsimSession, 1); + + session->magic = MSIM_SESSION_STRUCT_MAGIC; + session->account = acct; + session->gc = purple_account_get_connection(acct); + session->sesskey = 0; + session->userid = 0; + session->username = NULL; + session->fd = -1; + + /* TODO: Remove. */ + session->user_lookup_cb = g_hash_table_new_full(g_direct_hash, + g_direct_equal, NULL, NULL); /* do NOT free function pointers! (values) */ + session->user_lookup_cb_data = g_hash_table_new_full(g_direct_hash, + g_direct_equal, NULL, NULL);/* TODO: we don't know what the values are, + they could be integers inside gpointers + or strings, so I don't freed them. + Figure this out, once free cache. */ + + /* Created in msim_process_server_info() */ + session->server_info = NULL; + + session->rxoff = 0; + session->rxbuf = g_new0(gchar, MSIM_READ_BUF_SIZE); + session->next_rid = 1; + session->last_comm = time(NULL); + session->inbox_status = 0; + + return session; +} + +/** + * Free a session. + * + * @param session The session to destroy. + */ +void +msim_session_destroy(MsimSession *session) +{ + g_return_if_fail(MSIM_SESSION_VALID(session)); + + session->magic = -1; + + g_free(session->rxbuf); + g_free(session->username); + + /* TODO: Remove. */ + g_hash_table_destroy(session->user_lookup_cb); + g_hash_table_destroy(session->user_lookup_cb_data); + + if (session->server_info) { + msim_msg_free(session->server_info); + } + + g_free(session); +} + diff -r 191fa8a8c0bc -r 36ba89a2b7da libpurple/protocols/myspace/session.h --- a/libpurple/protocols/myspace/session.h Mon Sep 17 03:34:11 2007 +0000 +++ b/libpurple/protocols/myspace/session.h Tue Sep 18 00:47:51 2007 +0000 @@ -1,57 +1,57 @@ -/* MySpaceIM Protocol Plugin, session - * - * Copyright (C) 2007, Jeff Connelly - * - * 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 - */ - -#ifndef _MYSPACE_SESSION_H -#define _MYSPACE_SESSION_H - -/* Random number in every MsimSession, to ensure it is valid. */ -#define MSIM_SESSION_STRUCT_MAGIC 0xe4a6752b - -/* Everything needed to keep track of a session (proto_data field in PurpleConnection) */ -typedef struct _MsimSession -{ - guint magic; /**< MSIM_SESSION_STRUCT_MAGIC */ - PurpleAccount *account; - PurpleConnection *gc; - guint sesskey; /**< Session key from server */ - guint userid; /**< This user's numeric user ID */ - gchar *username; /**< This user's unique username */ - gint fd; /**< File descriptor to/from server */ - - /* TODO: Remove. */ - GHashTable *user_lookup_cb; /**< Username -> userid lookup callback */ - GHashTable *user_lookup_cb_data; /**< Username -> userid lookup callback data */ - - MsimMessage *server_info; /**< Parameters from server */ - - gchar *rxbuf; /**< Receive buffer */ - guint rxoff; /**< Receive buffer offset */ - guint next_rid; /**< Next request/response ID */ - time_t last_comm; /**< Time received last communication */ - guint inbox_status; /**< Bit field of inbox notifications */ -} MsimSession; - -/* Check if an MsimSession is valid */ -#define MSIM_SESSION_VALID(s) (session != NULL && session->magic == MSIM_SESSION_STRUCT_MAGIC) - - -MsimSession *msim_session_new(PurpleAccount *acct); -void msim_session_destroy(MsimSession *session); - -#endif /* !_MYSPACE_SESSION_H */ +/* MySpaceIM Protocol Plugin, session + * + * Copyright (C) 2007, Jeff Connelly + * + * 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 + */ + +#ifndef _MYSPACE_SESSION_H +#define _MYSPACE_SESSION_H + +/* Random number in every MsimSession, to ensure it is valid. */ +#define MSIM_SESSION_STRUCT_MAGIC 0xe4a6752b + +/* Everything needed to keep track of a session (proto_data field in PurpleConnection) */ +typedef struct _MsimSession +{ + guint magic; /**< MSIM_SESSION_STRUCT_MAGIC */ + PurpleAccount *account; + PurpleConnection *gc; + guint sesskey; /**< Session key from server */ + guint userid; /**< This user's numeric user ID */ + gchar *username; /**< This user's unique username */ + gint fd; /**< File descriptor to/from server */ + + /* TODO: Remove. */ + GHashTable *user_lookup_cb; /**< Username -> userid lookup callback */ + GHashTable *user_lookup_cb_data; /**< Username -> userid lookup callback data */ + + MsimMessage *server_info; /**< Parameters from server */ + + gchar *rxbuf; /**< Receive buffer */ + guint rxoff; /**< Receive buffer offset */ + guint next_rid; /**< Next request/response ID */ + time_t last_comm; /**< Time received last communication */ + guint inbox_status; /**< Bit field of inbox notifications */ +} MsimSession; + +/* Check if an MsimSession is valid */ +#define MSIM_SESSION_VALID(s) (session != NULL && session->magic == MSIM_SESSION_STRUCT_MAGIC) + + +MsimSession *msim_session_new(PurpleAccount *acct); +void msim_session_destroy(MsimSession *session); + +#endif /* !_MYSPACE_SESSION_H */ diff -r 191fa8a8c0bc -r 36ba89a2b7da libpurple/protocols/myspace/user.c --- a/libpurple/protocols/myspace/user.c Mon Sep 17 03:34:11 2007 +0000 +++ b/libpurple/protocols/myspace/user.c Tue Sep 18 00:47:51 2007 +0000 @@ -1,437 +1,437 @@ -/* MySpaceIM Protocol Plugin, header file - * - * Copyright (C) 2007, Jeff Connelly - * - * 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" - -static void msim_store_user_info_each(const gchar *key_str, gchar *value_str, MsimUser *user); -static gchar *msim_format_now_playing(gchar *band, gchar *song); -static void msim_downloaded_buddy_icon(PurpleUtilFetchUrlData *url_data, gpointer user_data, const gchar *url_text, - gsize len, const gchar *error_message); - -/** Format the "now playing" indicator, showing the artist and song. - * @return Return a new string (must be g_free()'d), or NULL. - */ -static gchar * -msim_format_now_playing(gchar *band, gchar *song) -{ - if ((band && strlen(band)) || (song && strlen(song))) { - return g_strdup_printf("%s - %s", - (band && strlen(band)) ? band : "Unknown Artist", - (song && strlen(song)) ? song : "Unknown Song"); - } else { - return NULL; - } -} -/** Get the MsimUser from a PurpleBuddy, creating it if needed. */ -MsimUser * -msim_get_user_from_buddy(PurpleBuddy *buddy) -{ - MsimUser *user; - - if (!buddy) { - return NULL; - } - - if (!buddy->proto_data) { - /* No MsimUser for this buddy; make one. */ - - /* TODO: where is this freed? */ - user = g_new0(MsimUser, 1); - user->buddy = buddy; - buddy->proto_data = (gpointer)user; - } - - user = (MsimUser *)(buddy->proto_data); - - return user; -} - -/** Find and return an MsimUser * representing a user on the buddy list, or NULL. */ -MsimUser * -msim_find_user(MsimSession *session, const gchar *username) -{ - PurpleBuddy *buddy; - MsimUser *user; - - buddy = purple_find_buddy(session->account, username); - if (!buddy) { - return NULL; - } - - user = msim_get_user_from_buddy(buddy); - - return user; -} - -/** Append user information to a PurpleNotifyUserInfo, given an MsimUser. - * Used by msim_tooltip_text() and msim_get_info_cb() to show a user's profile. - */ -void -msim_append_user_info(MsimSession *session, PurpleNotifyUserInfo *user_info, MsimUser *user, gboolean full) -{ - gchar *str; - guint uid; - guint cv; - - /* Useful to identify the account the tooltip refers to. - * Other prpls show this. */ - if (user->username) { - purple_notify_user_info_add_pair(user_info, _("User"), user->username); - } - - uid = purple_blist_node_get_int(&user->buddy->node, "UserID"); - - if (full) { - /* TODO: link to username, if available */ - purple_notify_user_info_add_pair(user_info, _("Profile"), - g_strdup_printf("http://myspace.com/%d", - uid, uid)); - } - - - /* a/s/l...the vitals */ - if (user->age) { - purple_notify_user_info_add_pair(user_info, _("Age"), - g_strdup_printf("%d", user->age)); - } - - if (user->gender && strlen(user->gender)) { - purple_notify_user_info_add_pair(user_info, _("Gender"), user->gender); - } - - if (user->location && strlen(user->location)) { - purple_notify_user_info_add_pair(user_info, _("Location"), user->location); - } - - /* Other information */ - if (user->headline && strlen(user->headline)) { - purple_notify_user_info_add_pair(user_info, _("Headline"), user->headline); - } - - str = msim_format_now_playing(user->band_name, user->song_name); - if (str && strlen(str)) { - purple_notify_user_info_add_pair(user_info, _("Song"), str); - } - - /* Note: total friends only available if looked up by uid, not username. */ - if (user->total_friends) { - purple_notify_user_info_add_pair(user_info, _("Total Friends"), - g_strdup_printf("%d", user->total_friends)); - } - - if (full) { - /* Client information */ - - str = user->client_info; - cv = user->client_cv; - - if (str && cv != 0) { - purple_notify_user_info_add_pair(user_info, _("Client Version"), - g_strdup_printf("%s (build %d)", str, cv)); - } else if (str) { - purple_notify_user_info_add_pair(user_info, _("Client Version"), - g_strdup(str)); - } else if (cv) { - purple_notify_user_info_add_pair(user_info, _("Client Version"), - g_strdup_printf("Build %d", cv)); - } - } -} - -/** Store a field of information about a buddy. */ -void -msim_store_user_info_each(const gchar *key_str, gchar *value_str, MsimUser *user) -{ - if (g_str_equal(key_str, "UserID") || g_str_equal(key_str, "ContactID")) { - /* Save to buddy list, if it exists, for quick cached uid lookup with msim_uid2username_from_blist(). */ - if (user->buddy) - { - purple_debug_info("msim", "associating uid %s with username %s\n", key_str, user->buddy->name); - purple_blist_node_set_int(&user->buddy->node, "UserID", atol(value_str)); - } - /* Need to store in MsimUser, too? What if not on blist? */ - } else if (g_str_equal(key_str, "Age")) { - user->age = atol(value_str); - } else if (g_str_equal(key_str, "Gender")) { - user->gender = g_strdup(value_str); - } else if (g_str_equal(key_str, "Location")) { - user->location = g_strdup(value_str); - } else if (g_str_equal(key_str, "TotalFriends")) { - user->total_friends = atol(value_str); - } else if (g_str_equal(key_str, "DisplayName")) { - user->display_name = g_strdup(value_str); - } else if (g_str_equal(key_str, "BandName")) { - user->band_name = g_strdup(value_str); - } else if (g_str_equal(key_str, "SongName")) { - user->song_name = g_strdup(value_str); - } else if (g_str_equal(key_str, "UserName") || g_str_equal(key_str, "IMName") || g_str_equal(key_str, "NickName")) { - /* Ignore because PurpleBuddy knows this already */ - ; - } else if (g_str_equal(key_str, "ImageURL") || g_str_equal(key_str, "AvatarURL")) { - const gchar *previous_url; - - user->image_url = g_strdup(value_str); - - /* Instead of showing 'no photo' picture, show nothing. */ - if (g_str_equal(user->image_url, "http://x.myspace.com/images/no_pic.gif")) - { - purple_buddy_icons_set_for_user(user->buddy->account, - user->buddy->name, - NULL, 0, NULL); - return; - } - - /* TODO: use ETag for checksum */ - previous_url = purple_buddy_icons_get_checksum_for_user(user->buddy); - - /* Only download if URL changed */ - if (!previous_url || !g_str_equal(previous_url, user->image_url)) { - purple_util_fetch_url(user->image_url, TRUE, NULL, TRUE, msim_downloaded_buddy_icon, (gpointer)user); - } - } else if (g_str_equal(key_str, "LastImageUpdated")) { - /* TODO: use somewhere */ - user->last_image_updated = atol(value_str); - } else if (g_str_equal(key_str, "Headline")) { - user->headline = g_strdup(value_str); - } else { - /* TODO: other fields in MsimUser */ - gchar *msg; - - msg = g_strdup_printf("msim_store_user_info_each: unknown field %s=%s", - key_str, value_str); - - msim_unrecognized(NULL, NULL, msg); - - g_free(msg); - } -} - -/** Save buddy information to the buddy list from a user info reply message. - * - * @param session - * @param msg The user information reply, with any amount of information. - * @param user The structure to save to, or NULL to save in PurpleBuddy->proto_data. - * - * Variable information is saved to the passed MsimUser structure. Permanent - * information (UserID) is stored in the blist node of the buddy list (and - * ends up in blist.xml, persisted to disk) if it exists. - * - * If the function has no buddy information, this function - * is a no-op (and returns FALSE). - * - */ -gboolean -msim_store_user_info(MsimSession *session, MsimMessage *msg, MsimUser *user) -{ - gchar *username; - MsimMessage *body, *body_node; - - g_return_val_if_fail(MSIM_SESSION_VALID(session), FALSE); - g_return_val_if_fail(msg != NULL, FALSE); - - body = msim_msg_get_dictionary(msg, "body"); - if (!body) { - return FALSE; - } - - username = msim_msg_get_string(body, "UserName"); - - if (!username) { - purple_debug_info("msim", - "msim_process_reply: not caching body, no UserName\n"); - msim_msg_free(body); - g_free(username); - return FALSE; - } - - /* Null user = find and store in PurpleBuddy's proto_data */ - if (!user) { - user = msim_find_user(session, username); - if (!user) { - msim_msg_free(body); - g_free(username); - return FALSE; - } - } - - /* TODO: make looping over MsimMessage's easier. */ - for (body_node = body; - body_node != NULL; - body_node = msim_msg_get_next_element_node(body_node)) - { - const gchar *key_str; - gchar *value_str; - MsimMessageElement *elem; - - elem = (MsimMessageElement *)body_node->data; - key_str = elem->name; - - value_str = msim_msg_get_string_from_element(elem); - msim_store_user_info_each(key_str, value_str, user); - g_free(value_str); - } - - if (msim_msg_get_integer(msg, "dsn") == MG_OWN_IM_INFO_DSN && - msim_msg_get_integer(msg, "lid") == MG_OWN_IM_INFO_LID) { - /* TODO: do something with our own IM info, if we need it for some - * specific purpose. Otherwise it is available on the buddy list, - * if the user has themselves as their own buddy. - * - * However, much of the info is already available in MsimSession, - * stored in msim_we_are_logged_on(). */ - } else if (msim_msg_get_integer(msg, "dsn") == MG_OWN_MYSPACE_INFO_DSN && - msim_msg_get_integer(msg, "lid") == MG_OWN_MYSPACE_INFO_LID) { - /* TODO: same as above, but for MySpace info. */ - } - - msim_msg_free(body); - - return TRUE; -} - -/** - * Asynchronously lookup user information, calling callback when receive result. - * - * @param session - * @param user The user id, email address, or username. Not freed. - * @param cb Callback, called with user information when available. - * @param data An arbitray data pointer passed to the callback. - */ -/* TODO: change to not use callbacks */ -void -msim_lookup_user(MsimSession *session, const gchar *user, MSIM_USER_LOOKUP_CB cb, gpointer data) -{ - MsimMessage *body; - gchar *field_name; - guint rid, cmd, dsn, lid; - - g_return_if_fail(MSIM_SESSION_VALID(session)); - g_return_if_fail(user != NULL); - /* Callback can be null to not call anything, just lookup & store information. */ - /*g_return_if_fail(cb != NULL);*/ - - purple_debug_info("msim", "msim_lookup_userid: " - "asynchronously looking up <%s>\n", user); - - msim_msg_dump("msim_lookup_user: data=%s\n", (MsimMessage *)data); - - /* Setup callback. Response will be associated with request using 'rid'. */ - rid = msim_new_reply_callback(session, cb, data); - - /* Send request */ - - cmd = MSIM_CMD_GET; - - if (msim_is_userid(user)) { - field_name = "UserID"; - dsn = MG_MYSPACE_INFO_BY_ID_DSN; - lid = MG_MYSPACE_INFO_BY_ID_LID; - } else if (msim_is_email(user)) { - field_name = "Email"; - dsn = MG_MYSPACE_INFO_BY_STRING_DSN; - lid = MG_MYSPACE_INFO_BY_STRING_LID; - } else { - field_name = "UserName"; - dsn = MG_MYSPACE_INFO_BY_STRING_DSN; - lid = MG_MYSPACE_INFO_BY_STRING_LID; - } - - body = msim_msg_new( - field_name, MSIM_TYPE_STRING, g_strdup(user), - NULL); - - g_return_if_fail(msim_send(session, - "persist", MSIM_TYPE_INTEGER, 1, - "sesskey", MSIM_TYPE_INTEGER, session->sesskey, - "cmd", MSIM_TYPE_INTEGER, 1, - "dsn", MSIM_TYPE_INTEGER, dsn, - "uid", MSIM_TYPE_INTEGER, session->userid, - "lid", MSIM_TYPE_INTEGER, lid, - "rid", MSIM_TYPE_INTEGER, rid, - "body", MSIM_TYPE_DICTIONARY, body, - NULL)); -} - - -/** - * Check if a string is a userid (all numeric). - * - * @param user The user id, email, or name. - * - * @return TRUE if is userid, FALSE if not. - */ -gboolean -msim_is_userid(const gchar *user) -{ - g_return_val_if_fail(user != NULL, FALSE); - - return strspn(user, "0123456789") == strlen(user); -} - -/** - * Check if a string is an email address (contains an @). - * - * @param user The user id, email, or name. - * - * @return TRUE if is an email, FALSE if not. - * - * This function is not intended to be used as a generic - * means of validating email addresses, but to distinguish - * between a user represented by an email address from - * other forms of identification. - */ -gboolean -msim_is_email(const gchar *user) -{ - g_return_val_if_fail(user != NULL, FALSE); - - return strchr(user, '@') != NULL; -} - - -/** Callback for when a buddy icon finished being downloaded. */ -static void -msim_downloaded_buddy_icon(PurpleUtilFetchUrlData *url_data, - gpointer user_data, - const gchar *url_text, - gsize len, - const gchar *error_message) -{ - MsimUser *user; - - user = (MsimUser *)user_data; - - purple_debug_info("msim_downloaded_buddy_icon", - "Downloaded %d bytes\n", len); - - if (!url_text) { - purple_debug_info("msim_downloaded_buddy_icon", - "failed to download icon for %s", - user->buddy->name); - return; - } - - purple_buddy_icons_set_for_user(user->buddy->account, - user->buddy->name, - g_memdup((gchar *)url_text, len), len, - /* Use URL itself as buddy icon "checksum" (TODO: ETag) */ - user->image_url); /* checksum */ -} - - +/* MySpaceIM Protocol Plugin, header file + * + * Copyright (C) 2007, Jeff Connelly + * + * 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" + +static void msim_store_user_info_each(const gchar *key_str, gchar *value_str, MsimUser *user); +static gchar *msim_format_now_playing(gchar *band, gchar *song); +static void msim_downloaded_buddy_icon(PurpleUtilFetchUrlData *url_data, gpointer user_data, const gchar *url_text, + gsize len, const gchar *error_message); + +/** Format the "now playing" indicator, showing the artist and song. + * @return Return a new string (must be g_free()'d), or NULL. + */ +static gchar * +msim_format_now_playing(gchar *band, gchar *song) +{ + if ((band && strlen(band)) || (song && strlen(song))) { + return g_strdup_printf("%s - %s", + (band && strlen(band)) ? band : "Unknown Artist", + (song && strlen(song)) ? song : "Unknown Song"); + } else { + return NULL; + } +} +/** Get the MsimUser from a PurpleBuddy, creating it if needed. */ +MsimUser * +msim_get_user_from_buddy(PurpleBuddy *buddy) +{ + MsimUser *user; + + if (!buddy) { + return NULL; + } + + if (!buddy->proto_data) { + /* No MsimUser for this buddy; make one. */ + + /* TODO: where is this freed? */ + user = g_new0(MsimUser, 1); + user->buddy = buddy; + buddy->proto_data = (gpointer)user; + } + + user = (MsimUser *)(buddy->proto_data); + + return user; +} + +/** Find and return an MsimUser * representing a user on the buddy list, or NULL. */ +MsimUser * +msim_find_user(MsimSession *session, const gchar *username) +{ + PurpleBuddy *buddy; + MsimUser *user; + + buddy = purple_find_buddy(session->account, username); + if (!buddy) { + return NULL; + } + + user = msim_get_user_from_buddy(buddy); + + return user; +} + +/** Append user information to a PurpleNotifyUserInfo, given an MsimUser. + * Used by msim_tooltip_text() and msim_get_info_cb() to show a user's profile. + */ +void +msim_append_user_info(MsimSession *session, PurpleNotifyUserInfo *user_info, MsimUser *user, gboolean full) +{ + gchar *str; + guint uid; + guint cv; + + /* Useful to identify the account the tooltip refers to. + * Other prpls show this. */ + if (user->username) { + purple_notify_user_info_add_pair(user_info, _("User"), user->username); + } + + uid = purple_blist_node_get_int(&user->buddy->node, "UserID"); + + if (full) { + /* TODO: link to username, if available */ + purple_notify_user_info_add_pair(user_info, _("Profile"), + g_strdup_printf("http://myspace.com/%d", + uid, uid)); + } + + + /* a/s/l...the vitals */ + if (user->age) { + purple_notify_user_info_add_pair(user_info, _("Age"), + g_strdup_printf("%d", user->age)); + } + + if (user->gender && strlen(user->gender)) { + purple_notify_user_info_add_pair(user_info, _("Gender"), user->gender); + } + + if (user->location && strlen(user->location)) { + purple_notify_user_info_add_pair(user_info, _("Location"), user->location); + } + + /* Other information */ + if (user->headline && strlen(user->headline)) { + purple_notify_user_info_add_pair(user_info, _("Headline"), user->headline); + } + + str = msim_format_now_playing(user->band_name, user->song_name); + if (str && strlen(str)) { + purple_notify_user_info_add_pair(user_info, _("Song"), str); + } + + /* Note: total friends only available if looked up by uid, not username. */ + if (user->total_friends) { + purple_notify_user_info_add_pair(user_info, _("Total Friends"), + g_strdup_printf("%d", user->total_friends)); + } + + if (full) { + /* Client information */ + + str = user->client_info; + cv = user->client_cv; + + if (str && cv != 0) { + purple_notify_user_info_add_pair(user_info, _("Client Version"), + g_strdup_printf("%s (build %d)", str, cv)); + } else if (str) { + purple_notify_user_info_add_pair(user_info, _("Client Version"), + g_strdup(str)); + } else if (cv) { + purple_notify_user_info_add_pair(user_info, _("Client Version"), + g_strdup_printf("Build %d", cv)); + } + } +} + +/** Store a field of information about a buddy. */ +void +msim_store_user_info_each(const gchar *key_str, gchar *value_str, MsimUser *user) +{ + if (g_str_equal(key_str, "UserID") || g_str_equal(key_str, "ContactID")) { + /* Save to buddy list, if it exists, for quick cached uid lookup with msim_uid2username_from_blist(). */ + if (user->buddy) + { + purple_debug_info("msim", "associating uid %s with username %s\n", key_str, user->buddy->name); + purple_blist_node_set_int(&user->buddy->node, "UserID", atol(value_str)); + } + /* Need to store in MsimUser, too? What if not on blist? */ + } else if (g_str_equal(key_str, "Age")) { + user->age = atol(value_str); + } else if (g_str_equal(key_str, "Gender")) { + user->gender = g_strdup(value_str); + } else if (g_str_equal(key_str, "Location")) { + user->location = g_strdup(value_str); + } else if (g_str_equal(key_str, "TotalFriends")) { + user->total_friends = atol(value_str); + } else if (g_str_equal(key_str, "DisplayName")) { + user->display_name = g_strdup(value_str); + } else if (g_str_equal(key_str, "BandName")) { + user->band_name = g_strdup(value_str); + } else if (g_str_equal(key_str, "SongName")) { + user->song_name = g_strdup(value_str); + } else if (g_str_equal(key_str, "UserName") || g_str_equal(key_str, "IMName") || g_str_equal(key_str, "NickName")) { + /* Ignore because PurpleBuddy knows this already */ + ; + } else if (g_str_equal(key_str, "ImageURL") || g_str_equal(key_str, "AvatarURL")) { + const gchar *previous_url; + + user->image_url = g_strdup(value_str); + + /* Instead of showing 'no photo' picture, show nothing. */ + if (g_str_equal(user->image_url, "http://x.myspace.com/images/no_pic.gif")) + { + purple_buddy_icons_set_for_user(user->buddy->account, + user->buddy->name, + NULL, 0, NULL); + return; + } + + /* TODO: use ETag for checksum */ + previous_url = purple_buddy_icons_get_checksum_for_user(user->buddy); + + /* Only download if URL changed */ + if (!previous_url || !g_str_equal(previous_url, user->image_url)) { + purple_util_fetch_url(user->image_url, TRUE, NULL, TRUE, msim_downloaded_buddy_icon, (gpointer)user); + } + } else if (g_str_equal(key_str, "LastImageUpdated")) { + /* TODO: use somewhere */ + user->last_image_updated = atol(value_str); + } else if (g_str_equal(key_str, "Headline")) { + user->headline = g_strdup(value_str); + } else { + /* TODO: other fields in MsimUser */ + gchar *msg; + + msg = g_strdup_printf("msim_store_user_info_each: unknown field %s=%s", + key_str, value_str); + + msim_unrecognized(NULL, NULL, msg); + + g_free(msg); + } +} + +/** Save buddy information to the buddy list from a user info reply message. + * + * @param session + * @param msg The user information reply, with any amount of information. + * @param user The structure to save to, or NULL to save in PurpleBuddy->proto_data. + * + * Variable information is saved to the passed MsimUser structure. Permanent + * information (UserID) is stored in the blist node of the buddy list (and + * ends up in blist.xml, persisted to disk) if it exists. + * + * If the function has no buddy information, this function + * is a no-op (and returns FALSE). + * + */ +gboolean +msim_store_user_info(MsimSession *session, MsimMessage *msg, MsimUser *user) +{ + gchar *username; + MsimMessage *body, *body_node; + + g_return_val_if_fail(MSIM_SESSION_VALID(session), FALSE); + g_return_val_if_fail(msg != NULL, FALSE); + + body = msim_msg_get_dictionary(msg, "body"); + if (!body) { + return FALSE; + } + + username = msim_msg_get_string(body, "UserName"); + + if (!username) { + purple_debug_info("msim", + "msim_process_reply: not caching body, no UserName\n"); + msim_msg_free(body); + g_free(username); + return FALSE; + } + + /* Null user = find and store in PurpleBuddy's proto_data */ + if (!user) { + user = msim_find_user(session, username); + if (!user) { + msim_msg_free(body); + g_free(username); + return FALSE; + } + } + + /* TODO: make looping over MsimMessage's easier. */ + for (body_node = body; + body_node != NULL; + body_node = msim_msg_get_next_element_node(body_node)) + { + const gchar *key_str; + gchar *value_str; + MsimMessageElement *elem; + + elem = (MsimMessageElement *)body_node->data; + key_str = elem->name; + + value_str = msim_msg_get_string_from_element(elem); + msim_store_user_info_each(key_str, value_str, user); + g_free(value_str); + } + + if (msim_msg_get_integer(msg, "dsn") == MG_OWN_IM_INFO_DSN && + msim_msg_get_integer(msg, "lid") == MG_OWN_IM_INFO_LID) { + /* TODO: do something with our own IM info, if we need it for some + * specific purpose. Otherwise it is available on the buddy list, + * if the user has themselves as their own buddy. + * + * However, much of the info is already available in MsimSession, + * stored in msim_we_are_logged_on(). */ + } else if (msim_msg_get_integer(msg, "dsn") == MG_OWN_MYSPACE_INFO_DSN && + msim_msg_get_integer(msg, "lid") == MG_OWN_MYSPACE_INFO_LID) { + /* TODO: same as above, but for MySpace info. */ + } + + msim_msg_free(body); + + return TRUE; +} + +/** + * Asynchronously lookup user information, calling callback when receive result. + * + * @param session + * @param user The user id, email address, or username. Not freed. + * @param cb Callback, called with user information when available. + * @param data An arbitray data pointer passed to the callback. + */ +/* TODO: change to not use callbacks */ +void +msim_lookup_user(MsimSession *session, const gchar *user, MSIM_USER_LOOKUP_CB cb, gpointer data) +{ + MsimMessage *body; + gchar *field_name; + guint rid, cmd, dsn, lid; + + g_return_if_fail(MSIM_SESSION_VALID(session)); + g_return_if_fail(user != NULL); + /* Callback can be null to not call anything, just lookup & store information. */ + /*g_return_if_fail(cb != NULL);*/ + + purple_debug_info("msim", "msim_lookup_userid: " + "asynchronously looking up <%s>\n", user); + + msim_msg_dump("msim_lookup_user: data=%s\n", (MsimMessage *)data); + + /* Setup callback. Response will be associated with request using 'rid'. */ + rid = msim_new_reply_callback(session, cb, data); + + /* Send request */ + + cmd = MSIM_CMD_GET; + + if (msim_is_userid(user)) { + field_name = "UserID"; + dsn = MG_MYSPACE_INFO_BY_ID_DSN; + lid = MG_MYSPACE_INFO_BY_ID_LID; + } else if (msim_is_email(user)) { + field_name = "Email"; + dsn = MG_MYSPACE_INFO_BY_STRING_DSN; + lid = MG_MYSPACE_INFO_BY_STRING_LID; + } else { + field_name = "UserName"; + dsn = MG_MYSPACE_INFO_BY_STRING_DSN; + lid = MG_MYSPACE_INFO_BY_STRING_LID; + } + + body = msim_msg_new( + field_name, MSIM_TYPE_STRING, g_strdup(user), + NULL); + + g_return_if_fail(msim_send(session, + "persist", MSIM_TYPE_INTEGER, 1, + "sesskey", MSIM_TYPE_INTEGER, session->sesskey, + "cmd", MSIM_TYPE_INTEGER, 1, + "dsn", MSIM_TYPE_INTEGER, dsn, + "uid", MSIM_TYPE_INTEGER, session->userid, + "lid", MSIM_TYPE_INTEGER, lid, + "rid", MSIM_TYPE_INTEGER, rid, + "body", MSIM_TYPE_DICTIONARY, body, + NULL)); +} + + +/** + * Check if a string is a userid (all numeric). + * + * @param user The user id, email, or name. + * + * @return TRUE if is userid, FALSE if not. + */ +gboolean +msim_is_userid(const gchar *user) +{ + g_return_val_if_fail(user != NULL, FALSE); + + return strspn(user, "0123456789") == strlen(user); +} + +/** + * Check if a string is an email address (contains an @). + * + * @param user The user id, email, or name. + * + * @return TRUE if is an email, FALSE if not. + * + * This function is not intended to be used as a generic + * means of validating email addresses, but to distinguish + * between a user represented by an email address from + * other forms of identification. + */ +gboolean +msim_is_email(const gchar *user) +{ + g_return_val_if_fail(user != NULL, FALSE); + + return strchr(user, '@') != NULL; +} + + +/** Callback for when a buddy icon finished being downloaded. */ +static void +msim_downloaded_buddy_icon(PurpleUtilFetchUrlData *url_data, + gpointer user_data, + const gchar *url_text, + gsize len, + const gchar *error_message) +{ + MsimUser *user; + + user = (MsimUser *)user_data; + + purple_debug_info("msim_downloaded_buddy_icon", + "Downloaded %d bytes\n", len); + + if (!url_text) { + purple_debug_info("msim_downloaded_buddy_icon", + "failed to download icon for %s", + user->buddy->name); + return; + } + + purple_buddy_icons_set_for_user(user->buddy->account, + user->buddy->name, + g_memdup((gchar *)url_text, len), len, + /* Use URL itself as buddy icon "checksum" (TODO: ETag) */ + user->image_url); /* checksum */ +} + + diff -r 191fa8a8c0bc -r 36ba89a2b7da libpurple/protocols/myspace/user.h --- a/libpurple/protocols/myspace/user.h Mon Sep 17 03:34:11 2007 +0000 +++ b/libpurple/protocols/myspace/user.h Tue Sep 18 00:47:51 2007 +0000 @@ -1,55 +1,55 @@ -/* MySpaceIM Protocol Plugin, header file - * - * Copyright (C) 2007, Jeff Connelly - * - * 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 - */ - -#ifndef _MYSPACE_USER_H -#define _MYSPACE_USER_H - -/* Hold ephemeral information about buddies, for proto_data of PurpleBuddy. */ -/* GHashTable? */ -typedef struct _MsimUser -{ - PurpleBuddy *buddy; - guint client_cv; - gchar *client_info; - guint age; - gchar *gender; - gchar *location; - guint total_friends; - gchar *headline; - gchar *display_name; - /* Note: uid is in &buddy->node (set_blist_node_int), since it never changes */ - gchar *username; - gchar *band_name, *song_name; - gchar *image_url; - guint last_image_updated; -} MsimUser; - -/* Callback function pointer type for when a user's information is received, - * initiated from a user lookup. */ -typedef void (*MSIM_USER_LOOKUP_CB)(MsimSession *session, MsimMessage *userinfo, gpointer data); - -MsimUser *msim_get_user_from_buddy(PurpleBuddy *buddy); -MsimUser *msim_find_user(MsimSession *session, const gchar *username); -void msim_append_user_info(MsimSession *session, PurpleNotifyUserInfo *user_info, MsimUser *user, gboolean full); -gboolean msim_store_user_info(MsimSession *session, MsimMessage *msg, MsimUser *user); -gboolean msim_is_userid(const gchar *user); -gboolean msim_is_email(const gchar *user); -void msim_lookup_user(MsimSession *session, const gchar *user, MSIM_USER_LOOKUP_CB cb, gpointer data); - -#endif /* !_MYSPACE_USER_H */ +/* MySpaceIM Protocol Plugin, header file + * + * Copyright (C) 2007, Jeff Connelly + * + * 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 + */ + +#ifndef _MYSPACE_USER_H +#define _MYSPACE_USER_H + +/* Hold ephemeral information about buddies, for proto_data of PurpleBuddy. */ +/* GHashTable? */ +typedef struct _MsimUser +{ + PurpleBuddy *buddy; + guint client_cv; + gchar *client_info; + guint age; + gchar *gender; + gchar *location; + guint total_friends; + gchar *headline; + gchar *display_name; + /* Note: uid is in &buddy->node (set_blist_node_int), since it never changes */ + gchar *username; + gchar *band_name, *song_name; + gchar *image_url; + guint last_image_updated; +} MsimUser; + +/* Callback function pointer type for when a user's information is received, + * initiated from a user lookup. */ +typedef void (*MSIM_USER_LOOKUP_CB)(MsimSession *session, MsimMessage *userinfo, gpointer data); + +MsimUser *msim_get_user_from_buddy(PurpleBuddy *buddy); +MsimUser *msim_find_user(MsimSession *session, const gchar *username); +void msim_append_user_info(MsimSession *session, PurpleNotifyUserInfo *user_info, MsimUser *user, gboolean full); +gboolean msim_store_user_info(MsimSession *session, MsimMessage *msg, MsimUser *user); +gboolean msim_is_userid(const gchar *user); +gboolean msim_is_email(const gchar *user); +void msim_lookup_user(MsimSession *session, const gchar *user, MSIM_USER_LOOKUP_CB cb, gpointer data); + +#endif /* !_MYSPACE_USER_H */ diff -r 191fa8a8c0bc -r 36ba89a2b7da libpurple/protocols/myspace/zap.c --- a/libpurple/protocols/myspace/zap.c Mon Sep 17 03:34:11 2007 +0000 +++ b/libpurple/protocols/myspace/zap.c Tue Sep 18 00:47:51 2007 +0000 @@ -1,265 +1,265 @@ -/* MySpaceIM Protocol Plugin - zap support - * - * Copyright (C) 2007, Jeff Connelly - * - * 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 "zap.h" - -static gboolean msim_send_zap(MsimSession *session, const gchar *username, guint code); -static void msim_send_zap_from_menu(PurpleBlistNode *node, gpointer zap_num_ptr); - - -/** Get zap types. */ -GList * -msim_attention_types(PurpleAccount *acct) -{ - static GList *types = NULL; - MsimAttentionType* attn; - - if (!types) { -#define _MSIM_ADD_NEW_ATTENTION(icn, nme, incoming, outgoing) \ - attn = g_new0(MsimAttentionType, 1); \ - attn->icon_name = icn; \ - attn->name = nme; \ - attn->incoming_description = incoming; \ - attn->outgoing_description = outgoing; \ - types = g_list_append(types, attn); - - /* TODO: icons for each zap */ - _MSIM_ADD_NEW_ATTENTION(NULL, _("zap"), _("zapped"), _("Zapping")); - _MSIM_ADD_NEW_ATTENTION(NULL, _("whack"), _("whacked"), _("Whacking")); - _MSIM_ADD_NEW_ATTENTION(NULL, _("torch"), _("torched"), _("Torching")); - _MSIM_ADD_NEW_ATTENTION(NULL, _("smooch"), _("smooched"), _("Smooching")); - _MSIM_ADD_NEW_ATTENTION(NULL, _("hug"), _("hugged"), _("Hugging")); - _MSIM_ADD_NEW_ATTENTION(NULL, _("bslap"), _("bslapped"), _("Bslapping")); - _MSIM_ADD_NEW_ATTENTION(NULL, _("goose"), _("goosed"), _("Goosing")); - _MSIM_ADD_NEW_ATTENTION(NULL, _("hi-five"), _("hi-fived"), _("Hi-fiving")); - _MSIM_ADD_NEW_ATTENTION(NULL, _("punk"), _("punk'd"), _("Punking")); - _MSIM_ADD_NEW_ATTENTION(NULL, _("raspberry"), _("raspberried"), _("Raspberry'ing")); - } - - return types; -} - -/** Send a zap */ -gboolean -msim_send_attention(PurpleConnection *gc, const gchar *username, guint code) -{ - GList *types; - MsimSession *session; - MsimAttentionType *attn; - PurpleBuddy *buddy; - - session = (MsimSession *)gc->proto_data; - - /* Look for this attention type, by the code index given. */ - types = msim_attention_types(gc->account); - attn = (MsimAttentionType *)g_list_nth_data(types, code); - - if (!attn) { - purple_debug_info("msim_send_attention", "got invalid zap code %d\n", code); - return FALSE; - } - - buddy = purple_find_buddy(session->account, username); - if (!buddy) { - return FALSE; - } - - msim_send_zap(session, username, code); - - return TRUE; -} - -/** Send a zap to a user. */ -static gboolean -msim_send_zap(MsimSession *session, const gchar *username, guint code) -{ - gchar *zap_string; - gboolean rc; -#ifndef MSIM_USE_ATTENTION_API - GList *types; - MsimAttentionType *attn; - gchar *zap_description; -#endif - - g_return_val_if_fail(session != NULL, FALSE); - g_return_val_if_fail(username != NULL, FALSE); - - -#ifdef MSIM_USE_ATTENTION_API - /* serv_send_attention(session->gc, username, code); */ -#else - types = msim_attention_types(session->account); - - attn = g_list_nth_data(types, code); - if (!attn) { - return FALSE; - } - - - zap_description = g_strdup_printf("*** Attention: %s %s ***", attn->outgoing_description, - username); - - serv_got_im(session->gc, username, zap_description, - PURPLE_MESSAGE_SEND | PURPLE_MESSAGE_SYSTEM, time(NULL)); - - g_free(zap_description); -#endif - - /* Construct and send the actual zap command. */ - zap_string = g_strdup_printf("!!!ZAP_SEND!!!=RTE_BTN_ZAPS_%d", code); - - if (!msim_send_bm(session, username, zap_string, MSIM_BM_ACTION)) { - purple_debug_info("msim_send_zap_from_menu", "msim_send_bm failed: zapping %s with %s", - username, zap_string); - rc = FALSE; - } else { - rc = TRUE; - } - - g_free(zap_string); - - return rc; - -} - -/** Zap someone. Callback from msim_blist_node_menu zap menu. */ -static void -msim_send_zap_from_menu(PurpleBlistNode *node, gpointer zap_num_ptr) -{ - PurpleBuddy *buddy; - PurpleAccount *account; - PurpleConnection *gc; - MsimSession *session; - guint zap; - - if (!PURPLE_BLIST_NODE_IS_BUDDY(node)) { - /* Only know about buddies for now. */ - return; - } - - g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node)); - - buddy = (PurpleBuddy *)node; - - /* Find the session */ - account = buddy->account; - gc = purple_account_get_connection(account); - session = (MsimSession *)gc->proto_data; - - zap = GPOINTER_TO_INT(zap_num_ptr); - -#ifdef MSIM_USE_ATTENTION_API - serv_send_attention(session->gc, buddy->name, zap); -#else - g_return_if_fail(msim_send_zap(session, buddy->name, zap)); -#endif -} - -/** Return menu, if any, for a buddy list node. */ -GList * -msim_blist_node_menu(PurpleBlistNode *node) -{ - GList *menu, *zap_menu; - GList *types; - PurpleMenuAction *act; - /* Warning: hardcoded to match that in msim_attention_types. */ - const gchar *zap_names[10]; - guint i; - - if (!PURPLE_BLIST_NODE_IS_BUDDY(node)) { - /* Only know about buddies for now. */ - return NULL; - } - - /* Names from official client. */ - types = msim_attention_types(NULL); - i = 0; - do - { - MsimAttentionType *attn; - - attn = (MsimAttentionType *)types->data; - zap_names[i] = attn->name; - ++i; - } while ((types = g_list_next(types))); - - menu = zap_menu = NULL; - - /* TODO: get rid of once is accessible directly in GUI */ - for (i = 0; i < sizeof(zap_names) / sizeof(zap_names[0]); ++i) { - act = purple_menu_action_new(zap_names[i], PURPLE_CALLBACK(msim_send_zap_from_menu), - GUINT_TO_POINTER(i), NULL); - zap_menu = g_list_append(zap_menu, act); - } - - act = purple_menu_action_new(_("Zap"), NULL, NULL, zap_menu); - menu = g_list_append(menu, act); - - return menu; -} - -/** Process an incoming zap. */ -gboolean -msim_incoming_zap(MsimSession *session, MsimMessage *msg) -{ - gchar *msg_text, *username; - gint zap; -#ifndef MSIM_USE_ATTENTION_API - const gchar *zap_past_tense[10]; - gchar *zap_text; - - zap_past_tense[0] = _("zapped"); - zap_past_tense[1] = _("whacked"); - zap_past_tense[2] = _("torched"); - zap_past_tense[3] = _("smooched"); - zap_past_tense[4] = _("hugged"); - zap_past_tense[5] = _("bslapped"); - zap_past_tense[6] = _("goosed"); - zap_past_tense[7] = _("hi-fived"); - zap_past_tense[8] = _("punk'd"); - zap_past_tense[9] = _("raspberried"); -#endif - - msg_text = msim_msg_get_string(msg, "msg"); - username = msim_msg_get_string(msg, "_username"); - - g_return_val_if_fail(msg_text != NULL, FALSE); - g_return_val_if_fail(username != NULL, FALSE); - - g_return_val_if_fail(sscanf(msg_text, "!!!ZAP_SEND!!!=RTE_BTN_ZAPS_%d", &zap) == 1, FALSE); - - zap = CLAMP(zap, 0, 9); - -#ifdef MSIM_USE_ATTENTION_API - serv_got_attention(session->gc, username, zap); -#else - zap_text = g_strdup_printf(_("*** You have been %s! ***"), zap_past_tense[zap]); - serv_got_im(session->gc, username, zap_text, - PURPLE_MESSAGE_RECV | PURPLE_MESSAGE_SYSTEM, time(NULL)); - g_free(zap_text); -#endif - - g_free(msg_text); - g_free(username); - - return TRUE; -} - - +/* MySpaceIM Protocol Plugin - zap support + * + * Copyright (C) 2007, Jeff Connelly + * + * 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 "zap.h" + +static gboolean msim_send_zap(MsimSession *session, const gchar *username, guint code); +static void msim_send_zap_from_menu(PurpleBlistNode *node, gpointer zap_num_ptr); + + +/** Get zap types. */ +GList * +msim_attention_types(PurpleAccount *acct) +{ + static GList *types = NULL; + MsimAttentionType* attn; + + if (!types) { +#define _MSIM_ADD_NEW_ATTENTION(icn, nme, incoming, outgoing) \ + attn = g_new0(MsimAttentionType, 1); \ + attn->icon_name = icn; \ + attn->name = nme; \ + attn->incoming_description = incoming; \ + attn->outgoing_description = outgoing; \ + types = g_list_append(types, attn); + + /* TODO: icons for each zap */ + _MSIM_ADD_NEW_ATTENTION(NULL, _("zap"), _("zapped"), _("Zapping")); + _MSIM_ADD_NEW_ATTENTION(NULL, _("whack"), _("whacked"), _("Whacking")); + _MSIM_ADD_NEW_ATTENTION(NULL, _("torch"), _("torched"), _("Torching")); + _MSIM_ADD_NEW_ATTENTION(NULL, _("smooch"), _("smooched"), _("Smooching")); + _MSIM_ADD_NEW_ATTENTION(NULL, _("hug"), _("hugged"), _("Hugging")); + _MSIM_ADD_NEW_ATTENTION(NULL, _("bslap"), _("bslapped"), _("Bslapping")); + _MSIM_ADD_NEW_ATTENTION(NULL, _("goose"), _("goosed"), _("Goosing")); + _MSIM_ADD_NEW_ATTENTION(NULL, _("hi-five"), _("hi-fived"), _("Hi-fiving")); + _MSIM_ADD_NEW_ATTENTION(NULL, _("punk"), _("punk'd"), _("Punking")); + _MSIM_ADD_NEW_ATTENTION(NULL, _("raspberry"), _("raspberried"), _("Raspberry'ing")); + } + + return types; +} + +/** Send a zap */ +gboolean +msim_send_attention(PurpleConnection *gc, const gchar *username, guint code) +{ + GList *types; + MsimSession *session; + MsimAttentionType *attn; + PurpleBuddy *buddy; + + session = (MsimSession *)gc->proto_data; + + /* Look for this attention type, by the code index given. */ + types = msim_attention_types(gc->account); + attn = (MsimAttentionType *)g_list_nth_data(types, code); + + if (!attn) { + purple_debug_info("msim_send_attention", "got invalid zap code %d\n", code); + return FALSE; + } + + buddy = purple_find_buddy(session->account, username); + if (!buddy) { + return FALSE; + } + + msim_send_zap(session, username, code); + + return TRUE; +} + +/** Send a zap to a user. */ +static gboolean +msim_send_zap(MsimSession *session, const gchar *username, guint code) +{ + gchar *zap_string; + gboolean rc; +#ifndef MSIM_USE_ATTENTION_API + GList *types; + MsimAttentionType *attn; + gchar *zap_description; +#endif + + g_return_val_if_fail(session != NULL, FALSE); + g_return_val_if_fail(username != NULL, FALSE); + + +#ifdef MSIM_USE_ATTENTION_API + /* serv_send_attention(session->gc, username, code); */ +#else + types = msim_attention_types(session->account); + + attn = g_list_nth_data(types, code); + if (!attn) { + return FALSE; + } + + + zap_description = g_strdup_printf("*** Attention: %s %s ***", attn->outgoing_description, + username); + + serv_got_im(session->gc, username, zap_description, + PURPLE_MESSAGE_SEND | PURPLE_MESSAGE_SYSTEM, time(NULL)); + + g_free(zap_description); +#endif + + /* Construct and send the actual zap command. */ + zap_string = g_strdup_printf("!!!ZAP_SEND!!!=RTE_BTN_ZAPS_%d", code); + + if (!msim_send_bm(session, username, zap_string, MSIM_BM_ACTION)) { + purple_debug_info("msim_send_zap_from_menu", "msim_send_bm failed: zapping %s with %s", + username, zap_string); + rc = FALSE; + } else { + rc = TRUE; + } + + g_free(zap_string); + + return rc; + +} + +/** Zap someone. Callback from msim_blist_node_menu zap menu. */ +static void +msim_send_zap_from_menu(PurpleBlistNode *node, gpointer zap_num_ptr) +{ + PurpleBuddy *buddy; + PurpleAccount *account; + PurpleConnection *gc; + MsimSession *session; + guint zap; + + if (!PURPLE_BLIST_NODE_IS_BUDDY(node)) { + /* Only know about buddies for now. */ + return; + } + + g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node)); + + buddy = (PurpleBuddy *)node; + + /* Find the session */ + account = buddy->account; + gc = purple_account_get_connection(account); + session = (MsimSession *)gc->proto_data; + + zap = GPOINTER_TO_INT(zap_num_ptr); + +#ifdef MSIM_USE_ATTENTION_API + serv_send_attention(session->gc, buddy->name, zap); +#else + g_return_if_fail(msim_send_zap(session, buddy->name, zap)); +#endif +} + +/** Return menu, if any, for a buddy list node. */ +GList * +msim_blist_node_menu(PurpleBlistNode *node) +{ + GList *menu, *zap_menu; + GList *types; + PurpleMenuAction *act; + /* Warning: hardcoded to match that in msim_attention_types. */ + const gchar *zap_names[10]; + guint i; + + if (!PURPLE_BLIST_NODE_IS_BUDDY(node)) { + /* Only know about buddies for now. */ + return NULL; + } + + /* Names from official client. */ + types = msim_attention_types(NULL); + i = 0; + do + { + MsimAttentionType *attn; + + attn = (MsimAttentionType *)types->data; + zap_names[i] = attn->name; + ++i; + } while ((types = g_list_next(types))); + + menu = zap_menu = NULL; + + /* TODO: get rid of once is accessible directly in GUI */ + for (i = 0; i < sizeof(zap_names) / sizeof(zap_names[0]); ++i) { + act = purple_menu_action_new(zap_names[i], PURPLE_CALLBACK(msim_send_zap_from_menu), + GUINT_TO_POINTER(i), NULL); + zap_menu = g_list_append(zap_menu, act); + } + + act = purple_menu_action_new(_("Zap"), NULL, NULL, zap_menu); + menu = g_list_append(menu, act); + + return menu; +} + +/** Process an incoming zap. */ +gboolean +msim_incoming_zap(MsimSession *session, MsimMessage *msg) +{ + gchar *msg_text, *username; + gint zap; +#ifndef MSIM_USE_ATTENTION_API + const gchar *zap_past_tense[10]; + gchar *zap_text; + + zap_past_tense[0] = _("zapped"); + zap_past_tense[1] = _("whacked"); + zap_past_tense[2] = _("torched"); + zap_past_tense[3] = _("smooched"); + zap_past_tense[4] = _("hugged"); + zap_past_tense[5] = _("bslapped"); + zap_past_tense[6] = _("goosed"); + zap_past_tense[7] = _("hi-fived"); + zap_past_tense[8] = _("punk'd"); + zap_past_tense[9] = _("raspberried"); +#endif + + msg_text = msim_msg_get_string(msg, "msg"); + username = msim_msg_get_string(msg, "_username"); + + g_return_val_if_fail(msg_text != NULL, FALSE); + g_return_val_if_fail(username != NULL, FALSE); + + g_return_val_if_fail(sscanf(msg_text, "!!!ZAP_SEND!!!=RTE_BTN_ZAPS_%d", &zap) == 1, FALSE); + + zap = CLAMP(zap, 0, 9); + +#ifdef MSIM_USE_ATTENTION_API + serv_got_attention(session->gc, username, zap); +#else + zap_text = g_strdup_printf(_("*** You have been %s! ***"), zap_past_tense[zap]); + serv_got_im(session->gc, username, zap_text, + PURPLE_MESSAGE_RECV | PURPLE_MESSAGE_SYSTEM, time(NULL)); + g_free(zap_text); +#endif + + g_free(msg_text); + g_free(username); + + return TRUE; +} + + diff -r 191fa8a8c0bc -r 36ba89a2b7da libpurple/protocols/myspace/zap.h --- a/libpurple/protocols/myspace/zap.h Mon Sep 17 03:34:11 2007 +0000 +++ b/libpurple/protocols/myspace/zap.h Tue Sep 18 00:47:51 2007 +0000 @@ -1,28 +1,28 @@ -/* MySpaceIM Protocol Plugin - zap support - * - * Copyright (C) 2007, Jeff Connelly - * - * 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 - */ - -#ifndef _MYSPACE_ZAP_H -#define _MYSPACE_ZAP_H - -GList *msim_attention_types(PurpleAccount *acct); -gboolean msim_send_attention(PurpleConnection *gc, const gchar *username, guint code); -GList *msim_blist_node_menu(PurpleBlistNode *node); -gboolean msim_incoming_zap(MsimSession *session, MsimMessage *msg); - -#endif /* !_MYSPACE_ZAP_H */ +/* MySpaceIM Protocol Plugin - zap support + * + * Copyright (C) 2007, Jeff Connelly + * + * 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 + */ + +#ifndef _MYSPACE_ZAP_H +#define _MYSPACE_ZAP_H + +GList *msim_attention_types(PurpleAccount *acct); +gboolean msim_send_attention(PurpleConnection *gc, const gchar *username, guint code); +GList *msim_blist_node_menu(PurpleBlistNode *node); +gboolean msim_incoming_zap(MsimSession *session, MsimMessage *msg); + +#endif /* !_MYSPACE_ZAP_H */