Mercurial > pidgin
changeset 19450:705eca1f82e2
merge of 'e6a26aab7c177405a9cbb8d8358228ed4a53d508'
and 'e885dc139c026cbbb5d55f8509a1c63468bd80b6'
author | Sadrul Habib Chowdhury <imadil@gmail.com> |
---|---|
date | Sun, 26 Aug 2007 14:02:35 +0000 |
parents | a8c9c42c3572 (diff) c040dac83934 (current diff) |
children | 4a7e5f947b1e |
files | |
diffstat | 12 files changed, 1321 insertions(+), 1554 deletions(-) [+] |
line wrap: on
line diff
--- a/libpurple/protocols/myspace/Makefile.am Sun Aug 26 14:02:30 2007 +0000 +++ b/libpurple/protocols/myspace/Makefile.am Sun Aug 26 14:02:35 2007 +0000 @@ -2,7 +2,18 @@ pkgdir = $(libdir)/purple-$(PURPLE_MAJOR_VERSION) -SOURCES = myspace.c message.c +SOURCES = myspace.c \ + myspace.h \ + persist.h \ + message.c \ + message.h \ + zap.c \ + session.c \ + session.h \ + markup.c \ + markup.h \ + user.c \ + user.h AM_CFLAGS = $(st)
--- a/libpurple/protocols/myspace/Makefile.mingw Sun Aug 26 14:02:30 2007 +0000 +++ b/libpurple/protocols/myspace/Makefile.mingw Sun Aug 26 14:02:35 2007 +0000 @@ -37,7 +37,7 @@ ## ## SOURCES, OBJECTS ## -C_SRC = myspace.c message.c +C_SRC = myspace.c message.c zap.c session.c markup.c user.c OBJECTS = $(C_SRC:%.c=%.o)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/myspace/markup.c Sun Aug 26 14:02:35 2007 +0000 @@ -0,0 +1,689 @@ +/* MySpaceIM Protocol Plugin - markup + * + * Copyright (C) 2007, Jeff Connelly <jeff2@soc.pidgin.im> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "myspace.h" + +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 <i n=whatever>) 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 <f s=#> */ +#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 <f> (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, "<font size='%d'>", + msim_point_to_purple_size(session, msim_height_to_point(session, height))); + } else if (height && face) { + g_string_printf(gs_begin, "<font face='%s' size='%d'>", face, + msim_point_to_purple_size(session, msim_height_to_point(session, height))); + } else { + g_string_printf(gs_begin, "<font>"); + } + + /* No support for font-size CSS? */ + /* g_string_printf(gs_begin, "<span style='font-family: %s; font-size: %dpt'>", face, + msim_height_to_point(height)); */ + + gs_end = g_string_new("</font>"); + + if (decor & MSIM_TEXT_BOLD) { + g_string_append(gs_begin, "<b>"); + g_string_prepend(gs_end, "</b>"); + } + + if (decor & MSIM_TEXT_ITALIC) { + g_string_append(gs_begin, "<i>"); + g_string_append(gs_end, "</i>"); + } + + if (decor & MSIM_TEXT_UNDERLINE) { + g_string_append(gs_begin, "<u>"); + g_string_append(gs_end, "</u>"); + } + + + *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 <a> (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("<a href=\"%s\">%s", href, href); + *end = g_strdup("</a>"); +} + +/** Convert the msim markup <p> (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("<p>"); + *end = g_strdup("</p>"); +} + +/** Convert the msim markup <c> 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: <c> 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("<font color='%s'>", purple_color); + + g_free(purple_color); + + /* *begin = g_strdup_printf("<span style='color: %s'>", color); */ + *end = g_strdup("</font>"); +} + +/** Convert the msim markup <b> 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: <b> 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("<span style='background-color: %s'>", + purple_color); + g_free(purple_color); + + *end = g_strdup("</p>"); +} + +/** Convert the msim markup <i> 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: <i> 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) +{ + /* TODO: Coalesce nested tags into one <f> 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. */ + if (!purple_utf8_strcasecmp(root->name, "root")) { + *begin = g_strdup(""); + *end = g_strdup(""); + } else if (!purple_utf8_strcasecmp(root->name, "b")) { + *begin = g_strdup_printf("<f s='%d'>", MSIM_TEXT_BOLD); + *end = g_strdup("</f>"); + } else if (!purple_utf8_strcasecmp(root->name, "i")) { + *begin = g_strdup_printf("<f s='%d'>", MSIM_TEXT_ITALIC); + *end = g_strdup("</f>"); + } else if (!purple_utf8_strcasecmp(root->name, "u")) { + *begin = g_strdup_printf("<f s='%d'>", MSIM_TEXT_UNDERLINE); + *end = g_strdup("</f>"); + } 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: <a href="URL">URL</a> + * Translate to <a h='URL' /> + * Displayed as text of URL with link to URL + */ + *begin = g_strdup_printf("<a h='%s' />", href); + } else { + /* But if we get: <a href="URL">text</a> + * Translate to: text: <a h='URL' /> + * + * Because official client only supports self-closed <a> + * tags; you can't change the link text. + */ + *begin = g_strdup_printf("%s: <a h='%s' />", link_text, href); + } + } else { + *begin = g_strdup("<a />"); + } + + /* Sorry, kid. MySpace doesn't support you within <a> 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("<f f='%s' h='%d'>", face, + msim_point_to_height(session, + msim_purple_size_to_point(session, atoi(size)))); + } else if (face) { + *begin = g_strdup_printf("<f f='%s'>", face); + } else if (size) { + *begin = g_strdup_printf("<f h='%d'>", + msim_point_to_height(session, + msim_purple_size_to_point(session, atoi(size)))); + } else { + *begin = g_strdup("<f>"); + } + + *end = g_strdup("</f>"); + + /* TODO: color (bg uses <body>), emoticons */ + } else { + *begin = g_strdup_printf("[%s]", root->name); + *end = g_strdup_printf("[/%s]", root->name); + } +} + +/** 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 <f> as + * a paragraph and will display each on its own line. You actually have + * to _nest_ <f> 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("<root>", raw, "</root>", 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 <i> markup tags. + * + * @param before Original text with ASCII smileys. Will be freed. + * @return A new string with <i> 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("<i n=\"%s\"/>", name); + + purple_debug_info("msim", "msim_convert_smileys_to_markup: %s->%s\n", + symbol ? symbol : "(NULL)", + replacement ? replacement : "(NULL)"); + new = str_replace(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. + * + * @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; +} + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/myspace/markup.h Sun Aug 26 14:02:35 2007 +0000 @@ -0,0 +1,27 @@ +/* MySpaceIM Protocol Plugin - markup + * + * Copyright (C) 2007, Jeff Connelly <jeff2@soc.pidgin.im> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#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 */
--- a/libpurple/protocols/myspace/message.c Sun Aug 26 14:02:30 2007 +0000 +++ b/libpurple/protocols/myspace/message.c Sun Aug 26 14:02:35 2007 +0000 @@ -250,13 +250,21 @@ return new_list; } -/** Free a GList * of gchar * strings. */ +/** Free a GList * of MsimMessageElement *'s. */ void msim_msg_list_free(GList *l) { for (; l != NULL; l = g_list_next(l)) { - g_free((gchar *)(l->data)); + MsimMessageElement *elem; + + elem = (MsimMessageElement *)l->data; + + /* Note that name is almost never dynamically allocated elsewhere; + * it is usually a static string, but not in lists. So cast it. */ + g_free((gchar *)elem->name); + g_free(elem->data); + g_free(elem); } g_list_free(l); } @@ -275,7 +283,19 @@ /* TODO: escape/unescape /3 <-> | within list elements */ for (i = 0; array[i] != NULL; ++i) { - list = g_list_append(list, g_strdup(array[i])); + MsimMessageElement *elem; + + /* Freed in msim_msg_list_free() */ + elem = g_new0(MsimMessageElement, 1); + + /* Give the element a name for debugging purposes. + * Not supposed to be looked up by this name; instead, + * lookup the elements by indexing the array. */ + elem->name = g_strdup_printf("(list item #%d)", i); + elem->type = MSIM_TYPE_RAW; + elem->data = g_strdup(array[i]); + + list = g_list_append(list, elem); } g_strfreev(array);
--- a/libpurple/protocols/myspace/myspace.c Sun Aug 26 14:02:30 2007 +0000 +++ b/libpurple/protocols/myspace/myspace.c Sun Aug 26 14:02:35 2007 +0000 @@ -33,135 +33,37 @@ #define PURPLE_PLUGIN -#include "message.h" -#include "persist.h" #include "myspace.h" -/* Globals */ - -/* The names in in emoticon_names (for <i n=whatever>) 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 } -}; - /* Internal functions */ -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); #ifdef MSIM_DEBUG_MSG static void print_hash_item(gpointer key, gpointer value, gpointer user_data); #endif -static int msim_send_really_raw(PurpleConnection *gc, const char *buf, - int total_bytes); +static int msim_send_really_raw(PurpleConnection *gc, const char *buf, int total_bytes); static gboolean msim_login_challenge(MsimSession *session, MsimMessage *msg); -static const gchar *msim_compute_login_response( - const gchar nonce[2 * NONCE_SIZE], const gchar *email, - const gchar *password, guint *response_len); -static gboolean msim_send_bm(MsimSession *session, const gchar *who, - const gchar *text, int type); - -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_unrecognized(MsimSession *session, MsimMessage *msg, gchar *note); - -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); - -/* High-level msim markup <=> html conversion functions. */ -static gchar *msim_markup_to_html(MsimSession *, const gchar *raw); -static gchar *html_to_msim_markup(MsimSession *, const gchar *raw); - -static MsimUser *msim_get_user_from_buddy(PurpleBuddy *buddy); -static MsimUser *msim_find_user(MsimSession *session, const gchar *username); - -static gboolean msim_incoming_bm_record_cv(MsimSession *session, - MsimMessage *msg); +static const gchar *msim_compute_login_response(const gchar nonce[2 * NONCE_SIZE], const gchar *email, const gchar *password, guint *response_len); + +static gboolean msim_incoming_bm_record_cv(MsimSession *session, MsimMessage *msg); static gboolean msim_incoming_bm(MsimSession *session, MsimMessage *msg); static gboolean msim_incoming_status(MsimSession *session, MsimMessage *msg); static gboolean msim_incoming_im(MsimSession *session, MsimMessage *msg); -static gboolean msim_incoming_zap(MsimSession *session, MsimMessage *msg); +/* static gboolean msim_incoming_zap(MsimSession *session, MsimMessage *msg); - in zap.c */ static gboolean msim_incoming_action(MsimSession *session, MsimMessage *msg); static gboolean msim_incoming_media(MsimSession *session, MsimMessage *msg); static gboolean msim_incoming_unofficial_client(MsimSession *session, MsimMessage *msg); #ifdef MSIM_SEND_CLIENT_VERSION -static gboolean msim_send_unofficial_client(MsimSession *session, - gchar *username); +static gboolean msim_send_unofficial_client(MsimSession *session, gchar *username); #endif static void msim_get_info_cb(MsimSession *session, MsimMessage *userinfo, gpointer data); -static gchar *msim_format_now_playing(gchar *band, gchar *song); - -static void msim_set_status_code(MsimSession *session, guint code, - gchar *statstring); - -static void msim_append_user_info(MsimSession *session, PurpleNotifyUserInfo *user_info, MsimUser *user, gboolean full); - -static void msim_downloaded_buddy_icon(PurpleUtilFetchUrlData *url_data, gpointer user_data, const gchar *url_text, - gsize len, const gchar *error_message); - -static void msim_store_user_info_each(const gchar *key_str, gchar *value_str, MsimUser *user); -static gboolean msim_store_user_info(MsimSession *session, MsimMessage *msg, MsimUser *user); -static gboolean msim_process_server_info(MsimSession *session, - MsimMessage *msg); + +static void msim_set_status_code(MsimSession *session, guint code, gchar *statstring); + +static gboolean msim_process_server_info(MsimSession *session, MsimMessage *msg); static gboolean msim_web_challenge(MsimSession *session, MsimMessage *msg); static gboolean msim_process_reply(MsimSession *session, MsimMessage *msg); @@ -175,51 +77,22 @@ static gboolean msim_process(MsimSession *session, MsimMessage *msg); -static MsimMessage *msim_do_postprocessing(MsimMessage *msg, - const gchar *uid_field_name, const gchar *uid_before, guint uid); -static void msim_postprocess_outgoing_cb(MsimSession *session, - MsimMessage *userinfo, gpointer data); -static gboolean msim_postprocess_outgoing(MsimSession *session, - MsimMessage *msg, const gchar *username, const gchar *uid_field_name, - const gchar *uid_before); +static MsimMessage *msim_do_postprocessing(MsimMessage *msg, const gchar *uid_field_name, const gchar *uid_before, guint uid); +static void msim_postprocess_outgoing_cb(MsimSession *session, MsimMessage *userinfo, gpointer data); +static gboolean msim_postprocess_outgoing(MsimSession *session, MsimMessage *msg, const gchar *username, const gchar *uid_field_name, const gchar *uid_before); static gboolean msim_error(MsimSession *session, MsimMessage *msg); -static void msim_check_inbox_cb(MsimSession *session, MsimMessage *userinfo, - gpointer data); +static void msim_check_inbox_cb(MsimSession *session, MsimMessage *userinfo, gpointer data); static gboolean msim_check_inbox(gpointer data); -static void msim_input_cb(gpointer gc_uncasted, gint source, - PurpleInputCondition cond); - -static guint msim_new_reply_callback(MsimSession *session, - MSIM_USER_LOOKUP_CB cb, gpointer data); - -static void msim_connect_cb(gpointer data, gint source, - const gchar *error_message); - -static gboolean msim_is_userid(const gchar *user); -static gboolean msim_is_email(const gchar *user); - -static void msim_lookup_user(MsimSession *session, const gchar *user, - MSIM_USER_LOOKUP_CB cb, gpointer data); +static void msim_input_cb(gpointer gc_uncasted, gint source, PurpleInputCondition cond); + + +static void msim_connect_cb(gpointer data, gint source, const gchar *error_message); static void msim_import_friends(PurplePluginAction *action); -double msim_round(double round); - -/* 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); - } -} - /** * Load the plugin. */ @@ -283,195 +156,6 @@ return types; } -/** 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; - } - - /* TODO: make use of the MsimAttentionType we found, instead of - * doing it all over in msim_send_zap_from_menu. */ - msim_send_zap_from_menu(&buddy->node, GUINT_TO_POINTER(code)); - - return TRUE; -} - -/** Send a zap to a user. */ -static gboolean -msim_send_zap(MsimSession *session, const gchar *username, guint code) -{ - gchar *zap_string; -#ifndef MSIM_USE_ATTENTION_API - gchar *zap_description; -#endif - GList *types; - MsimAttentionType *attn; - gboolean rc; - - g_return_val_if_fail(session != NULL, FALSE); - g_return_val_if_fail(username != NULL, FALSE); - - types = msim_attention_types(session->account); - - attn = g_list_nth_data(types, code); - if (!attn) { - return FALSE; - } - - -#ifdef MSIM_USE_ATTENTION_API - serv_got_attention(session->gc, username, attn, FALSE); -#else - 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); - - g_return_if_fail(msim_send_zap(session, buddy->name, zap)); -} - - -/** 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; -} - /** * Return the icon name for a buddy and account. * @@ -911,7 +595,7 @@ * Buddy messages ('bm') include instant messages, action messages, status messages, etc. * */ -static gboolean +gboolean msim_send_bm(MsimSession *session, const gchar *who, const gchar *text, int type) { @@ -945,613 +629,6 @@ return rc; } -/* 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 */ - -/** 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 <f> (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, "<font size='%d'>", - msim_point_to_purple_size(session, msim_height_to_point(session, height))); - } else if (height && face) { - g_string_printf(gs_begin, "<font face='%s' size='%d'>", face, - msim_point_to_purple_size(session, msim_height_to_point(session, height))); - } else { - g_string_printf(gs_begin, "<font>"); - } - - /* No support for font-size CSS? */ - /* g_string_printf(gs_begin, "<span style='font-family: %s; font-size: %dpt'>", face, - msim_height_to_point(height)); */ - - gs_end = g_string_new("</font>"); - - if (decor & MSIM_TEXT_BOLD) { - g_string_append(gs_begin, "<b>"); - g_string_prepend(gs_end, "</b>"); - } - - if (decor & MSIM_TEXT_ITALIC) { - g_string_append(gs_begin, "<i>"); - g_string_append(gs_end, "</i>"); - } - - if (decor & MSIM_TEXT_UNDERLINE) { - g_string_append(gs_begin, "<u>"); - g_string_append(gs_end, "</u>"); - } - - - *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 <a> (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("<a href=\"%s\">%s", href, href); - *end = g_strdup("</a>"); -} - -/** Convert the msim markup <p> (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("<p>"); - *end = g_strdup("</p>"); -} - -/** Convert the msim markup <c> 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: <c> 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("<font color='%s'>", purple_color); - - g_free(purple_color); - - /* *begin = g_strdup_printf("<span style='color: %s'>", color); */ - *end = g_strdup("</font>"); -} - -/** Convert the msim markup <b> 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: <b> 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("<span style='background-color: %s'>", - purple_color); - g_free(purple_color); - - *end = g_strdup("</p>"); -} - -/** Convert the msim markup <i> 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: <i> 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) -{ - /* TODO: Coalesce nested tags into one <f> 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. */ - if (!purple_utf8_strcasecmp(root->name, "root")) { - *begin = g_strdup(""); - *end = g_strdup(""); - } else if (!purple_utf8_strcasecmp(root->name, "b")) { - *begin = g_strdup_printf("<f s='%d'>", MSIM_TEXT_BOLD); - *end = g_strdup("</f>"); - } else if (!purple_utf8_strcasecmp(root->name, "i")) { - *begin = g_strdup_printf("<f s='%d'>", MSIM_TEXT_ITALIC); - *end = g_strdup("</f>"); - } else if (!purple_utf8_strcasecmp(root->name, "u")) { - *begin = g_strdup_printf("<f s='%d'>", MSIM_TEXT_UNDERLINE); - *end = g_strdup("</f>"); - } 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: <a href="URL">URL</a> - * Translate to <a h='URL' /> - * Displayed as text of URL with link to URL - */ - *begin = g_strdup_printf("<a h='%s' />", href); - } else { - /* But if we get: <a href="URL">text</a> - * Translate to: text: <a h='URL' /> - * - * Because official client only supports self-closed <a> - * tags; you can't change the link text. - */ - *begin = g_strdup_printf("%s: <a h='%s' />", link_text, href); - } - } else { - *begin = g_strdup("<a />"); - } - - /* Sorry, kid. MySpace doesn't support you within <a> 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("<f f='%s' h='%d'>", face, - msim_point_to_height(session, - msim_purple_size_to_point(session, atoi(size)))); - } else if (face) { - *begin = g_strdup_printf("<f f='%s'>", face); - } else if (size) { - *begin = g_strdup_printf("<f h='%d'>", - msim_point_to_height(session, - msim_purple_size_to_point(session, atoi(size)))); - } else { - *begin = g_strdup("<f>"); - } - - *end = g_strdup("</f>"); - - /* TODO: color (bg uses <body>), emoticons */ - } else { - *begin = g_strdup_printf("[%s]", root->name); - *end = g_strdup_printf("[/%s]", root->name); - } -} - -/** 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 <f> as - * a paragraph and will display each on its own line. You actually have - * to _nest_ <f> 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("<root>", raw, "</root>", 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 <i> markup tags. - * - * @param before Original text with ASCII smileys. Will be freed. - * @return A new string with <i> 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("<i n=\"%s\"/>", name); - - purple_debug_info("msim", "msim_convert_smileys_to_markup: %s->%s\n", - symbol ? symbol : "(NULL)", - replacement ? replacement : "(NULL)"); - new = str_replace(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. */ -static 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. - * - * @return HTML markup string, must be g_free()'d. */ -static 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; -} - -/** Get the MsimUser from a PurpleBuddy, creating it if needed. */ -static 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. */ -static 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; -} - /** Record the client version in the buddy list, from an incoming message. */ static gboolean @@ -1655,7 +732,7 @@ * @param msg An MsimMessage that was unrecognized, or NULL. * @param note Information on what was unrecognized, or NULL. */ -static void +void msim_unrecognized(MsimSession *session, MsimMessage *msg, gchar *note) { /* TODO: Some more context, outwardly equivalent to a backtrace, @@ -1680,60 +757,6 @@ } } -/** Process an incoming zap. */ -static gboolean -msim_incoming_zap(MsimSession *session, MsimMessage *msg) -{ - gchar *msg_text, *username; - gint zap; - const gchar *zap_past_tense[10]; -#ifdef MSIM_USE_ATTENTION_API - MsimAttentionType attn; -#else - gchar *zap_text; -#endif - - 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"); - - 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, sizeof(zap_past_tense) / sizeof(zap_past_tense[0])); - - /* TODO:ZAP: use msim_attention_types */ -#ifdef MSIM_USE_ATTENTION_API - attn.incoming_description = zap_past_tense[zap]; - attn.outgoing_description = NULL; - attn.icon_name = NULL; /* TODO: icon */ - - serv_got_attention(session->gc, username, &attn, TRUE); -#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; -} - /** * Handle an incoming action message. * @@ -1902,95 +925,7 @@ return 0; } -/** 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; - } -} - -/** 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. - */ -static 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("<a href=\"http://myspace.com/%d\">http://myspace.com/%d</a>", - 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)); - } - } -} + /** Callback for msim_get_info(), for when user info is received. */ static void @@ -2505,6 +1440,9 @@ * some of the time, but can vary. This is our own user ID. */ session->userid = msim_msg_get_integer(msg, "userid"); + /* Save uid to account so this account can be looked up by uid. */ + purple_account_set_int(session->account, "uid", session->userid); + /* Not sure what profileid is used for. */ if (msim_msg_get_integer(msg, "profileid") != session->userid) { msim_unrecognized(session, msg, @@ -2582,10 +1520,12 @@ (GSourceFunc)msim_check_alive, session); #endif - purple_timeout_add(MSIM_MAIL_INTERVAL_CHECK, - (GSourceFunc)msim_check_inbox, session); - - msim_check_inbox(session); + if (purple_account_get_check_mail(session->account)) { + purple_timeout_add(MSIM_MAIL_INTERVAL_CHECK, + (GSourceFunc)msim_check_inbox, session); + msim_check_inbox(session); + } + return TRUE; } @@ -2626,186 +1566,6 @@ } } -/** 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 */ -} - -/** Store a field of information about a buddy. */ -static 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). - * - */ -static 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; -} - /** Process the initial server information from the server. */ static gboolean msim_process_server_info(MsimSession *session, MsimMessage *msg) @@ -2991,9 +1751,9 @@ */ list = msim_msg_get_list(msg, "msg"); - status_code = atoi(g_list_nth_data(list, MSIM_STATUS_ORDINAL_ONLINE)); + status_code = msim_msg_get_integer_from_element(g_list_nth_data(list, MSIM_STATUS_ORDINAL_ONLINE)); purple_debug_info("msim", "msim_status: %s's status code = %d\n", username, status_code); - status_headline = g_list_nth_data(list, MSIM_STATUS_ORDINAL_HEADLINE); + status_headline = msim_msg_get_string_from_element(g_list_nth_data(list, MSIM_STATUS_ORDINAL_HEADLINE)); blist = purple_get_blist(); @@ -3017,7 +1777,8 @@ purple_debug_info("msim", "msim_status: found buddy %s\n", username); } - user->headline = g_strdup(status_headline); + /* don't copy; let the MsimUser own the headline, memory-wise */ + user->headline = status_headline; /* Set user status */ switch (status_code) { @@ -3559,7 +2320,7 @@ * 1) MSIM_USER_LOOKUP_CB - make it for PERSIST_REPLY, not just user lookup * 2) data - make it an MsimMessage? */ -static guint +guint msim_new_reply_callback(MsimSession *session, MSIM_USER_LOOKUP_CB cb, gpointer data) { @@ -3607,79 +2368,7 @@ gc->inpa = purple_input_add(source, PURPLE_INPUT_READ, msim_input_cb, gc); } -/* 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); -} - + /** * Close the connection. * @@ -3713,106 +2402,6 @@ /** - * 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. - */ -static 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. - */ -static gboolean -msim_is_email(const gchar *user) -{ - g_return_val_if_fail(user != NULL, FALSE); - - return strchr(user, '@') != NULL; -} - - -/** - * 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 */ -static 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)); -} - - -/** * Obtain the status text for a buddy. * * @param buddy The buddy to obtain status text for. @@ -4395,16 +2984,74 @@ } #endif +static gboolean +msim_uri_handler(const gchar *proto, const gchar *cmd, GHashTable *params) +{ + PurpleAccount *account; + GList *l; + gchar *uid_str, *cid_str; + guint uid, cid; + + if (g_ascii_strcasecmp(proto, "myim")) + return FALSE; + + uid_str = g_hash_table_lookup(params, "uID"); + cid_str = g_hash_table_lookup(params, "cID"); + + uid = uid_str ? atol(uid_str) : 0; + cid = cid_str ? atol(cid_str) : 0; + + /* Find our account with specified user id, or use first connected account if uid=0. */ + account = NULL; + l = purple_accounts_get_all(); + while (l) { + if (purple_account_is_connected(l->data) && + (uid == 0 || purple_account_get_int(l->data, "uid", 0) == uid)) { + account = l->data; + break; + } + l = l->next; + } + + if (!account) { + purple_notify_error(NULL, _("myim URL handler"), + _("No suitable MySpaceIM account could be found to open this myim URL."), + _("Enable the proper MySpaceIM account and try again.")); + return FALSE; + } + + /* myim:sendIM?uID=USERID&cID=CONTACTID */ + if (!g_ascii_strcasecmp(cmd, "sendIM")) { + PurpleConversation *conv; + + conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, cid_str, account); + if (!conv) + conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, cid_str); + purple_conversation_present(conv); + + /* TODO: where to get the message? or is there any? */ + purple_conv_send_confirm(conv, "test"); + + /* myim:addContact?uID=USERID&cID=CONTACTID */ + } else if (!g_ascii_strcasecmp(cmd, "addContact")) { + purple_blist_request_add_buddy(account, cid_str, _("Buddies"), NULL); + } + + return FALSE; +} + /** Initialize plugin. */ void init_plugin(PurplePlugin *plugin) { - PurpleAccountOption *option; #ifdef MSIM_SELF_TEST msim_test_all(); exit(0); #endif /* MSIM_SELF_TEST */ + PurpleAccountOption *option; + static gboolean initialized = FALSE; + /* TODO: default to automatically try different ports. Make the user be * able to set the first port to try (like LastConnectedPort in Windows client). */ @@ -4435,21 +3082,14 @@ prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); #endif - /* TODO: /zap command. Problem with this is that there are different kinds of zaps, - * and the selection is best made available in a drop-down menu, instead of forcing - * the user to type the kind of zap and memorizing available zaps (or putting it in the - * help menu). A new "attention" API, for zap/buzz/nudge (different protocols) will - * solve this. */ -#if 0 - purple_cmd_register("zap", /* cmd */ - "w", /* args - accept a single word */ - PURPLE_CMD_P_PRPL, /* priority */ - PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_PRPL_ONLY, /* flags */ - "prpl-myspace", /* prpl_id */ - msim_cmd_zap, /* func */ - _("zap: zap a user to get their attention"), /* helpstr */ - NULL); /* data */ -#endif + /* Code below only runs once. Based on oscar.c's oscar_init(). */ + if (initialized) + return; + + initialized = TRUE; + + purple_signal_connect(purple_get_core(), "uri-handler", &initialized, + PURPLE_CALLBACK(msim_uri_handler), NULL); } PURPLE_INIT_PLUGIN(myspace, init_plugin, info);
--- a/libpurple/protocols/myspace/myspace.h Sun Aug 26 14:02:30 2007 +0000 +++ b/libpurple/protocols/myspace/myspace.h Sun Aug 26 14:02:35 2007 +0000 @@ -46,9 +46,15 @@ #include "util.h" /* for base64 */ #include "debug.h" /* for purple_debug_info */ #include "xmlnode.h" +#include "core.h" /* MySpaceIM includes */ +#include "persist.h" #include "message.h" +#include "session.h" +#include "zap.h" +#include "markup.h" +#include "user.h" /* Conditional compilation options */ /* Send third-party client version? (Recognized by us and Miranda's plugin) */ @@ -157,23 +163,6 @@ #define MSIM_STATUS_CODE_IDLE 2 #define MSIM_STATUS_CODE_AWAY 5 -/* Text formatting bits for <f s=#> */ -#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 - - -/* Random number in every MsimSession, to ensure it is valid. */ -#define MSIM_SESSION_STRUCT_MAGIC 0xe4a6752b /* Inbox status bitfield values for MsimSession.inbox_status */ #define MSIM_INBOX_MAIL (1 << 0) @@ -182,54 +171,6 @@ #define MSIM_INBOX_FRIEND_REQUEST (1 << 3) #define MSIM_INBOX_PICTURE_COMMENT (1 << 4) -/* 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) - -/* 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; - - #ifdef MSIM_USE_ATTENTION_API #define MsimAttentionType PurpleAttentionType #else @@ -249,31 +190,17 @@ gchar *str_replace(const gchar *str, const gchar *old, const gchar *new); -/* 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); - /* Functions */ gboolean msim_load(PurplePlugin *plugin); GList *msim_status_types(PurpleAccount *acct); -GList *msim_attention_types(PurpleAccount *acct); -gboolean msim_send_attention(PurpleConnection *gc, const gchar *username, guint code); - -GList *msim_blist_node_menu(PurpleBlistNode *node); - const gchar *msim_list_icon(PurpleAccount *acct, PurpleBuddy *buddy); - gboolean msim_send_raw(MsimSession *session, const gchar *msg); void msim_login(PurpleAccount *acct); - -int msim_send_im(PurpleConnection *gc, const gchar *who, const gchar *message, -PurpleMessageFlags flags); +int msim_send_im(PurpleConnection *gc, const gchar *who, const gchar *message, PurpleMessageFlags flags); +unsigned int msim_send_typing(PurpleConnection *gc, const gchar *name, PurpleTypingState state); -typedef void (*MSIM_XMLNODE_CONVERT)(MsimSession *, xmlnode *, gchar **, gchar **); - -unsigned int msim_send_typing(PurpleConnection *gc, const gchar *name, PurpleTypingState state); void msim_get_info(PurpleConnection *gc, const gchar *name); void msim_set_status(PurpleAccount *account, PurpleStatus *status); @@ -284,9 +211,6 @@ gboolean msim_offline_message(const PurpleBuddy *buddy); -MsimSession *msim_session_new(PurpleAccount *acct); -void msim_session_destroy(MsimSession *session); - void msim_close(PurpleConnection *gc); char *msim_status_text(PurpleBuddy *buddy); @@ -299,6 +223,13 @@ int msim_test_escaping(void); #endif +gboolean msim_send_bm(MsimSession *session, const gchar *who, const gchar *text, int type); + + +void msim_unrecognized(MsimSession *session, MsimMessage *msg, gchar *note); +guint msim_new_reply_callback(MsimSession *session, MSIM_USER_LOOKUP_CB cb, gpointer data); + + void init_plugin(PurplePlugin *plugin); #endif /* !_MYSPACE_MYSPACE_H */
--- a/libpurple/protocols/myspace/persist.h Sun Aug 26 14:02:30 2007 +0000 +++ b/libpurple/protocols/myspace/persist.h Sun Aug 26 14:02:35 2007 +0000 @@ -42,8 +42,8 @@ /** Define a set of _DSN and _LID constants for a persistance request. */ #define MSIM_PERSIST_DSN_LID(name,dsn,lid) \ - const int name##_DSN = dsn; \ - const int name##_LID = lid; + static const int name##_DSN = dsn; \ + static const int name##_LID = lid; /* Can't do this, errors: * persist.h:51:3: error: '#' is not followed by a macro parameter
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/myspace/session.c Sun Aug 26 14:02:35 2007 +0000 @@ -0,0 +1,95 @@ +/* MySpaceIM Protocol Plugin, session + * + * Copyright (C) 2007, Jeff Connelly <jeff2@soc.pidgin.im> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "myspace.h" + +/* 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); +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/myspace/session.h Sun Aug 26 14:02:35 2007 +0000 @@ -0,0 +1,57 @@ +/* MySpaceIM Protocol Plugin, session + * + * Copyright (C) 2007, Jeff Connelly <jeff2@soc.pidgin.im> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#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 */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/myspace/zap.c Sun Aug 26 14:02:35 2007 +0000 @@ -0,0 +1,269 @@ +/* MySpaceIM Protocol Plugin - zap support + * + * Copyright (C) 2007, Jeff Connelly <jeff2@soc.pidgin.im> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "myspace.h" +#include "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; + } + + /* TODO: make use of the MsimAttentionType we found, instead of + * doing it all over in msim_send_zap_from_menu. */ + msim_send_zap_from_menu(&buddy->node, GUINT_TO_POINTER(code)); + + return TRUE; +} + +/** Send a zap to a user. */ +static gboolean +msim_send_zap(MsimSession *session, const gchar *username, guint code) +{ + gchar *zap_string; +#ifndef MSIM_USE_ATTENTION_API + gchar *zap_description; +#endif + GList *types; + MsimAttentionType *attn; + gboolean rc; + + g_return_val_if_fail(session != NULL, FALSE); + g_return_val_if_fail(username != NULL, FALSE); + + types = msim_attention_types(session->account); + + attn = g_list_nth_data(types, code); + if (!attn) { + return FALSE; + } + + +#ifdef MSIM_USE_ATTENTION_API + serv_got_attention(session->gc, username, attn, FALSE); +#else + 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); + + g_return_if_fail(msim_send_zap(session, buddy->name, zap)); +} + +/** 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; + const gchar *zap_past_tense[10]; +#ifdef MSIM_USE_ATTENTION_API + MsimAttentionType attn; +#else + gchar *zap_text; +#endif + + 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"); + + 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, sizeof(zap_past_tense) / sizeof(zap_past_tense[0])); + + /* TODO:ZAP: use msim_attention_types */ +#ifdef MSIM_USE_ATTENTION_API + attn.incoming_description = zap_past_tense[zap]; + attn.outgoing_description = NULL; + attn.icon_name = NULL; /* TODO: icon */ + + serv_got_attention(session->gc, username, &attn, TRUE); +#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; +} + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/myspace/zap.h Sun Aug 26 14:02:35 2007 +0000 @@ -0,0 +1,28 @@ +/* MySpaceIM Protocol Plugin - zap support + * + * Copyright (C) 2007, Jeff Connelly <jeff2@soc.pidgin.im> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#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 */