diff options
author | Robert Vehse <robertvehse@fastmail.fm> | 2016-10-04 22:57:40 +0200 |
---|---|---|
committer | Robert Vehse <robertvehse@fastmail.fm> | 2016-10-04 22:57:40 +0200 |
commit | 59828aab27107feccf227ae16fd2cd64e40d3403 (patch) | |
tree | 91d651b4b0d982a4dc1b642a8730fb02b42c4ca4 | |
parent | 6f1eed99dfb9cf7b6325da43b8727b4b8a4e32b4 (diff) | |
download | pidgin-59828aab27107feccf227ae16fd2cd64e40d3403.tar.gz |
Remove the MySpaceIM protocol plugin. The service has been defunct for a long time. References #15356.
32 files changed, 10 insertions, 7884 deletions
@@ -1,5 +1,9 @@ Pidgin and Finch: The Pimpin' Penguin IM Clients That're Good for the Soul +version 2.12.0 (06/21/16): + General: + * Remove the MySpaceIM protocol plugin. The service has been defunct for a long time. (#15356) + version 2.11.0 (06/21/16): General: * 2.10.12 was accidentally released with new additions to the API and diff --git a/configure.ac b/configure.ac index 528ac55ca5..d97920224f 100644 --- a/configure.ac +++ b/configure.ac @@ -1189,7 +1189,7 @@ if test "x$STATIC_PRPLS" != "x" -a "x$DYNAMIC_PRPLS" = "xall"; then fi if test "x$STATIC_PRPLS" = "xall" ; then - STATIC_PRPLS="bonjour gg irc jabber myspace novell oscar sametime silc simple yahoo zephyr" + STATIC_PRPLS="bonjour gg irc jabber novell oscar sametime silc simple yahoo zephyr" fi if test "x$have_meanwhile" != "xyes" ; then STATIC_PRPLS=`echo $STATIC_PRPLS | $sedpath 's/sametime//'` @@ -1238,7 +1238,6 @@ for i in $STATIC_PRPLS ; do gg) static_gg=yes ;; irc) static_irc=yes ;; jabber) static_jabber=yes ;; - myspace) static_myspace=yes ;; novell) static_novell=yes ;; oscar) static_oscar=yes ;; aim) static_oscar=yes ;; @@ -1256,7 +1255,6 @@ AM_CONDITIONAL(STATIC_BONJOUR, test "x$static_bonjour" = "xyes") AM_CONDITIONAL(STATIC_GG, test "x$static_gg" = "xyes") AM_CONDITIONAL(STATIC_IRC, test "x$static_irc" = "xyes") AM_CONDITIONAL(STATIC_JABBER, test "x$static_jabber" = "xyes") -AM_CONDITIONAL(STATIC_MYSPACE, test "x$static_myspace" = "xyes") AM_CONDITIONAL(STATIC_NOVELL, test "x$static_novell" = "xyes") AM_CONDITIONAL(STATIC_OSCAR, test "x$static_oscar" = "xyes") AM_CONDITIONAL(STATIC_SAMETIME, test "x$static_sametime" = "xyes" -a "x$have_meanwhile" = "xyes") @@ -1270,7 +1268,7 @@ AC_DEFINE_UNQUOTED(STATIC_PROTO_INIT, $extern_init static void static_proto_init AC_ARG_WITH(dynamic_prpls, [AC_HELP_STRING([--with-dynamic-prpls], [specify which protocols to build dynamically])], [DYNAMIC_PRPLS=`echo $withval | $sedpath 's/,/ /g'`]) if test "x$DYNAMIC_PRPLS" = "xall" ; then - DYNAMIC_PRPLS="bonjour gg irc jabber myspace novell oscar sametime silc simple yahoo zephyr" + DYNAMIC_PRPLS="bonjour gg irc jabber novell oscar sametime silc simple yahoo zephyr" fi if test "x$have_meanwhile" != "xyes"; then DYNAMIC_PRPLS=`echo $DYNAMIC_PRPLS | $sedpath 's/sametime//'` @@ -1291,7 +1289,6 @@ for i in $DYNAMIC_PRPLS ; do gg) dynamic_gg=yes ;; irc) dynamic_irc=yes ;; jabber) dynamic_jabber=yes ;; - myspace) dynamic_myspace=yes ;; novell) dynamic_novell=yes ;; null) dynamic_null=yes ;; oscar) dynamic_oscar=yes ;; @@ -2656,7 +2653,6 @@ AC_CONFIG_FILES([Makefile libpurple/protocols/gg/Makefile libpurple/protocols/irc/Makefile libpurple/protocols/jabber/Makefile - libpurple/protocols/myspace/Makefile libpurple/protocols/novell/Makefile libpurple/protocols/null/Makefile libpurple/protocols/oscar/Makefile diff --git a/libpurple/protocols/Makefile.am b/libpurple/protocols/Makefile.am index 675ee5818f..db1a8b4fa7 100644 --- a/libpurple/protocols/Makefile.am +++ b/libpurple/protocols/Makefile.am @@ -1,5 +1,5 @@ EXTRA_DIST = Makefile.mingw -DIST_SUBDIRS = bonjour gg irc jabber myspace novell null oscar sametime silc silc10 simple yahoo zephyr +DIST_SUBDIRS = bonjour gg irc jabber novell null oscar sametime silc silc10 simple yahoo zephyr SUBDIRS = $(DYNAMIC_PRPLS) $(STATIC_PRPLS) diff --git a/libpurple/protocols/Makefile.mingw b/libpurple/protocols/Makefile.mingw index 36000fc18b..323ea9ae9f 100644 --- a/libpurple/protocols/Makefile.mingw +++ b/libpurple/protocols/Makefile.mingw @@ -8,7 +8,7 @@ PIDGIN_TREE_TOP := ../.. include $(PIDGIN_TREE_TOP)/libpurple/win32/global.mak -SUBDIRS = gg irc jabber novell null oscar sametime silc simple yahoo bonjour myspace +SUBDIRS = gg irc jabber novell null oscar sametime silc simple yahoo bonjour .PHONY: all install clean diff --git a/libpurple/protocols/myspace/Makefile.am b/libpurple/protocols/myspace/Makefile.am deleted file mode 100644 index 715cabc875..0000000000 --- a/libpurple/protocols/myspace/Makefile.am +++ /dev/null @@ -1,44 +0,0 @@ -EXTRA_DIST = \ - Makefile.mingw - -pkgdir = $(libdir)/purple-$(PURPLE_MAJOR_VERSION) - -MSIMSOURCES = markup.c \ - markup.h \ - message.c \ - message.h \ - myspace.c \ - myspace.h \ - persist.h \ - session.c \ - session.h \ - user.c \ - user.h \ - zap.c \ - zap.h - -AM_CFLAGS = $(st) - -libmyspace_la_LDFLAGS = -module -avoid-version - -if STATIC_MYSPACE - -st = -DPURPLE_STATIC_PRPL -noinst_LTLIBRARIES = libmyspace.la -libmyspace_la_SOURCES = $(MSIMSOURCES) -libmyspace_la_CFLAGS = $(AM_CFLAGS) - -else - -st = -pkg_LTLIBRARIES = libmyspace.la -libmyspace_la_SOURCES = $(MSIMSOURCES) -libmyspace_la_LIBADD = $(GLIB_LIBS) - -endif - -AM_CPPFLAGS = \ - -I$(top_srcdir)/libpurple \ - -I$(top_builddir)/libpurple \ - $(GLIB_CFLAGS) \ - $(DEBUG_CFLAGS) diff --git a/libpurple/protocols/myspace/Makefile.mingw b/libpurple/protocols/myspace/Makefile.mingw deleted file mode 100644 index 0fc7534760..0000000000 --- a/libpurple/protocols/myspace/Makefile.mingw +++ /dev/null @@ -1,81 +0,0 @@ -# -# Makefile.mingw -# -# Description: Makefile for win32 (mingw) version of libmyspace -# - -PIDGIN_TREE_TOP := ../../.. -include $(PIDGIN_TREE_TOP)/libpurple/win32/global.mak - -TARGET = libmyspace -TYPE = PLUGIN - -# Static or Plugin... -ifeq ($(TYPE),STATIC) - DEFINES += -DSTATIC - DLL_INSTALL_DIR = $(PURPLE_INSTALL_DIR) -else -ifeq ($(TYPE),PLUGIN) - DLL_INSTALL_DIR = $(PURPLE_INSTALL_PLUGINS_DIR) -endif -endif - -## -## INCLUDE PATHS -## -INCLUDE_PATHS += -I. \ - -I$(GTK_TOP)/include \ - -I$(GTK_TOP)/include/glib-2.0 \ - -I$(GTK_TOP)/lib/glib-2.0/include \ - -I$(PURPLE_TOP) \ - -I$(PURPLE_TOP)/win32 \ - -I$(PIDGIN_TREE_TOP) - -LIB_PATHS = -L$(GTK_TOP)/lib \ - -L$(PURPLE_TOP) - -## -## SOURCES, OBJECTS -## -C_SRC = myspace.c message.c zap.c session.c markup.c user.c - -OBJECTS = $(C_SRC:%.c=%.o) - -## -## LIBRARIES -## -LIBS = \ - -lglib-2.0 \ - -lws2_32 \ - -lintl \ - -lpurple - -include $(PIDGIN_COMMON_RULES) - -## -## TARGET DEFINITIONS -## -.PHONY: all install clean - -all: $(TARGET).dll - -install: all $(DLL_INSTALL_DIR) - cp $(TARGET).dll $(DLL_INSTALL_DIR) - -$(OBJECTS): $(PURPLE_CONFIG_H) - -## -## BUILD DLL -## -$(TARGET).dll: $(PURPLE_DLL).a $(OBJECTS) - $(CC) -shared $(OBJECTS) $(LIB_PATHS) $(LIBS) $(DLL_LD_FLAGS) -o $(TARGET).dll - -## -## CLEAN RULES -## - -clean: - rm -f $(OBJECTS) - rm -f $(TARGET).dll - -include $(PIDGIN_COMMON_TARGETS) diff --git a/libpurple/protocols/myspace/README b/libpurple/protocols/myspace/README deleted file mode 100644 index f9f062c9e6..0000000000 --- a/libpurple/protocols/myspace/README +++ /dev/null @@ -1,27 +0,0 @@ -MySpaceIM Protocol Plugin for libpurple by Jeff Connelly 2007-08-07 - -Greetings. This package contains a plugin for libpurple (as used in -Pidgin, formerly Gaim) to connect to the new MySpaceIM instant messaging -network and send/receive messages. Functionality is only basic as of yet, -and this code should be considered alpha quality. - -This code was initially developed under Google Summer of Code 2007. - -For features and TODO, see http://developer.pidgin.im/wiki/MySpaceIM - -Usage: - -Login using your _email address_ you use to login to myspace.com. You can't -login using your numeric ID or alias. - -To test it out, send a message to yourself (by your username or numeric -uid (email not yet supported)) or tom (6221). In either case you should -get a reply. You should also be able to talk to other MySpaceIM users if -you desire. Replies will always be shown as coming from a user's username, -even if you IM by email or userid. - -Feedback welcome. You can IM my test account at "msimprpl" if you feel like it. - -Enjoy, --Jeff Connelly -msimprpl@xyzzy.cjb.net diff --git a/libpurple/protocols/myspace/markup.c b/libpurple/protocols/myspace/markup.c deleted file mode 100644 index 05d473e130..0000000000 --- a/libpurple/protocols/myspace/markup.c +++ /dev/null @@ -1,763 +0,0 @@ -/* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ - -#include "myspace.h" - -typedef int (*MSIM_XMLNODE_CONVERT)(MsimSession *, xmlnode *, gchar **, gchar **); - -/* 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 }; - -/* Purple maximum font size. Equivalent to sizeof(_font_scale) / sizeof(_font_scale[0]) */ -#define MAX_FONT_SIZE 7 - -#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 - */ -static 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; - - base = purple_account_get_int(session->account, "base_font_size", MSIM_BASE_FONT_POINT_SIZE); - - for (size = 0; size < MAX_FONT_SIZE; ++size) { - this_point = (guint)msim_round(base * _font_scale[size]); - - 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, "dpi", 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, "dpi", 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"); - - /* Validate the font face, to avoid constructing invalid HTML later */ - if (face != NULL && strchr(face, '\'') != NULL) - face = NULL; - - height = height_str != NULL ? atol(height_str) : 12; - decor = decor_str != NULL ? atol(decor_str) : 0; - - /* - * The HTML we're constructing here is a bit redudant. Ideally we - * would use only the font-family and font-size CSS span, but Pidgin - * doesn't support it (it's included for other UIs). For Pidgin we - * wrap the whole thing in an ugly font tag, and Pidgin will happily - * ignore the <span>. - */ - gs_begin = g_string_new(""); - if (height && !face) { - guint point_size = msim_height_to_point(session, height); - g_string_printf(gs_begin, - "<font size='%d'><span style='font-size: %dpt'>", - msim_point_to_purple_size(session, point_size), - point_size); - } else if (height && face) { - guint point_size = msim_height_to_point(session, height); - g_string_printf(gs_begin, - "<font face='%s' size='%d'><span style='font-family: %s; font-size: %dpt'>", - face, msim_point_to_purple_size(session, point_size), - face, point_size); - } else { - g_string_printf(gs_begin, "<font><span>"); - } - - gs_end = g_string_new("</span></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 = g_string_free(gs_begin, FALSE); - *end = g_string_free(gs_end, FALSE); -} - -/** - * 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. - */ -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\n"); - *begin = g_strdup(""); - *end = g_strdup(""); - /* TODO: log as unrecognized */ - return; - } - - purple_color = msim_color_to_purple(color); - -#ifdef USE_CSS_FORMATTING - *begin = g_strdup_printf("<span style='color: %s'>", purple_color); - *end = g_strdup("</span>"); -#else - *begin = g_strdup_printf("<font color='%s'>", purple_color); - *end = g_strdup("</font>"); -#endif - - g_free(purple_color); -} - -/** - * Convert the msim markup <b> tag (background color) into HTML. - */ -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\n"); - /* TODO: log as unrecognized. */ - return; - } - - purple_color = msim_color_to_purple(color); - -#ifdef USE_CSS_FORMATTING - *begin = g_strdup_printf("<span style='background-color: %s'>", purple_color); - *end = g_strdup("</span>"); -#else - *begin = g_strdup_printf("<body bgcolor='%s'>", purple_color); - *end = g_strdup("</body>"); -#endif - - g_free(purple_color); -} - -/** - * 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\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 int -msim_markup_tag_to_html(MsimSession *session, xmlnode *root, gchar **begin, - gchar **end) -{ - g_return_val_if_fail(root != NULL, 0); - - 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\n", - root->name ? root->name : "(NULL)"); - *begin = g_strdup(""); - *end = g_strdup(""); - } - return 0; -} - -/** - * Convert an individual HTML tag to msim markup. - */ -static int -html_tag_to_msim_markup(MsimSession *session, xmlnode *root, gchar **begin, - gchar **end) -{ - int ret = 0; - - if (!purple_utf8_strcasecmp(root->name, "root") || - !purple_utf8_strcasecmp(root->name, "html")) { - *begin = g_strdup(""); - *end = g_strdup(""); - /* TODO: Coalesce nested tags into one <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. */ - } else if (!purple_utf8_strcasecmp(root->name, "b")) { - if (root->child->type == XMLNODE_TYPE_DATA) { - *begin = g_strdup_printf("<f s='%d'>", MSIM_TEXT_BOLD); - *end = g_strdup("</f>"); - } else { - if (!purple_utf8_strcasecmp(root->child->name,"i")) { - ret++; - if (root->child->child->type == XMLNODE_TYPE_DATA) { - *begin = g_strdup_printf("<f s='%d'>", (MSIM_TEXT_BOLD + MSIM_TEXT_ITALIC)); - *end = g_strdup("</f>"); - } else { - if (!purple_utf8_strcasecmp(root->child->child->name,"u")) { - ret++; - *begin = g_strdup_printf("<f s='%d'>", (MSIM_TEXT_BOLD + MSIM_TEXT_ITALIC + MSIM_TEXT_UNDERLINE)); - *end = g_strdup("</f>"); - } - } - } else if (!purple_utf8_strcasecmp(root->child->name,"u")) { - ret++; - *begin = g_strdup_printf("<f s='%d'>", (MSIM_TEXT_BOLD + MSIM_TEXT_UNDERLINE)); - *end = g_strdup("</f>"); - } - } - } else if (!purple_utf8_strcasecmp(root->name, "i")) { - if (root->child->type == XMLNODE_TYPE_DATA) { - *begin = g_strdup_printf("<f s='%d'>", MSIM_TEXT_ITALIC); - *end = g_strdup("</f>"); - } else { - if (!purple_utf8_strcasecmp(root->child->name,"u")) { - ret++; - *begin = g_strdup_printf("<f s='%d'>", (MSIM_TEXT_ITALIC + MSIM_TEXT_UNDERLINE)); - *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; - gchar *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); - g_free(link_text); - root->child = NULL; - - *end = g_strdup(""); - } else if (!purple_utf8_strcasecmp(root->name, "font")) { - GString *tmpbegin, *tmpend; - const gchar *size; - const gchar *face; - const gchar *color; - - size = xmlnode_get_attrib(root, "size"); - face = xmlnode_get_attrib(root, "face"); - color = xmlnode_get_attrib(root, "color"); - - tmpbegin = g_string_new("<f"); - tmpend = g_string_new("</f>"); - - if (face != NULL) - g_string_append_printf(tmpbegin, " f='%s'", face); - - if (size != NULL) - g_string_append_printf(tmpbegin, " h='%d'", - msim_point_to_height(session, - msim_purple_size_to_point(session, atoi(size)))); - - /* Close the <f> tag */ - g_string_append(tmpbegin, ">"); - - if (color != NULL) { - g_string_append_printf(tmpbegin, "<c v='%s'>", color); - g_string_prepend(tmpend, "</c>"); - } - - *begin = g_string_free(tmpbegin, FALSE); - *end = g_string_free(tmpend, FALSE); - - } else if (!purple_utf8_strcasecmp(root->name, "body")) { - const gchar *bgcolor; - - bgcolor = xmlnode_get_attrib(root, "bgcolor"); - - if (bgcolor != NULL) { - *begin = g_strdup_printf("<b v='%s'>", bgcolor); - *end = g_strdup("</b>"); - } - - } else { - gchar *err; - -#ifdef MSIM_MARKUP_SHOW_UNKNOWN_TAGS - *begin = g_strdup_printf("[%s]", root->name); - *end = g_strdup_printf("[/%s]", root->name); -#else - *begin = g_strdup(""); - *end = g_strdup(""); -#endif - - err = g_strdup_printf("html_tag_to_msim_markup: unrecognized " - "HTML tag %s was sent by the IM client; ignoring", - root->name ? root->name : "(NULL)"); - msim_unrecognized(NULL, NULL, err); - g_free(err); - } - return ret; -} - -/** - * 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 void -msim_convert_xmlnode(MsimSession *session, GString *out, xmlnode *root, MSIM_XMLNODE_CONVERT f, int nodes_processed) -{ - xmlnode *node; - gchar *begin, *end, *tmp; - int descended = nodes_processed; - - if (!root || !root->name) - return; - - purple_debug_info("msim", "msim_convert_xmlnode: got root=%s\n", - root->name); - - begin = end = NULL; - - if (descended == 0) /* We've not formatted this yet.. :) */ - descended = f(session, root, &begin, &end); /* Get the value that our format function has already descended for us */ - - g_string_append(out, begin); - g_free(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. */ - msim_convert_xmlnode(session, out, node, f, descended); - - purple_debug_info("msim", " ** node name=%s\n", - node->name ? node->name : "(NULL)"); - break; - - case XMLNODE_TYPE_DATA: - /* Literal text. */ - /* - * TODO: Why is it necessary to escape here? I thought - * node->data was already escaped? - */ - tmp = g_markup_escape_text(node->data, node->data_sz); - g_string_append(out, tmp); - g_free(tmp); - break; - - default: - purple_debug_warning("msim", - "msim_convert_xmlnode: unknown node type\n"); - } - } - - /* 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(out, end); - g_free(end); -} - -/** - * 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; - GString *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_warning("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 = g_string_new(NULL); - msim_convert_xmlnode(session, str, root, f, 0); - xmlnode_free(root); - - purple_debug_info("msim", "msim_markup_to_html: returning %s\n", str->str); - - return g_string_free(str, FALSE); -} - -/** - * 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 = purple_strreplace(old, symbol, replacement); - - g_free(replacement); - g_free(old); - - old = new; - } - - return new; -} - -/** - * High-level function to convert MySpaceIM markup to Purple (HTML) markup. - * - * @return Purple markup string, must be g_free()'d. */ -gchar * -msim_markup_to_html(MsimSession *session, const gchar *raw) -{ - return msim_convert_xml(session, raw, msim_markup_tag_to_html); -} - -/** - * High-level function to convert Purple (HTML) to MySpaceIM markup. - * - * TODO: consider using purple_markup_html_to_xhtml() to make valid XML. - * - * @return HTML markup string, must be g_free()'d. */ -gchar * -html_to_msim_markup(MsimSession *session, const gchar *raw) -{ - gchar *markup; - - markup = msim_convert_xml(session, raw, html_tag_to_msim_markup); - - if (purple_account_get_bool(session->account, "emoticons", TRUE)) { - /* Frees markup and allocates a new one. */ - markup = msim_convert_smileys_to_markup(markup); - } - - return markup; -} diff --git a/libpurple/protocols/myspace/markup.h b/libpurple/protocols/myspace/markup.h deleted file mode 100644 index dfbbebad1b..0000000000 --- a/libpurple/protocols/myspace/markup.h +++ /dev/null @@ -1,27 +0,0 @@ -/* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ - -#ifndef _MYSPACE_MARKUP_H -#define _MYSPACE_MARKUP_H - -/* High-level msim markup <=> Purple html conversion functions. */ -gchar *msim_markup_to_html(MsimSession *, const gchar *raw); -gchar *html_to_msim_markup(MsimSession *, const gchar *raw); - -#endif /* !_MYSPACE_MARKUP_H */ diff --git a/libpurple/protocols/myspace/message.c b/libpurple/protocols/myspace/message.c deleted file mode 100644 index 9eb44b3ade..0000000000 --- a/libpurple/protocols/myspace/message.c +++ /dev/null @@ -1,1413 +0,0 @@ -/** MySpaceIM protocol messages - * - * \author Jeff Connelly - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ - -#include "myspace.h" -#include "message.h" - -static void msim_msg_debug_string_element(gpointer data, gpointer user_data); - -/** - * Escape codes and associated replacement text, used for protocol message - * escaping and unescaping. - */ -static struct MSIM_ESCAPE_REPLACEMENT { - gchar *code; - gchar text; -} msim_escape_replacements[] = { - { "/1", '/' }, - { "/2", '\\' }, - /* { "/3", "|" }, */ /* Not used here -- only for within arrays */ - { NULL, 0 } -}; - -/** - * Escape a protocol message. - * - * @return The escaped message. Caller must g_free(). - */ -gchar * -msim_escape(const gchar *msg) -{ - GString *gs; - guint i, j; - guint msg_len; - - gs = g_string_new(""); - msg_len = strlen(msg); - - for (i = 0; i < msg_len; ++i) { - struct MSIM_ESCAPE_REPLACEMENT *replacement; - gchar *replace; - - replace = NULL; - - /* Check for characters that need to be escaped, and escape them. */ - for (j = 0; (replacement = &msim_escape_replacements[j]) && - replacement->code != NULL; ++j) { - if (msg[i] == replacement->text) { - replace = replacement->code; - break; - } - } - - if (replace) { - g_string_append(gs, replace); - } else { - g_string_append_c(gs, msg[i]); - } - } - -#ifdef MSIM_DEBUG_ESCAPE - purple_debug_info("msim", "msim_escape: msg=%s, ret=%s\n", msg, gs->str); -#endif - - return g_string_free(gs, FALSE); -} - -/** - * Unescape a protocol message. - * - * @return The unescaped message, caller must g_free(). - */ -gchar * -msim_unescape(const gchar *msg) -{ - GString *gs; - guint i, j; - guint msg_len; - - gs = g_string_new(""); - msg_len = strlen(msg); - - for (i = 0; i < msg_len; ++i) { - struct MSIM_ESCAPE_REPLACEMENT *replacement; - gchar replace; - - replace = msg[i]; - - for (j = 0; (replacement = &msim_escape_replacements[j]) && - replacement->code != NULL; ++j) { - if (msg[i] == replacement->code[0] && - i + 1 < msg_len && - msg[i + 1] == replacement->code[1]) { - replace = replacement->text; - ++i; - break; - } - } - - g_string_append_c(gs, replace); - } - -#ifdef MSIM_DEBUG_ESCAPE - purple_debug_info("msim", "msim_unescape: msg=%s, ret=%s\n", msg, gs->str); -#endif - - return g_string_free(gs, FALSE); -} - -/** - * Create a new message from va_list and its first argument. - * - * @param first_key The first argument (a key), or NULL to take all arguments - * from argp. - * @param argp A va_list of variadic arguments, already started with va_start(). - * @return New MsimMessage *, must be freed with msim_msg_free(). - * - * For internal use - users probably want msim_msg_new() or msim_send(). - */ -static MsimMessage * -msim_msg_new_v(gchar *first_key, va_list argp) -{ - gchar *key, *value; - MsimMessageType type; - MsimMessage *msg; - gboolean first; - - GString *gs; - GList *gl; - MsimMessage *dict; - - /* Begin with an empty message. */ - msg = NULL; - - /* First parameter can be given explicitly. */ - first = first_key != NULL; - - /* Read key, type, value triplets until NULL. */ - do { - if (first) { - key = first_key; - first = FALSE; - } else { - key = va_arg(argp, gchar *); - if (!key) { - break; - } - } - - type = va_arg(argp, int); - - /* Interpret variadic arguments. */ - switch (type) { - case MSIM_TYPE_INTEGER: - case MSIM_TYPE_BOOLEAN: - msg = msim_msg_append(msg, key, type, GUINT_TO_POINTER(va_arg(argp, int))); - break; - - case MSIM_TYPE_STRING: - value = va_arg(argp, char *); - - g_return_val_if_fail(value != NULL, FALSE); - - msg = msim_msg_append(msg, key, type, value); - break; - - case MSIM_TYPE_BINARY: - gs = va_arg(argp, GString *); - - g_return_val_if_fail(gs != NULL, FALSE); - - /* msim_msg_free() will free this GString the caller created. */ - msg = msim_msg_append(msg, key, type, gs); - break; - - case MSIM_TYPE_LIST: - gl = va_arg(argp, GList *); - - g_return_val_if_fail(gl != NULL, FALSE); - - msg = msim_msg_append(msg, key, type, gl); - break; - - case MSIM_TYPE_DICTIONARY: - dict = va_arg(argp, MsimMessage *); - - g_return_val_if_fail(dict != NULL, FALSE); - - msg = msim_msg_append(msg, key, type, dict); - break; - - default: - purple_debug_info("msim", "msim_send: unknown type %d\n", type); - break; - } - } while(key); - - return msg; -} - -/** - * Create a new MsimMessage. - * - * @param first_key The first key in the sequence, or NULL for an empty message. - * @param ... A sequence of gchar* key/type/value triplets, terminated with NULL. - * - * See msim_msg_append() documentation for details on types. - */ -MsimMessage * -msim_msg_new(gchar *first_key, ...) -{ - MsimMessage *ret = NULL; - va_list argp; - - if (first_key) { - va_start(argp, first_key); - ret = msim_msg_new_v(first_key, argp); - va_end(argp); - } - - return ret; -} - -/** - * Pack a string using the given GFunc and seperator. - * Used by msim_msg_dump() and msim_msg_pack(). - */ -static gchar * -msim_msg_pack_using(MsimMessage *msg, - GFunc gf, - const gchar *sep, - const gchar *begin, const gchar *end) -{ - int num_items; - gchar **strings; - gchar **strings_tmp; - gchar *joined; - gchar *final; - int i; - - g_return_val_if_fail(msg != NULL, NULL); - - num_items = g_list_length(msg); - - /* Add one for NULL terminator for g_strjoinv(). */ - strings = (gchar **)g_new0(gchar *, num_items + 1); - - strings_tmp = strings; - g_list_foreach(msg, gf, &strings_tmp); - - joined = g_strjoinv(sep, strings); - final = g_strconcat(begin, joined, end, NULL); - g_free(joined); - - /* Clean up. */ - for (i = 0; i < num_items; ++i) { - g_free(strings[i]); - } - - g_free(strings); - - return final; -} - -/** - * Return a human-readable string of the message. - * - * @return A new gchar *, must be g_free()'d. - */ -static gchar * -msim_msg_dump_to_str(MsimMessage *msg) -{ - gchar *debug_str; - - if (!msg) { - debug_str = g_strdup("<MsimMessage: empty>"); - } else { - debug_str = msim_msg_pack_using(msg, msim_msg_debug_string_element, - "\n", "<MsimMessage: \n", "\n/MsimMessage>"); - } - - return debug_str; -} - -/** - * Store a human-readable string describing the element. - * - * @param data Pointer to an MsimMessageElement. - * @param user_data - */ -static void -msim_msg_debug_string_element(gpointer data, gpointer user_data) -{ - MsimMessageElement *elem; - gchar *string; - GString *gs; - gchar *binary; - gchar ***items; /* wow, a pointer to a pointer to a pointer */ - - gchar *s; - GList *gl; - guint i; - - elem = (MsimMessageElement *)data; - items = user_data; - - switch (elem->type) { - case MSIM_TYPE_INTEGER: - string = g_strdup_printf("%s(integer): %d", elem->name, - GPOINTER_TO_UINT(elem->data)); - break; - - case MSIM_TYPE_RAW: - string = g_strdup_printf("%s(raw): %s", elem->name, - elem->data ? (gchar *)elem->data : "(NULL)"); - break; - - case MSIM_TYPE_STRING: - string = g_strdup_printf("%s(string): %s", elem->name, - elem->data ? (gchar *)elem->data : "(NULL)"); - break; - - case MSIM_TYPE_BINARY: - gs = (GString *)elem->data; - binary = purple_base64_encode((guchar*)gs->str, gs->len); - string = g_strdup_printf("%s(binary, %d bytes): %s", elem->name, (int)gs->len, binary); - g_free(binary); - break; - - case MSIM_TYPE_BOOLEAN: - string = g_strdup_printf("%s(boolean): %s", elem->name, - elem->data ? "TRUE" : "FALSE"); - break; - - case MSIM_TYPE_DICTIONARY: - if (!elem->data) { - s = g_strdup("(NULL)"); - } else { - s = msim_msg_dump_to_str((MsimMessage *)elem->data); - } - - if (!s) { - s = g_strdup("(NULL, couldn't msim_msg_dump_to_str)"); - } - - string = g_strdup_printf("%s(dict): %s", elem->name, s); - - g_free(s); - break; - - case MSIM_TYPE_LIST: - gs = g_string_new(""); - g_string_append_printf(gs, "%s(list): \n", elem->name); - - i = 0; - for (gl = (GList *)elem->data; gl != NULL; gl = g_list_next(gl)) { - g_string_append_printf(gs, " %d. %s\n", i, (gchar *)(gl->data)); - ++i; - } - - string = g_string_free(gs, FALSE); - break; - - default: - string = g_strdup_printf("%s(unknown type %d", - elem->name ? elem->name : "(NULL)", elem->type); - break; - } - - **items = string; - ++(*items); -} - -/** - * Search for and return the node in msg, matching name, or NULL. - * - * @param msg Message to search within. - * @param name Field name to search for. - * - * @return The GList * node for the MsimMessageElement with the given name, or NULL if not found or name is NULL. - * - * For internal use - users probably want to use msim_msg_get() to - * access the MsimMessageElement *, instead of the GList * container. - * - */ -static GList * -msim_msg_get_node(const MsimMessage *msg, const gchar *name) -{ - GList *node; - - if (!name || !msg) { - return NULL; - } - - /* Linear search for the given name. O(n) but n is small. */ - for (node = (GList*)msg; node != NULL; node = g_list_next(node)) { - MsimMessageElement *elem; - - elem = (MsimMessageElement *)node->data; - - g_return_val_if_fail(elem != NULL, NULL); - g_return_val_if_fail(elem->name != NULL, NULL); - - if (strcmp(elem->name, name) == 0) { - return node; - } - } - return NULL; -} - -/** - * Create a new MsimMessageElement * - must be g_free()'d. - * - * For internal use; users probably want msim_msg_append() or msim_msg_insert_before(). - * - * @param dynamic_name Whether 'name' should be freed when the message is destroyed. - */ -static MsimMessageElement * -msim_msg_element_new(const gchar *name, MsimMessageType type, gpointer data, gboolean dynamic_name) -{ - MsimMessageElement *elem; - - elem = g_new0(MsimMessageElement, 1); - - elem->name = name; - elem->dynamic_name = dynamic_name; - elem->type = type; - elem->data = data; - - return elem; -} - -/** - * Append a new element to a message. - * - * @param name Textual name of element (static string, neither copied nor freed). - * @param type An MSIM_TYPE_* code. - * @param data Pointer to data, see below. - * - * @return The new message - must be assigned to as with GList*. For example: - * - * msg = msim_msg_append(msg, ...) - * - * The data parameter depends on the type given: - * - * * MSIM_TYPE_INTEGER: Use GUINT_TO_POINTER(x). - * - * * MSIM_TYPE_BINARY: Same as integer, non-zero is TRUE and zero is FALSE. - * - * * MSIM_TYPE_STRING: gchar *. The data WILL BE FREED - use g_strdup() if needed. - * - * * MSIM_TYPE_RAW: gchar *. The data WILL BE FREED - use g_strdup() if needed. - * - * * MSIM_TYPE_BINARY: g_string_new_len(data, length). The data AND GString will be freed. - * - * * MSIM_TYPE_DICTIONARY: An MsimMessage *. Freed when message is destroyed. - * - * * MSIM_TYPE_LIST: GList * of gchar *. Again, everything will be freed. - * - * */ -MsimMessage * -msim_msg_append(MsimMessage *msg, const gchar *name, - MsimMessageType type, gpointer data) -{ - return g_list_append(msg, msim_msg_element_new(name, type, data, FALSE)); -} - -/** - * Append a new element, but with a dynamically-allocated name. - * Exactly the same as msim_msg_append(), except 'name' will be freed when - * the message is destroyed. Normally, it isn't, because a static string is given. - */ -static MsimMessage * -msim_msg_append_dynamic_name(MsimMessage *msg, gchar *name, - MsimMessageType type, gpointer data) -{ - return g_list_append(msg, msim_msg_element_new(name, type, data, TRUE)); -} - -/** - * Insert a new element into a message, before the given element name. - * - * @param name_before Name of the element to insert the new element before. If - * could not be found or NULL, new element will be inserted at end. - * - * See msim_msg_append() for usage of other parameters, and an important note about return value. - */ -MsimMessage * -msim_msg_insert_before(MsimMessage *msg, const gchar *name_before, - const gchar *name, MsimMessageType type, gpointer data) -{ - MsimMessageElement *new_elem; - GList *node_before; - - new_elem = msim_msg_element_new(name, type, data, FALSE); - - node_before = msim_msg_get_node(msg, name_before); - - return g_list_insert_before(msg, node_before, new_elem); -} - -/** - * Perform a deep copy on a GList * of gchar * strings. Free with msim_msg_list_free(). - */ -static GList * -msim_msg_list_copy(const GList *old) -{ - GList *new_list; - - new_list = NULL; - - /* Deep copy (g_list_copy is shallow). Copy each string. */ - for (; old != NULL; old = g_list_next(old)) { - new_list = g_list_append(new_list, g_strdup(old->data)); - } - - return new_list; -} - -/** - * Clone an individual element. - * - * @param data MsimMessageElement * to clone. - * @param user_data Pointer to MsimMessage * to add cloned element to. - */ -static void -msim_msg_clone_element(gpointer data, gpointer user_data) -{ - MsimMessageElement *elem; - MsimMessage **new; - gpointer new_data; - - GString *gs; - MsimMessage *dict; - - elem = (MsimMessageElement *)data; - new = (MsimMessage **)user_data; - - switch (elem->type) { - case MSIM_TYPE_BOOLEAN: - case MSIM_TYPE_INTEGER: - new_data = elem->data; - break; - - case MSIM_TYPE_RAW: - case MSIM_TYPE_STRING: - new_data = g_strdup((gchar *)elem->data); - break; - - case MSIM_TYPE_LIST: - new_data = (gpointer)msim_msg_list_copy((GList *)(elem->data)); - break; - - case MSIM_TYPE_BINARY: - gs = (GString *)elem->data; - - new_data = g_string_new_len(gs->str, gs->len); - break; - case MSIM_TYPE_DICTIONARY: - dict = (MsimMessage *)elem->data; - - new_data = msim_msg_clone(dict); - break; - - default: - purple_debug_info("msim", "msim_msg_clone_element: unknown type %d\n", elem->type); - g_return_if_reached(); - } - - /* Append cloned data. Note that the 'name' field is a static string, so it - * never needs to be copied nor freed. */ - if (elem->dynamic_name) - *new = msim_msg_append_dynamic_name(*new, g_strdup(elem->name), elem->type, new_data); - else - *new = msim_msg_append(*new, elem->name, elem->type, new_data); -} - -/** - * Clone an existing MsimMessage. - * - * @return Cloned message; caller should free with msim_msg_free(). - */ -MsimMessage * -msim_msg_clone(MsimMessage *old) -{ - MsimMessage *new; - - if (old == NULL) { - return NULL; - } - - new = msim_msg_new(FALSE); - - g_list_foreach(old, msim_msg_clone_element, &new); - - return new; -} - -/** - * Free the data of a message element. - * - * @param elem The MsimMessageElement * - * - * Note this only frees the element data; you may also want to free the - * element itself with g_free() (see msim_msg_free_element()). - */ -void -msim_msg_free_element_data(MsimMessageElement *elem) -{ - switch (elem->type) { - case MSIM_TYPE_BOOLEAN: - case MSIM_TYPE_INTEGER: - /* Integer value stored in gpointer - no need to free(). */ - break; - - case MSIM_TYPE_RAW: - case MSIM_TYPE_STRING: - /* Always free strings - caller should have g_strdup()'d if - * string was static or temporary and not to be freed. */ - g_free(elem->data); - break; - - case MSIM_TYPE_BINARY: - /* Free the GString itself and the binary data. */ - g_string_free((GString *)elem->data, TRUE); - break; - - case MSIM_TYPE_DICTIONARY: - msim_msg_free((MsimMessage *)elem->data); - break; - - case MSIM_TYPE_LIST: - g_list_free((GList *)elem->data); - break; - - default: - purple_debug_info("msim", "msim_msg_free_element_data: " - "not freeing unknown type %d\n", elem->type); - break; - } -} - -/** - * Free a GList * of MsimMessageElement *'s. - */ -void -msim_msg_list_free(GList *l) -{ - - for (; l != NULL; l = g_list_next(l)) { - 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); -} - -/** - * Free an individual message element. - * - * @param data MsimMessageElement * to free. - * @param user_data Not used; required to match g_list_foreach() callback prototype. - * - * Frees both the element data and the element itself. - * Also frees the name if dynamic_name is TRUE. - */ -static void -msim_msg_free_element(gpointer data, gpointer user_data) -{ - MsimMessageElement *elem; - - elem = (MsimMessageElement *)data; - - msim_msg_free_element_data(elem); - - if (elem->dynamic_name) - /* Need to cast to remove const-ness, because - * elem->name is almost always a constant, static - * string, but not in this case. */ - g_free((gchar *)elem->name); - - g_free(elem); -} - -/** - * Free a complete message. - */ -void -msim_msg_free(MsimMessage *msg) -{ - if (!msg) { - /* already free as can be */ - return; - } - - g_list_foreach(msg, msim_msg_free_element, NULL); - g_list_free(msg); -} - -/** - * Pack an element into its protocol representation. - * - * @param data Pointer to an MsimMessageElement. - * @param user_data Pointer to a gchar ** array of string items. - * - * Called by msim_msg_pack(). Will pack the MsimMessageElement into - * a part of the protocol string and append it to the array. Caller - * is responsible for creating array to correct dimensions, and - * freeing each string element of the array added by this function. - */ -static void -msim_msg_pack_element(gpointer data, gpointer user_data) -{ - MsimMessageElement *elem; - gchar *string, *data_string; - gchar ***items; - - elem = (MsimMessageElement *)data; - items = (gchar ***)user_data; - - /* Exclude elements beginning with '_' from packed protocol messages. */ - if (elem->name[0] == '_') { - return; - } - - data_string = msim_msg_pack_element_data(elem); - - switch (elem->type) { - /* These types are represented by key name/value pairs (converted above). */ - case MSIM_TYPE_INTEGER: - case MSIM_TYPE_RAW: - case MSIM_TYPE_STRING: - case MSIM_TYPE_BINARY: - case MSIM_TYPE_DICTIONARY: - case MSIM_TYPE_LIST: - string = g_strconcat(elem->name, "\\", data_string, NULL); - break; - - /* Boolean is represented by absence or presence of name. */ - case MSIM_TYPE_BOOLEAN: - if (GPOINTER_TO_UINT(elem->data)) { - /* True - leave in, with blank value. */ - string = g_strdup_printf("%s\\", elem->name); - } else { - /* False - leave out. */ - string = g_strdup(""); - } - break; - - default: - g_free(data_string); - g_return_if_reached(); - break; - } - - g_free(data_string); - - **items = string; - ++(*items); -} - -/** - * Pack an element into its protcol representation inside a dictionary. - * - * See msim_msg_pack_element(). - */ -static void -msim_msg_pack_element_dict(gpointer data, gpointer user_data) -{ - MsimMessageElement *elem; - gchar *string, *data_string, ***items; - - elem = (MsimMessageElement *)data; - items = (gchar ***)user_data; - - /* Exclude elements beginning with '_' from packed protocol messages. */ - if (elem->name[0] == '_') { - return; - } - - data_string = msim_msg_pack_element_data(elem); - - g_return_if_fail(data_string != NULL); - - switch (elem->type) { - /* These types are represented by key name/value pairs (converted above). */ - case MSIM_TYPE_INTEGER: - case MSIM_TYPE_RAW: - case MSIM_TYPE_STRING: - case MSIM_TYPE_BINARY: - case MSIM_TYPE_DICTIONARY: - case MSIM_TYPE_LIST: - case MSIM_TYPE_BOOLEAN: /* Boolean is On or Off */ - string = g_strconcat(elem->name, "=", data_string, NULL); - break; - - default: - g_free(data_string); - g_return_if_fail(FALSE); - break; - } - - g_free(data_string); - - **items = string; - ++(*items); -} - -/** - * Return a packed string of a message suitable for sending over the wire. - * - * @return A string. Caller must g_free(). - */ -gchar * -msim_msg_pack(MsimMessage *msg) -{ - g_return_val_if_fail(msg != NULL, NULL); - - return msim_msg_pack_using(msg, msim_msg_pack_element, "\\", "\\", "\\final\\"); -} - -/** - * Return a packed string of a dictionary, suitable for embedding in MSIM_TYPE_DICTIONARY. - * - * @return A string; caller must g_free(). - */ -static gchar * -msim_msg_pack_dict(MsimMessage *msg) -{ - g_return_val_if_fail(msg != NULL, NULL); - - return msim_msg_pack_using(msg, msim_msg_pack_element_dict, "\034", "", ""); -} - -/** - * Send an existing MsimMessage. - */ -gboolean -msim_msg_send(MsimSession *session, MsimMessage *msg) -{ - gchar *raw; - gboolean success; - - raw = msim_msg_pack(msg); - g_return_val_if_fail(raw != NULL, FALSE); - success = msim_send_raw(session, raw); - g_free(raw); - - return success; -} - -/** - * Return a message element data as a new string for a raw protocol message, - * converting from other types (integer, etc.) if necessary. - * - * @return const gchar * The data as a string, or NULL. Caller must g_free(). - * - * Returns a string suitable for inclusion in a raw protocol message, not necessarily - * optimal for human consumption. For example, strings are escaped. Use - * msim_msg_get_string() if you want a string, which in some cases is same as this. - */ -gchar * -msim_msg_pack_element_data(MsimMessageElement *elem) -{ - GString *gs; - GList *gl; - - g_return_val_if_fail(elem != NULL, NULL); - - switch (elem->type) { - case MSIM_TYPE_INTEGER: - return g_strdup_printf("%d", GPOINTER_TO_UINT(elem->data)); - - case MSIM_TYPE_RAW: - /* Not un-escaped - this is a raw element, already escaped if necessary. */ - return (gchar *)g_strdup((gchar *)elem->data); - - case MSIM_TYPE_STRING: - /* Strings get escaped. msim_escape() creates a new string. */ - g_return_val_if_fail(elem->data != NULL, NULL); - return elem->data ? msim_escape((gchar *)elem->data) : - g_strdup("(NULL)"); - - case MSIM_TYPE_BINARY: - gs = (GString *)elem->data; - /* Do not escape! */ - return purple_base64_encode((guchar *)gs->str, gs->len); - - case MSIM_TYPE_BOOLEAN: - /* Not used by messages in the wire protocol * -- see msim_msg_pack_element. - * Only used by dictionaries, see msim_msg_pack_element_dict. */ - return elem->data ? g_strdup("On") : g_strdup("Off"); - - case MSIM_TYPE_DICTIONARY: - return msim_msg_pack_dict((MsimMessage *)elem->data); - - case MSIM_TYPE_LIST: - /* Pack using a|b|c|d|... */ - gs = g_string_new(""); - - for (gl = (GList *)elem->data; gl != NULL; gl = g_list_next(gl)) { - g_string_append_printf(gs, "%s", (gchar*)(gl->data)); - - /* All but last element is separated by a bar. */ - if (g_list_next(gl)) - g_string_append(gs, "|"); - } - - return g_string_free(gs, FALSE); - - default: - purple_debug_info("msim", "field %s, unknown type %d\n", - elem->name ? elem->name : "(NULL)", - elem->type); - return NULL; - } -} - -/** - * Send a message to the server, whose contents is specified using - * variable arguments. - * - * @param session - * @param ... A sequence of gchar* key/type/value triplets, terminated with NULL. - * - * This function exists for coding convenience: it allows a message to be created - * and sent in one line of code. Internally it calls msim_msg_send(). - * - * IMPORTANT: See msim_msg_append() documentation for details on element types. - * - */ -gboolean -msim_send(MsimSession *session, ...) -{ - gboolean success; - MsimMessage *msg; - va_list argp; - - va_start(argp, session); - msg = msim_msg_new_v(NULL, argp); - va_end(argp); - - /* Actually send the message. */ - success = msim_msg_send(session, msg); - - /* Cleanup. */ - msim_msg_free(msg); - - return success; -} - -/** - * Print a human-readable string of the message to Purple's debug log. - * - * @param fmt_string A static string, in which '%s' will be replaced. - */ -void -msim_msg_dump(const gchar *fmt_string, MsimMessage *msg) -{ - gchar *debug_str; - - g_return_if_fail(fmt_string != NULL); - - debug_str = msim_msg_dump_to_str(msg); - - g_return_if_fail(debug_str != NULL); - - purple_debug_info("msim", fmt_string, debug_str); - - g_free(debug_str); -} - -/** - * Parse a raw protocol message string into a MsimMessage *. - * - * @param raw The raw message string to parse, will be g_free()'d. - * - * @return MsimMessage *. Caller should msim_msg_free() when done. - */ -MsimMessage * -msim_parse(const gchar *raw) -{ - MsimMessage *msg; - gchar *token; - gchar **tokens; - gchar *key; - gchar *value; - int i; - - g_return_val_if_fail(raw != NULL, NULL); - - purple_debug_info("msim", "msim_parse: got <%s>\n", raw); - - key = NULL; - - /* All messages begin with a \. */ - if (raw[0] != '\\' || raw[1] == 0) { - purple_debug_info("msim", "msim_parse: incomplete/bad string, " - "missing initial backslash: <%s>\n", raw); - /* XXX: Should we try to recover, and read to first backslash? */ - - return NULL; - } - - msg = msim_msg_new(FALSE); - - for (tokens = g_strsplit(raw + 1, "\\", 0), i = 0; - (token = tokens[i]); - i++) { -#ifdef MSIM_DEBUG_PARSE - purple_debug_info("msim", "tok=<%s>, i%2=%d\n", token, i % 2); -#endif - if (i % 2) { - /* Odd-numbered ordinal is a value. */ - - value = token; - - /* Incoming protocol messages get tagged as MSIM_TYPE_RAW, which - * represents an untyped piece of data. msim_msg_get_* will - * convert to appropriate types for caller, and handle unescaping if needed. */ - msg = msim_msg_append_dynamic_name(msg, g_strdup(key), MSIM_TYPE_RAW, g_strdup(value)); -#ifdef MSIM_DEBUG_PARSE - purple_debug_info("msim", "insert string: |%s|=|%s|\n", key, value); -#endif - } else { - /* Even numbered indexes are key names. */ - key = token; - } - } - g_strfreev(tokens); - - return msg; -} - -/** - * Return the first MsimMessageElement * with given name in the MsimMessage *. - * - * @param name Name to search for. - * - * @return MsimMessageElement * matching name, or NULL. - * - * Note: useful fields of MsimMessageElement are 'data' and 'type', which - * you can access directly. But it is often more convenient to use - * another msim_msg_get_* that converts the data to what type you want. - */ -MsimMessageElement * -msim_msg_get(const MsimMessage *msg, const gchar *name) -{ - GList *node; - - node = msim_msg_get_node(msg, name); - if (node) { - return (MsimMessageElement *)node->data; - } else { - return NULL; - } -} - -gchar * -msim_msg_get_string_from_element(MsimMessageElement *elem) -{ - g_return_val_if_fail(elem != NULL, NULL); - switch (elem->type) { - case MSIM_TYPE_INTEGER: - return g_strdup_printf("%d", GPOINTER_TO_UINT(elem->data)); - - case MSIM_TYPE_RAW: - /* Raw element from incoming message - if its a string, it'll - * be escaped. */ - return msim_unescape((gchar *)elem->data); - - case MSIM_TYPE_STRING: - /* Already unescaped. */ - return g_strdup((gchar *)elem->data); - - default: - purple_debug_info("msim", "msim_msg_get_string_element: type %d unknown, name %s\n", - elem->type, elem->name ? elem->name : "(NULL)"); - return NULL; - } -} - -/** - * Return the data of an element of a given name, as a string. - * - * @param name Name of element. - * - * @return gchar * The data as a string, or NULL if not found. - * Caller must g_free(). - * - * Note that msim_msg_pack_element_data() is similar, but returns a string - * for inclusion into a raw protocol string (escaped and everything). - * This function unescapes the string for you, if needed. - */ -gchar * -msim_msg_get_string(const MsimMessage *msg, const gchar *name) -{ - MsimMessageElement *elem; - - elem = msim_msg_get(msg, name); - if (!elem) { - return NULL; - } - - return msim_msg_get_string_from_element(elem); -} - -/** - * Parse a |-separated string into a new GList. Free with msim_msg_list_free(). - */ -static GList * -msim_msg_list_parse(const gchar *raw) -{ - gchar **array; - GList *list; - guint i; - - array = g_strsplit(raw, "|", 0); - list = NULL; - - /* TODO: escape/unescape /3 <-> | within list elements */ - - for (i = 0; array[i] != NULL; ++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); - - return list; -} - -static GList * -msim_msg_get_list_from_element(MsimMessageElement *elem) -{ - g_return_val_if_fail(elem != NULL, NULL); - switch (elem->type) { - case MSIM_TYPE_LIST: - return msim_msg_list_copy((GList *)elem->data); - - case MSIM_TYPE_RAW: - return msim_msg_list_parse((gchar *)elem->data); - - default: - purple_debug_info("msim_msg_get_list", "type %d unknown, name %s\n", - elem->type, elem->name ? elem->name : "(NULL)"); - return NULL; - } -} - -/** - * Return an element as a new list. Caller frees with msim_msg_list_free(). - */ -GList * -msim_msg_get_list(const MsimMessage *msg, const gchar *name) -{ - MsimMessageElement *elem; - - elem = msim_msg_get(msg, name); - if (!elem) { - return NULL; - } - - return msim_msg_get_list_from_element(elem); -} - -/** - * Parse a \x1c-separated "dictionary" of key=value pairs into a hash table. - * - * @param raw The text of the dictionary to parse. Often the - * value for the 'body' field. - * - * @return A new MsimMessage *. Must msim_msg_free() when done. - */ -static MsimMessage * -msim_msg_dictionary_parse(const gchar *raw) -{ - MsimMessage *dict; - gchar *item; - gchar **items; - gchar **elements; - guint i; - - g_return_val_if_fail(raw != NULL, NULL); - - dict = msim_msg_new(NULL); - - for (items = g_strsplit(raw, "\x1c", 0), i = 0; - (item = items[i]); - i++) { - gchar *key, *value; - - elements = g_strsplit(item, "=", 2); - - key = elements[0]; - if (!key) { - purple_debug_info("msim", "msim_msg_dictionary_parse(%s): null key\n", - raw); - g_strfreev(elements); - break; - } - - value = elements[1]; - if (!value) { - purple_debug_info("msim", "msim_msg_dictionary_prase(%s): null value\n", - raw); - g_strfreev(elements); - break; - } - -#ifdef MSIM_DEBUG_PARSE - purple_debug_info("msim_msg_dictionary_parse","-- %s: %s\n", key ? key : "(NULL)", - value ? value : "(NULL)"); -#endif - /* Append with _dynamic_name since g_strdup(key) is dynamic, and - * needs to be freed when the message is destroyed. It isn't static as usual. */ - dict = msim_msg_append_dynamic_name(dict, g_strdup(key), MSIM_TYPE_RAW, g_strdup(value)); - - g_strfreev(elements); - } - - g_strfreev(items); - - return dict; -} - -static MsimMessage * -msim_msg_get_dictionary_from_element(MsimMessageElement *elem) -{ - g_return_val_if_fail(elem != NULL, NULL); - switch (elem->type) { - case MSIM_TYPE_DICTIONARY: - return msim_msg_clone((MsimMessage *)elem->data); - - case MSIM_TYPE_RAW: - return msim_msg_dictionary_parse(elem->data); - - default: - purple_debug_info("msim_msg_get_dictionary", "type %d unknown, name %s\n", - elem->type, elem->name ? elem->name : "(NULL)"); - return NULL; - } -} - -/** - * Return an element as a new dictionary. Caller frees with msim_msg_free(). - */ -MsimMessage * -msim_msg_get_dictionary(const MsimMessage *msg, const gchar *name) -{ - MsimMessageElement *elem; - - elem = msim_msg_get(msg, name); - if (!elem) { - return NULL; - } - - return msim_msg_get_dictionary_from_element(elem); -} - -guint -msim_msg_get_integer_from_element(MsimMessageElement *elem) -{ - g_return_val_if_fail(elem != NULL, 0); - switch (elem->type) { - case MSIM_TYPE_INTEGER: - return GPOINTER_TO_UINT(elem->data); - - case MSIM_TYPE_RAW: - case MSIM_TYPE_STRING: - /* TODO: find out if we need larger integers */ - return (guint)atoi((gchar *)elem->data); - - default: - return 0; - } -} - -/** - * Return the data of an element of a given name, as an unsigned integer. - * - * @param name Name of element. - * - * @return guint Numeric representation of data, or 0 if could not be converted / not found. - * - * Useful to obtain an element's data if you know it should be an integer, - * even if it is not stored as an MSIM_TYPE_INTEGER. MSIM_TYPE_STRING will - * be converted handled correctly, for example. - */ -guint -msim_msg_get_integer(const MsimMessage *msg, const gchar *name) -{ - MsimMessageElement *elem; - - elem = msim_msg_get(msg, name); - - if (!elem) { - return 0; - } - - return msim_msg_get_integer_from_element(elem); -} - -static gboolean -msim_msg_get_binary_from_element(MsimMessageElement *elem, gchar **binary_data, gsize *binary_length) -{ - GString *gs; - - g_return_val_if_fail(elem != NULL, FALSE); - - switch (elem->type) { - case MSIM_TYPE_RAW: - /* Incoming messages are tagged with MSIM_TYPE_RAW, and - * converted appropriately. They can still be "strings", just they won't - * be tagged as MSIM_TYPE_STRING (as MSIM_TYPE_STRING is intended to be used - * by msimprpl code for things like instant messages - stuff that should be - * escaped if needed). DWIM. - */ - - /* Previously, incoming messages were stored as MSIM_TYPE_STRING. - * This was fine for integers and strings, since they can easily be - * converted in msim_get_*, as desirable. However, it does not work - * well for binary strings. Consider: - * - * If incoming base64'd elements were tagged as MSIM_TYPE_STRING. - * msim_msg_get_binary() sees MSIM_TYPE_STRING, base64 decodes, returns. - * everything is fine. - * But then, msim_send() is called on the incoming message, which has - * a base64'd MSIM_TYPE_STRING that really is encoded binary. The values - * will be escaped since strings are escaped, and / becomes /2; no good. - * - */ - *binary_data = (gchar *)purple_base64_decode((const gchar *)elem->data, binary_length); - return ((*binary_data) != NULL); - - case MSIM_TYPE_BINARY: - gs = (GString *)elem->data; - - /* Duplicate data, so caller can g_free() it. */ - *binary_data = g_memdup(gs->str, gs->len); - *binary_length = gs->len; - - return TRUE; - - - /* Rejected because if it isn't already a GString, have to g_new0 it and - * then caller has to ALSO free the GString! - * - * return (GString *)elem->data; */ - - default: - purple_debug_info("msim", "msim_msg_get_binary: unhandled type %d for key %s\n", - elem->type, elem->name ? elem->name : "(NULL)"); - return FALSE; - } -} - -/** - * Return the data of an element of a given name, as a binary GString. - * - * @param binary_data A pointer to a new pointer, which will be filled in with the binary data. CALLER MUST g_free(). - * - * @param binary_length A pointer to an integer, which will be set to the binary data length. - * - * @return TRUE if successful, FALSE if not. - */ -gboolean -msim_msg_get_binary(const MsimMessage *msg, const gchar *name, - gchar **binary_data, gsize *binary_length) -{ - MsimMessageElement *elem; - - elem = msim_msg_get(msg, name); - if (!elem) { - return FALSE; - } - - return msim_msg_get_binary_from_element(elem, binary_data, binary_length); -} diff --git a/libpurple/protocols/myspace/message.h b/libpurple/protocols/myspace/message.h deleted file mode 100644 index 5f1bcc852b..0000000000 --- a/libpurple/protocols/myspace/message.h +++ /dev/null @@ -1,109 +0,0 @@ -/** MySpaceIM protocol messages - * - * \author Jeff Connelly - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ - -#ifndef _MYSPACE_MESSAGE_H -#define _MYSPACE_MESSAGE_H - -#include <glib.h> - -#define MsimMessage GList /* #define instead of typedef to avoid casting */ -typedef gchar MsimMessageType; -typedef struct _MsimMessageElement MsimMessageElement; - -#include "session.h" - -/* Types */ -struct _MsimMessageElement -{ - const gchar *name; /**< Textual name of element. */ - gboolean dynamic_name; /**< TRUE if 'name' is a dynamic string to be freed, not static. */ - guint type; /**< MSIM_TYPE_* code. */ - gpointer data; /**< Pointer to data, or GUINT_TO_POINTER for int/bool. */ -}; - -#define msim_msg_get_next_element_node(msg) ((MsimMessage *)(msg->next)) - -/* Protocol field types */ -#define MSIM_TYPE_RAW '-' -#define MSIM_TYPE_INTEGER 'i' -#define MSIM_TYPE_STRING 's' -#define MSIM_TYPE_BINARY 'b' -#define MSIM_TYPE_BOOLEAN 'f' -#define MSIM_TYPE_DICTIONARY 'd' -#define MSIM_TYPE_LIST 'l' - -gchar *msim_escape(const gchar *msg); -gchar *msim_unescape(const gchar *msg); - -MsimMessage *msim_msg_new(gchar *first_key, ...); -/* No sentinel attribute, because can leave off varargs if not_empty is FALSE. */ - -MsimMessage *msim_msg_clone(MsimMessage *old); -void msim_msg_free_element_data(MsimMessageElement *elem); -void msim_msg_free(MsimMessage *msg); -MsimMessage *msim_msg_append(MsimMessage *msg, const gchar *name, MsimMessageType type, gpointer data); -MsimMessage *msim_msg_insert_before(MsimMessage *msg, const gchar *name_before, const gchar *name, MsimMessageType type, gpointer data); -gchar *msim_msg_pack_element_data(MsimMessageElement *elem); -void msim_msg_dump(const char *fmt_string, MsimMessage *msg); -gchar *msim_msg_pack(MsimMessage *msg); - -void msim_msg_list_free(GList *l); - -/* Based on http://permalink.gmane.org/gmane.comp.parsers.sparse/695 - * Define macros for useful gcc attributes. */ -#ifdef __GNUC__ -#define GCC_VERSION (__GNUC__ * 1000 + __GNUC_MINOR__) -#define FORMAT_ATTR(pos) __attribute__ ((__format__ (__printf__, pos, pos+1))) -#define NORETURN_ATTR __attribute__ ((__noreturn__)) -/* __sentinel__ attribute was introduced in gcc 3.5 */ -#if (GCC_VERSION >= 3005) - #define SENTINEL_ATTR __attribute__ ((__sentinel__(0))) -#else - #define SENTINEL_ATTR -#endif /* gcc >= 3.5 */ -#else - #define FORMAT_ATTR(pos) - #define NORETURN_ATTR - #define SENTINEL_ATTR -#endif - -/* Cause gcc to emit "a missing sentinel in function call" if forgot - * to write NULL as last, terminating parameter. */ -gboolean msim_send(struct _MsimSession *session, ...) SENTINEL_ATTR; - -gboolean msim_msg_send(struct _MsimSession *session, MsimMessage *msg); - -MsimMessage *msim_parse(const gchar *raw); - -MsimMessageElement *msim_msg_get(const MsimMessage *msg, const gchar *name); - -/* Retrieve data by name */ -gchar *msim_msg_get_string(const MsimMessage *msg, const gchar *name); -GList *msim_msg_get_list(const MsimMessage *msg, const gchar *name); -MsimMessage *msim_msg_get_dictionary(const MsimMessage *msg, const gchar *name); -guint msim_msg_get_integer(const MsimMessage *msg, const gchar *name); -gboolean msim_msg_get_binary(const MsimMessage *msg, const gchar *name, gchar **binary_data, gsize *binary_length); - -/* Retrieve data by element (MsimMessageElement *), returned from msim_msg_get() */ -gchar *msim_msg_get_string_from_element(MsimMessageElement *elem); -guint msim_msg_get_integer_from_element(MsimMessageElement *elem); - -#endif /* _MYSPACE_MESSAGE_H */ diff --git a/libpurple/protocols/myspace/myspace.c b/libpurple/protocols/myspace/myspace.c deleted file mode 100644 index 4a261192a0..0000000000 --- a/libpurple/protocols/myspace/myspace.c +++ /dev/null @@ -1,3667 +0,0 @@ -/** - * MySpaceIM Protocol Plugin - * - * \author Jeff Connelly - * - * Copyright (C) 2007, Jeff Connelly <jeff2@soc.pidgin.im> - * - * Based on Purple's "C Plugin HOWTO" hello world example. - * - * Code also drawn from mockprpl: - * http://snarfed.org/space/purple+mock+protocol+plugin - * Copyright (C) 2004-2007, Ryan Barrett <mockprpl@ryanb.org> - * - * and some constructs also based on existing Purple plugins, which are: - * Copyright (C) 2003, Robbert Haarman <purple@inglorion.net> - * Copyright (C) 2003, Ethan Blanton <eblanton@cs.purdue.edu> - * Copyright (C) 2000-2003, Rob Flynn <rob@tgflinux.com> - * Copyright (C) 1998-1999, Mark Spencer <markster@marko.net> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ - -#define PURPLE_PLUGIN - -#include "myspace.h" - -#include "privacy.h" - -static void msim_set_status(PurpleAccount *account, PurpleStatus *status); -static void msim_set_idle(PurpleConnection *gc, int time); - -/** - * Perform actual postprocessing on a message, adding userid as specified. - * - * @param msg The message to postprocess. - * @param uid_before Name of field where to insert new field before, or NULL for end. - * @param uid_field_name Name of field to add uid to. - * @param uid The userid to insert. - * - * If the field named by uid_field_name already exists, then its string contents will - * be used for the field, except "<uid>" will be replaced by the userid. - * - * If the field named by uid_field_name does not exist, it will be added before the - * field named by uid_before, as an integer, with the userid. - * - * Does not handle sending, or scheduling userid lookup. For that, see msim_postprocess_outgoing(). - */ -static MsimMessage * -msim_do_postprocessing(MsimMessage *msg, const gchar *uid_before, - const gchar *uid_field_name, guint uid) -{ - MsimMessageElement *elem; - - /* First, check - if the field already exists, replace <uid> within it */ - if ((elem = msim_msg_get(msg, uid_field_name)) != NULL) { - gchar *fmt_string; - gchar *uid_str, *new_str; - - /* Get the packed element, flattening it. This allows <uid> to be - * replaced within nested data structures, since the replacement is done - * on the linear, packed data, not on a complicated data structure. - * - * For example, if the field was originally a dictionary or a list, you - * would have to iterate over all the items in it to see what needs to - * be replaced. But by packing it first, the <uid> marker is easily replaced - * just by a string replacement. - */ - fmt_string = msim_msg_pack_element_data(elem); - - uid_str = g_strdup_printf("%d", uid); - new_str = purple_strreplace(fmt_string, "<uid>", uid_str); - g_free(uid_str); - g_free(fmt_string); - - /* Free the old element data */ - msim_msg_free_element_data(elem->data); - - /* Replace it with our new data */ - elem->data = new_str; - elem->type = MSIM_TYPE_RAW; - - } else { - /* Otherwise, insert new field into outgoing message. */ - msg = msim_msg_insert_before(msg, uid_before, uid_field_name, MSIM_TYPE_INTEGER, GUINT_TO_POINTER(uid)); - } - - return msg; -} - -/** - * Callback for msim_postprocess_outgoing() to add a userid to a message, and send it (once receiving userid). - * - * @param session - * @param userinfo The user information reply message, containing the user ID - * @param data The message to postprocess and send. - * - * The data message should contain these fields: - * - * _uid_field_name: string, name of field to add with userid from userinfo message - * _uid_before: string, name of field before field to insert, or NULL for end - */ -static void -msim_postprocess_outgoing_cb(MsimSession *session, const MsimMessage *userinfo, - gpointer data) -{ - gchar *uid_field_name, *uid_before, *username; - guint uid; - MsimMessage *msg, *body; - - msg = (MsimMessage *)data; - - /* Obtain userid from userinfo message. */ - body = msim_msg_get_dictionary(userinfo, "body"); - g_return_if_fail(body != NULL); - - uid = msim_msg_get_integer(body, "UserID"); - msim_msg_free(body); - - username = msim_msg_get_string(msg, "_username"); - - if (!uid) { - gchar *msg; - - msg = g_strdup_printf(_("No such user: %s"), username); - if (!purple_conv_present_error(username, session->account, msg)) { - purple_notify_error(NULL, NULL, _("User lookup"), msg); - } - - g_free(msg); - g_free(username); - /* TODO: free - * msim_msg_free(msg); - */ - return; - } - - uid_field_name = msim_msg_get_string(msg, "_uid_field_name"); - uid_before = msim_msg_get_string(msg, "_uid_before"); - - msg = msim_do_postprocessing(msg, uid_before, uid_field_name, uid); - - /* Send */ - if (!msim_msg_send(session, msg)) { - msim_msg_dump("msim_postprocess_outgoing_cb: sending failed for message: %s\n", msg); - } - - - /* Free field names AFTER sending message, because MsimMessage does NOT copy - * field names - instead, treats them as static strings (which they usually are). - */ - g_free(uid_field_name); - g_free(uid_before); - g_free(username); - /* TODO: free - * msim_msg_free(msg); - */ -} - -/** - * Postprocess and send a message. - * - * @param session - * @param msg Message to postprocess. Will NOT be freed. - * @param username Username to resolve. Assumed to be a static string (will not be freed or copied). - * @param uid_field_name Name of new field to add, containing uid of username. Static string. - * @param uid_before Name of existing field to insert username field before. Static string. - * - * @return TRUE if successful. - */ -static gboolean -msim_postprocess_outgoing(MsimSession *session, MsimMessage *msg, - const gchar *username, const gchar *uid_field_name, - const gchar *uid_before) -{ - PurpleBuddy *buddy; - guint uid; - gboolean rc; - - g_return_val_if_fail(msg != NULL, FALSE); - - /* Store information for msim_postprocess_outgoing_cb(). */ - msg = msim_msg_append(msg, "_username", MSIM_TYPE_STRING, g_strdup(username)); - msg = msim_msg_append(msg, "_uid_field_name", MSIM_TYPE_STRING, g_strdup(uid_field_name)); - msg = msim_msg_append(msg, "_uid_before", MSIM_TYPE_STRING, g_strdup(uid_before)); - - /* First, try the most obvious. If numeric userid is given, use that directly. */ - if (msim_is_userid(username)) { - uid = atol(username); - } else { - /* Next, see if on buddy list and know uid. */ - buddy = purple_find_buddy(session->account, username); - if (buddy) { - uid = purple_blist_node_get_int(PURPLE_BLIST_NODE(buddy), "UserID"); - } else { - uid = 0; - } - - if (!buddy || !uid) { - /* Don't have uid offhand - need to ask for it, and wait until hear back before sending. */ - purple_debug_info("msim", ">>> msim_postprocess_outgoing: couldn't find username %s in blist\n", - username ? username : "(NULL)"); - msim_lookup_user(session, username, msim_postprocess_outgoing_cb, msim_msg_clone(msg)); - return TRUE; /* not sure of status yet - haven't sent! */ - } - } - - /* Already have uid, postprocess and send msg immediately. */ - purple_debug_info("msim", "msim_postprocess_outgoing: found username %s has uid %d\n", - username ? username : "(NULL)", uid); - - msg = msim_do_postprocessing(msg, uid_before, uid_field_name, uid); - - rc = msim_msg_send(session, msg); - - /* TODO: free - * msim_msg_free(msg); - */ - - return rc; -} - -/** - * Send a buddy message of a given type. - * - * @param session - * @param who Username to send message to. - * @param text Message text to send. Not freed; will be copied. - * @param type A MSIM_BM_* constant. - * - * @return TRUE if success, FALSE if fail. - * - * Buddy messages ('bm') include instant messages, action messages, status messages, etc. - */ -gboolean -msim_send_bm(MsimSession *session, const gchar *who, const gchar *text, - int type) -{ - gboolean rc; - MsimMessage *msg; - const gchar *from_username; - - g_return_val_if_fail(who != NULL, FALSE); - g_return_val_if_fail(text != NULL, FALSE); - - from_username = session->account->username; - - g_return_val_if_fail(from_username != NULL, FALSE); - - purple_debug_info("msim", "sending %d message from %s to %s: %s\n", - type, from_username, who, text); - - msg = msim_msg_new( - "bm", MSIM_TYPE_INTEGER, GUINT_TO_POINTER(type), - "sesskey", MSIM_TYPE_INTEGER, GUINT_TO_POINTER(session->sesskey), - /* 't' will be inserted here */ - "cv", MSIM_TYPE_INTEGER, GUINT_TO_POINTER(MSIM_CLIENT_VERSION), - "msg", MSIM_TYPE_STRING, g_strdup(text), - NULL); - - rc = msim_postprocess_outgoing(session, msg, who, "t", "cv"); - - msim_msg_free(msg); - - return rc; -} - -/** - * Lookup a username by userid, from buddy list. - * - * @param wanted_uid - * - * @return Username of wanted_uid, if on blist, or NULL. - * This is a static string, so don't free it. Copy it if needed. - * - */ -static const gchar * -msim_uid2username_from_blist(PurpleAccount *account, guint wanted_uid) -{ - GSList *buddies, *cur; - const gchar *ret; - - buddies = purple_find_buddies(account, NULL); - - if (!buddies) - { - purple_debug_info("msim", "msim_uid2username_from_blist: no buddies?\n"); - return NULL; - } - - ret = NULL; - - for (cur = buddies; cur != NULL; cur = g_slist_next(cur)) - { - PurpleBuddy *buddy; - guint uid; - const gchar *name; - - /* See finch/gnthistory.c */ - buddy = cur->data; - - uid = purple_blist_node_get_int(PURPLE_BLIST_NODE(buddy), "UserID"); - name = purple_buddy_get_name(buddy); - - if (uid == wanted_uid) - { - ret = name; - break; - } - } - - g_slist_free(buddies); - return ret; -} - -/** - * Setup a callback, to be called when a reply is received with the returned rid. - * - * @param cb The callback, an MSIM_USER_LOOKUP_CB. - * @param data Arbitrary user data to be passed to callback (probably an MsimMessage *). - * - * @return The request/reply ID, used to link replies with requests, or -1. - * Put the rid in your request, 'rid' field. - * - * TODO: Make more generic and more specific: - * 1) MSIM_USER_LOOKUP_CB - make it for PERSIST_REPLY, not just user lookup - * 2) data - make it an MsimMessage? - */ -guint -msim_new_reply_callback(MsimSession *session, MSIM_USER_LOOKUP_CB cb, - gpointer data) -{ - guint rid; - - rid = session->next_rid++; - - g_hash_table_insert(session->user_lookup_cb, GUINT_TO_POINTER(rid), cb); - g_hash_table_insert(session->user_lookup_cb_data, GUINT_TO_POINTER(rid), data); - - return rid; -} - -/** - * Return the icon name for a buddy and account. - * - * @param acct The account to find the icon for, or NULL for protocol icon. - * @param buddy The buddy to find the icon for, or NULL for the account icon. - * - * @return The base icon name string. - */ -static const gchar * -msim_list_icon(PurpleAccount *acct, PurpleBuddy *buddy) -{ - /* Use a MySpace icon submitted by hbons at - * http://developer.pidgin.im/wiki/MySpaceIM. */ - return "myspace"; -} - -/** - * Obtain the status text for a buddy. - * - * @param buddy The buddy to obtain status text for. - * - * @return Status text, or NULL if error. Caller g_free()'s. - */ -static char * -msim_status_text(PurpleBuddy *buddy) -{ - MsimUser *user; - const gchar *display_name = NULL, *headline = NULL; - PurpleAccount *account; - - g_return_val_if_fail(buddy != NULL, NULL); - - account = purple_buddy_get_account(buddy); - - user = msim_get_user_from_buddy(buddy, FALSE); - if (user != NULL) { - /* Retrieve display name and/or headline, depending on user preference. */ - if (purple_account_get_bool(account, "show_headline", TRUE)) { - headline = user->headline; - } - - if (purple_account_get_bool(account, "show_display_name", FALSE)) { - display_name = user->display_name; - } - } - - /* Return appropriate combination of display name and/or headline, or neither. */ - - if (display_name && headline) { - return g_strconcat(display_name, " ", headline, NULL); - } else if (display_name) { - return g_strdup(display_name); - } else if (headline) { - return g_strdup(headline); - } - - return NULL; -} - -/** - * Obtain the tooltip text for a buddy. - * - * @param buddy Buddy to obtain tooltip text on. - * @param user_info Variable modified to have the tooltip text. - * @param full TRUE if should obtain full tooltip text. - */ -static void -msim_tooltip_text(PurpleBuddy *buddy, PurpleNotifyUserInfo *user_info, - gboolean full) -{ - MsimUser *user; - - g_return_if_fail(buddy != NULL); - g_return_if_fail(user_info != NULL); - - user = msim_get_user_from_buddy(buddy, TRUE); - - if (PURPLE_BUDDY_IS_ONLINE(buddy)) { - MsimSession *session; - PurpleAccount *account = purple_buddy_get_account(buddy); - PurpleConnection *gc = purple_account_get_connection(account); - - session = (MsimSession *)gc->proto_data; - - /* TODO: if (full), do something different? */ - - /* TODO: request information? have to figure out how to do - * the asynchronous lookup like oscar does (tooltip shows - * 'retrieving...' if not yet available, then changes when it is). - * - * Right now, only show what we have on hand. - */ - - /* Show abbreviated user info. */ - msim_append_user_info(session, user_info, user, FALSE); - } -} - -/** - * Get possible user status types. Based on mockprpl. - * - * @return GList of status types. - */ -static GList * -msim_status_types(PurpleAccount *acct) -{ - GList *types; - PurpleStatusType *status; - - purple_debug_info("myspace", "returning status types\n"); - - types = NULL; - - /* Statuses are almost all the same. Define a macro to reduce code repetition. */ -#define _MSIM_ADD_NEW_STATUS(prim) status = \ - purple_status_type_new_with_attrs( \ - prim, /* PurpleStatusPrimitive */ \ - NULL, /* id - use default */ \ - NULL, /* name - use default */ \ - TRUE, /* saveable */ \ - TRUE, /* user_settable */ \ - FALSE, /* not independent */ \ - \ - /* Attributes - each status can have a message. */ \ - "message", \ - _("Message"), \ - purple_value_new(PURPLE_TYPE_STRING), \ - NULL); \ - \ - \ - types = g_list_append(types, status) - - - _MSIM_ADD_NEW_STATUS(PURPLE_STATUS_AVAILABLE); - _MSIM_ADD_NEW_STATUS(PURPLE_STATUS_AWAY); - _MSIM_ADD_NEW_STATUS(PURPLE_STATUS_OFFLINE); - _MSIM_ADD_NEW_STATUS(PURPLE_STATUS_INVISIBLE); - - /* Except tune status is different... */ - status = purple_status_type_new_with_attrs( - PURPLE_STATUS_TUNE, /* primitive */ - "tune", /* ID */ - NULL, /* name - use default */ - FALSE, /* saveable */ - TRUE, /* should be user_settable some day */ - TRUE, /* independent */ - - PURPLE_TUNE_ARTIST, _("Tune Artist"), purple_value_new(PURPLE_TYPE_STRING), - PURPLE_TUNE_TITLE, _("Tune Title"), purple_value_new(PURPLE_TYPE_STRING), - NULL); - - types = g_list_append(types, status); - - return types; -} - -/* - * TODO: This define is stolen from oscar.h. - * It's also in yahoo.h. - * It should be in libpurple/util.c - */ -#define msim_put32(buf, data) ( \ - (*((buf)) = (unsigned char)((data)>>24)&0xff), \ - (*((buf)+1) = (unsigned char)((data)>>16)&0xff), \ - (*((buf)+2) = (unsigned char)((data)>>8)&0xff), \ - (*((buf)+3) = (unsigned char)(data)&0xff), \ - 4) - -/** - * Compute the base64'd login challenge response based on username, password, nonce, and IPs. - * - * @param nonce The base64 encoded nonce ('nc') field from the server. - * @param email User's email address (used as login name). - * @param password User's cleartext password. - * @param response_len Will be written with response length. - * - * @return Binary login challenge response, ready to send to the server. - * Must be g_free()'d when finished. NULL if error. - */ -static gchar * -msim_compute_login_response(const gchar nonce[2 * NONCE_SIZE], - const gchar *email, const gchar *password, guint *response_len) -{ - PurpleCipherContext *key_context; - PurpleCipher *sha1; - PurpleCipherContext *rc4; - - guchar hash_pw[HASH_SIZE]; - guchar key[HASH_SIZE]; - gchar *password_truncated, *password_utf16le, *password_utf8_lc; - GString *data; - guchar *data_out; - size_t data_out_len; - gsize conv_bytes_read, conv_bytes_written; - GError *conv_error; -#ifdef MSIM_DEBUG_LOGIN_CHALLENGE - int i; -#endif - - g_return_val_if_fail(nonce != NULL, NULL); - g_return_val_if_fail(email != NULL, NULL); - g_return_val_if_fail(password != NULL, NULL); - g_return_val_if_fail(response_len != NULL, NULL); - - /* - * Truncate password to 10 characters. Their "change password" - * web page doesn't let you enter more than 10 characters, but you - * can enter more than 10 when logging in on myspace.com and they - * truncate it. - */ - password_truncated = g_strndup(password, 10); - - /* Convert password to lowercase (required for passwords containing - * uppercase characters). MySpace passwords are lowercase, - * see ticket #2066. */ - password_utf8_lc = g_utf8_strdown(password_truncated, -1); - g_free(password_truncated); - - /* Convert ASCII password to UTF16 little endian */ - purple_debug_info("msim", "converting password to UTF-16LE\n"); - conv_error = NULL; - password_utf16le = g_convert(password_utf8_lc, -1, "UTF-16LE", "UTF-8", - &conv_bytes_read, &conv_bytes_written, &conv_error); - g_free(password_utf8_lc); - - if (conv_error != NULL) { - purple_debug_error("msim", - "g_convert password UTF8->UTF16LE failed: %s", - conv_error->message); - g_error_free(conv_error); - return NULL; - } - - /* Compute password hash */ - purple_cipher_digest_region("sha1", (guchar *)password_utf16le, - conv_bytes_written, sizeof(hash_pw), hash_pw, NULL); - g_free(password_utf16le); - -#ifdef MSIM_DEBUG_LOGIN_CHALLENGE - purple_debug_info("msim", "pwhash = "); - for (i = 0; i < sizeof(hash_pw); i++) - purple_debug_info("msim", "%.2x ", hash_pw[i]); - purple_debug_info("msim", "\n"); -#endif - - /* key = sha1(sha1(pw) + nonce2) */ - sha1 = purple_ciphers_find_cipher("sha1"); - key_context = purple_cipher_context_new(sha1, NULL); - purple_cipher_context_append(key_context, hash_pw, HASH_SIZE); - purple_cipher_context_append(key_context, (guchar *)(nonce + NONCE_SIZE), NONCE_SIZE); - purple_cipher_context_digest(key_context, sizeof(key), key, NULL); - purple_cipher_context_destroy(key_context); - -#ifdef MSIM_DEBUG_LOGIN_CHALLENGE - purple_debug_info("msim", "key = "); - for (i = 0; i < sizeof(key); i++) { - purple_debug_info("msim", "%.2x ", key[i]); - } - purple_debug_info("msim", "\n"); -#endif - - rc4 = purple_cipher_context_new_by_name("rc4", NULL); - - /* Note: 'key' variable is 0x14 bytes (from SHA-1 hash), - * but only first 0x10 used for the RC4 key. */ - purple_cipher_context_set_option(rc4, "key_len", (gpointer)0x10); - purple_cipher_context_set_key(rc4, key); - - /* rc4 encrypt: - * nonce1+email+IP list */ - - data = g_string_new(NULL); - g_string_append_len(data, nonce, NONCE_SIZE); - - /* Include the null terminator */ - g_string_append_len(data, email, strlen(email) + 1); - - while (data->len % 4 != 0) - g_string_append_c(data, 0xfb); - -#ifdef SEND_OUR_IP_ADDRESSES - /* TODO: Obtain IPs of network interfaces instead of using this hardcoded value */ - g_string_set_size(data, data->len + 4); - (void)msim_put32(data->str + data->len - 4, MSIM_LOGIN_IP_LIST_LEN); - g_string_append_len(data, MSIM_LOGIN_IP_LIST, MSIM_LOGIN_IP_LIST_LEN); -#else - g_string_set_size(data, data->len + 4); - (void)msim_put32(data->str + data->len - 4, 0); -#endif /* !SEND_OUR_IP_ADDRESSES */ - - data_out = g_new0(guchar, data->len); - - purple_cipher_context_encrypt(rc4, (const guchar *)data->str, - data->len, data_out, &data_out_len); - purple_cipher_context_destroy(rc4); - - if (data_out_len != data->len) { - purple_debug_info("msim", "msim_compute_login_response: " - "data length mismatch: %" G_GSIZE_FORMAT " != %" - G_GSIZE_FORMAT "\n", data_out_len, data->len); - } - - g_string_free(data, TRUE); - -#ifdef MSIM_DEBUG_LOGIN_CHALLENGE - purple_debug_info("msim", "response=<%s>\n", data_out); -#endif - - *response_len = data_out_len; - - return (gchar *)data_out; -} - -/** - * Process a login challenge, sending a response. - * - * @param session - * @param msg Login challenge message. - * - * @return TRUE if successful, FALSE if not - */ -static gboolean -msim_login_challenge(MsimSession *session, MsimMessage *msg) -{ - PurpleAccount *account; - gchar *response; - guint response_len; - gchar *nc; - gsize nc_len; - gboolean ret; - - g_return_val_if_fail(msg != NULL, FALSE); - - g_return_val_if_fail(msim_msg_get_binary(msg, "nc", &nc, &nc_len), FALSE); - - account = session->account; - - g_return_val_if_fail(account != NULL, FALSE); - - purple_connection_update_progress(session->gc, _("Reading challenge"), 1, 4); - - purple_debug_info("msim", "nc is %" G_GSIZE_FORMAT - " bytes, decoded\n", nc_len); - - if (nc_len != MSIM_AUTH_CHALLENGE_LENGTH) { - purple_debug_info("msim", "bad nc length: %" G_GSIZE_MODIFIER - "x != 0x%x\n", nc_len, MSIM_AUTH_CHALLENGE_LENGTH); - purple_connection_error_reason (session->gc, - PURPLE_CONNECTION_ERROR_NETWORK_ERROR, - _("Unexpected challenge length from server")); - return FALSE; - } - - purple_connection_update_progress(session->gc, _("Logging in"), 2, 4); - - response_len = 0; - response = msim_compute_login_response(nc, account->username, account->password, &response_len); - - g_free(nc); - - ret = msim_send(session, - "login2", MSIM_TYPE_INTEGER, MSIM_AUTH_ALGORITHM, - /* This is actually user's email address. */ - "username", MSIM_TYPE_STRING, g_strdup(account->username), - /* GString will be freed in msim_msg_free() in msim_send(). */ - "response", MSIM_TYPE_BINARY, g_string_new_len(response, response_len), - "clientver", MSIM_TYPE_INTEGER, MSIM_CLIENT_VERSION, - "langid", MSIM_TYPE_INTEGER, MSIM_LANGUAGE_ID_ENGLISH, - "imlang", MSIM_TYPE_STRING, g_strdup(MSIM_LANGUAGE_NAME_ENGLISH), - "reconn", MSIM_TYPE_INTEGER, 0, - "status", MSIM_TYPE_INTEGER, 100, - "id", MSIM_TYPE_INTEGER, 1, - NULL); - - g_free(response); - - return ret; -} - -/** - * Process unrecognized information. - * - * @param session - * @param msg An MsimMessage that was unrecognized, or NULL. - * @param note Information on what was unrecognized, or NULL. - */ -void -msim_unrecognized(MsimSession *session, MsimMessage *msg, gchar *note) -{ - /* TODO: Some more context, outwardly equivalent to a backtrace, - * for helping figure out what this msg is for. What was going on? - * But not too much information so that a user - * posting this dump reveals confidential information. - */ - - /* TODO: dump unknown msgs to file, so user can send them to me - * if they wish, to help add support for new messages (inspired - * by Alexandr Shutko, who maintains OSCAR protocol documentation). - * - * Filed enhancement ticket for libpurple as #4688. - */ - - purple_debug_info("msim", "Unrecognized data on account for %s\n", - (session && session->account && session->account->username) ? - session->account->username : "(NULL)"); - if (note) { - purple_debug_info("msim", "(Note: %s)\n", note); - } - - if (msg) { - msim_msg_dump("Unrecognized message dump: %s\n", msg); - } -} - -/** Called when the session key arrives to check whether the user - * has a username, and set one if desired. */ -static gboolean -msim_is_username_set(MsimSession *session, MsimMessage *msg) -{ - g_return_val_if_fail(msg != NULL, FALSE); - g_return_val_if_fail(session->gc != NULL, FALSE); - - session->sesskey = msim_msg_get_integer(msg, "sesskey"); - purple_debug_info("msim", "SESSKEY=<%d>\n", session->sesskey); - - /* What is proof? Used to be uid, but now is 52 base64'd bytes... */ - - /* Comes with: proof,profileid,userid,uniquenick -- all same values - * 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, - "Profile ID didn't match user ID, don't know why"); - } - - /* We now know are our own username, only after we're logged in.. - * which is weird, but happens because you login with your email - * address and not username. Will be freed in msim_session_destroy(). */ - session->username = msim_msg_get_string(msg, "uniquenick"); - - /* If user lacks a username, help them get one. */ - if (msim_msg_get_integer(msg, "uniquenick") == session->userid) { - purple_debug_info("msim_is_username_set", "no username is set\n"); - purple_request_yes_no(session->gc, - _("MySpaceIM - No Username Set"), - _("You appear to have no MySpace username."), - _("Would you like to set one now? (Note: THIS CANNOT BE CHANGED!)"), - 0, - session->account, - NULL, - NULL, - session->gc, - G_CALLBACK(msim_set_username_cb), - G_CALLBACK(msim_do_not_set_username_cb)); - purple_debug_info("msim_is_username_set","'username not set' alert prompted\n"); - return FALSE; - } - return TRUE; -} - -#ifdef MSIM_USE_KEEPALIVE -/** - * Check if the connection is still alive, based on last communication. - */ -static gboolean -msim_check_alive(gpointer data) -{ - MsimSession *session; - time_t delta; - - session = (MsimSession *)data; - - delta = time(NULL) - session->last_comm; - - /* purple_debug_info("msim", "msim_check_alive: delta=%d\n", delta); */ - if (delta >= MSIM_KEEPALIVE_INTERVAL) { - purple_debug_info("msim", - "msim_check_alive: %zu > interval of %d, presumed dead\n", - delta, MSIM_KEEPALIVE_INTERVAL); - purple_connection_error_reason(session->gc, - PURPLE_CONNECTION_ERROR_NETWORK_ERROR, - _("Lost connection with server")); - - return FALSE; - } - - return TRUE; -} -#endif - -/** - * Handle mail reply checks. - */ -static void -msim_check_inbox_cb(MsimSession *session, const MsimMessage *reply, gpointer data) -{ - MsimMessage *body; - guint i, n; - /* Information for each new inbox message type. */ - static struct - { - const gchar *key; - guint bit; - const gchar *url; - const gchar *text; - } message_types[] = { - { "Mail", MSIM_INBOX_MAIL, "http://messaging.myspace.com/index.cfm?fuseaction=mail.inbox", NULL }, - { "BlogComment", MSIM_INBOX_BLOG_COMMENT, "http://blog.myspace.com/index.cfm?fuseaction=blog", NULL }, - { "ProfileComment", MSIM_INBOX_PROFILE_COMMENT, "http://home.myspace.com/index.cfm?fuseaction=user", NULL }, - { "FriendRequest", MSIM_INBOX_FRIEND_REQUEST, "http://messaging.myspace.com/index.cfm?fuseaction=mail.friendRequests", NULL }, - { "PictureComment", MSIM_INBOX_PICTURE_COMMENT, "http://home.myspace.com/index.cfm?fuseaction=user", NULL } - }; - const gchar *froms[G_N_ELEMENTS(message_types) + 1] = { "" }, - *tos[G_N_ELEMENTS(message_types) + 1] = { "" }, - *urls[G_N_ELEMENTS(message_types) + 1] = { "" }, - *subjects[G_N_ELEMENTS(message_types) + 1] = { "" }; - - g_return_if_fail(reply != NULL); - - /* Can't write _()'d strings in array initializers. Workaround. */ - /* khc: then use N_() in the array initializer and use _() when they are - used */ - message_types[0].text = _("New mail messages"); - message_types[1].text = _("New blog comments"); - message_types[2].text = _("New profile comments"); - message_types[3].text = _("New friend requests!"); - message_types[4].text = _("New picture comments"); - - body = msim_msg_get_dictionary(reply, "body"); - - if (body == NULL) - return; - - n = 0; - - for (i = 0; i < G_N_ELEMENTS(message_types); ++i) { - const gchar *key; - guint bit; - - key = message_types[i].key; - bit = message_types[i].bit; - - if (msim_msg_get(body, key)) { - /* Notify only on when _changes_ from no mail -> has mail - * (edge triggered) */ - if (!(session->inbox_status & bit)) { - purple_debug_info("msim", "msim_check_inbox_cb: got %s, at %d\n", - key ? key : "(NULL)", n); - - subjects[n] = message_types[i].text; - froms[n] = _("MySpace"); - tos[n] = session->username; - /* TODO: append token, web challenge, so automatically logs in. - * Would also need to free strings because they won't be static - */ - urls[n] = message_types[i].url; - - ++n; - } else { - purple_debug_info("msim", - "msim_check_inbox_cb: already notified of %s\n", - key ? key : "(NULL)"); - } - - session->inbox_status |= bit; - } - } - - if (n) { - purple_debug_info("msim", - "msim_check_inbox_cb: notifying of %d\n", n); - - /* TODO: free strings with callback _if_ change to dynamic (w/ token) */ - purple_notify_emails(session->gc, /* handle */ - n, /* count */ - TRUE, /* detailed */ - subjects, froms, tos, urls, - NULL, /* PurpleNotifyCloseCallback cb */ - NULL); /* gpointer user_data */ - - } - - msim_msg_free(body); -} - -/** - * Send request to check if there is new mail. - */ -static gboolean -msim_check_inbox(gpointer data) -{ - MsimSession *session; - - session = (MsimSession *)data; - - purple_debug_info("msim", "msim_check_inbox: checking mail\n"); - g_return_val_if_fail(msim_send(session, - "persist", MSIM_TYPE_INTEGER, 1, - "sesskey", MSIM_TYPE_INTEGER, session->sesskey, - "cmd", MSIM_TYPE_INTEGER, MSIM_CMD_GET, - "dsn", MSIM_TYPE_INTEGER, MG_CHECK_MAIL_DSN, - "lid", MSIM_TYPE_INTEGER, MG_CHECK_MAIL_LID, - "uid", MSIM_TYPE_INTEGER, session->userid, - "rid", MSIM_TYPE_INTEGER, - msim_new_reply_callback(session, msim_check_inbox_cb, NULL), - "body", MSIM_TYPE_STRING, g_strdup(""), - NULL), TRUE); - - /* Always return true, so that we keep checking for mail. */ - return TRUE; -} - -/** - * Add contact from server to buddy list, after looking up username. - * Callback from msim_add_contact_from_server(). - * - * @param data An MsimMessage * of the contact information. Will be freed. - */ -static void -msim_add_contact_from_server_cb(MsimSession *session, const MsimMessage *user_lookup_info, gpointer data) -{ - MsimMessage *contact_info, *user_lookup_info_body; - PurpleGroup *group; - PurpleBuddy *buddy; - MsimUser *user; - gchar *username, *group_name, *display_name; - guint uid, visibility; - - contact_info = (MsimMessage *)data; - purple_debug_info("msim_add_contact_from_server_cb", "contact_info addr=%p\n", contact_info); - uid = msim_msg_get_integer(contact_info, "ContactID"); - - if (!user_lookup_info) { - username = g_strdup(msim_uid2username_from_blist(session->account, uid)); - display_name = NULL; - g_return_if_fail(username != NULL); - } else { - user_lookup_info_body = msim_msg_get_dictionary(user_lookup_info, "body"); - username = msim_msg_get_string(user_lookup_info_body, "UserName"); - display_name = msim_msg_get_string(user_lookup_info_body, "DisplayName"); - msim_msg_free(user_lookup_info_body); - g_return_if_fail(username != NULL); - } - - purple_debug_info("msim_add_contact_from_server_cb", - "*** about to add/update username=%s\n", username); - - /* 1. Creates a new group, or gets existing group if it exists (or so - * the documentation claims). */ - group_name = msim_msg_get_string(contact_info, "GroupName"); - if (!group_name || (*group_name == '\0')) { - g_free(group_name); - group_name = g_strdup(_("IM Friends")); - purple_debug_info("myspace", "No GroupName specified, defaulting to '%s'.\n", group_name); - } - group = purple_find_group(group_name); - if (!group) { - group = purple_group_new(group_name); - /* Add group to beginning. See #2752. */ - purple_blist_add_group(group, NULL); - } - g_free(group_name); - - visibility = msim_msg_get_integer(contact_info, "Visibility"); - if (visibility == 2) { - /* This buddy is blocked (and therefore not on our buddy list */ - purple_privacy_deny_add(session->account, username, TRUE); - msim_msg_free(contact_info); - g_free(username); - g_free(display_name); - return; - } - - /* 2. Get or create buddy */ - buddy = purple_find_buddy(session->account, username); - if (!buddy) { - purple_debug_info("msim_add_contact_from_server_cb", - "creating new buddy: %s\n", username); - buddy = purple_buddy_new(session->account, username, NULL); - } - - /* TODO: use 'Position' in contact_info to take into account where buddy is */ - purple_blist_add_buddy(buddy, NULL, group, NULL /* insertion point */); - - if (strtoul(username, NULL, 10) == uid) { - /* - * This user has not set their username! Set their server - * alias to their display name so that we don't see a bunch - * of numbers in the buddy list. - */ - if (display_name != NULL) { - purple_blist_node_set_string(PURPLE_BLIST_NODE(buddy), "DisplayName", display_name); - serv_got_alias(session->gc, username, display_name); - } else { - serv_got_alias(session->gc, username, - purple_blist_node_get_string(PURPLE_BLIST_NODE(buddy), "DisplayName")); - } - } - g_free(display_name); - - /* 3. Update buddy information */ - user = msim_get_user_from_buddy(buddy, TRUE); - - user->id = uid; - /* Keep track of the user ID across sessions */ - purple_blist_node_set_int(PURPLE_BLIST_NODE(buddy), "UserID", uid); - - /* Stores a few fields in the MsimUser, relevant to the buddy itself. - * AvatarURL, Headline, ContactID. */ - msim_store_user_info(session, contact_info, NULL); - - /* TODO: other fields, store in 'user' */ - msim_msg_free(contact_info); - - g_free(username); -} - -/** - * Add first ContactID in contact_info to buddy's list. Used to add - * server-side buddies to client-side list. - * - * @return TRUE if added. - */ -static gboolean -msim_add_contact_from_server(MsimSession *session, MsimMessage *contact_info) -{ - guint uid; - const gchar *username; - - uid = msim_msg_get_integer(contact_info, "ContactID"); - g_return_val_if_fail(uid != 0, FALSE); - - /* Lookup the username, since NickName and IMName is unreliable */ - username = msim_uid2username_from_blist(session->account, uid); - if (!username) { - gchar *uid_str; - - uid_str = g_strdup_printf("%d", uid); - purple_debug_info("msim_add_contact_from_server", - "contact_info addr=%p\n", contact_info); - msim_lookup_user(session, uid_str, msim_add_contact_from_server_cb, (gpointer)msim_msg_clone(contact_info)); - g_free(uid_str); - } else { - msim_add_contact_from_server_cb(session, NULL, (gpointer)msim_msg_clone(contact_info)); - } - - /* Say that the contact was added, even if we're still looking up - * their username. */ - return TRUE; -} - -/** - * Called when contact list is received from server. - */ -static void -msim_got_contact_list(MsimSession *session, const MsimMessage *reply, gpointer user_data) -{ - MsimMessage *body, *body_node; - gchar *msg; - guint buddy_count; - - body = msim_msg_get_dictionary(reply, "body"); - - buddy_count = 0; - - for (body_node = body; - body_node != NULL; - body_node = msim_msg_get_next_element_node(body_node)) - { - MsimMessageElement *elem; - - elem = (MsimMessageElement *)body_node->data; - - if (g_str_equal(elem->name, "ContactID")) - { - /* Will look for first contact in body_node */ - if (msim_add_contact_from_server(session, body_node)) { - ++buddy_count; - } - } - } - - switch (GPOINTER_TO_UINT(user_data)) { - case MSIM_CONTACT_LIST_IMPORT_ALL_FRIENDS: - msg = g_strdup_printf(ngettext("%d buddy was added or updated from the server (including buddies already on the server-side list)", - "%d buddies were added or updated from the server (including buddies already on the server-side list)", - buddy_count), - buddy_count); - purple_notify_info(session->account, _("Add contacts from server"), msg, NULL); - g_free(msg); - break; - - case MSIM_CONTACT_LIST_IMPORT_TOP_FRIENDS: - /* TODO */ - break; - - case MSIM_CONTACT_LIST_INITIAL_FRIENDS: - /* The session is now set up, ready to be connected. This emits the - * signedOn signal, so clients can now do anything with msimprpl, and - * we're ready for it (session key, userid, username all setup). */ - purple_connection_update_progress(session->gc, _("Connected"), 3, 4); - purple_connection_set_state(session->gc, PURPLE_CONNECTED); - break; - } - - msim_msg_free(body); -} - -/** - * Get contact list, calling msim_got_contact_list() with - * what_to_do_after as user_data gpointer. - * - * @param what_to_do_after should be one of the MSIM_CONTACT_LIST_* #defines. - */ -static gboolean -msim_get_contact_list(MsimSession *session, int what_to_do_after) -{ - return msim_send(session, - "persist", MSIM_TYPE_INTEGER, 1, - "sesskey", MSIM_TYPE_INTEGER, session->sesskey, - "cmd", MSIM_TYPE_INTEGER, MSIM_CMD_GET, - "dsn", MSIM_TYPE_INTEGER, MG_LIST_ALL_CONTACTS_DSN, - "lid", MSIM_TYPE_INTEGER, MG_LIST_ALL_CONTACTS_LID, - "uid", MSIM_TYPE_INTEGER, session->userid, - "rid", MSIM_TYPE_INTEGER, - msim_new_reply_callback(session, msim_got_contact_list, GUINT_TO_POINTER(what_to_do_after)), - "body", MSIM_TYPE_STRING, g_strdup(""), - NULL); -} - -/** Called after username is set, if necessary and we're open for business. */ -gboolean msim_we_are_logged_on(MsimSession *session) -{ - MsimMessage *body; - - /* Set display name to username (otherwise will show email address) */ - purple_connection_set_display_name(session->gc, session->username); - - body = msim_msg_new( - "UserID", MSIM_TYPE_INTEGER, session->userid, - NULL); - - /* Request IM info about ourself. */ - msim_send(session, - "persist", MSIM_TYPE_INTEGER, 1, - "sesskey", MSIM_TYPE_INTEGER, session->sesskey, - "cmd", MSIM_TYPE_INTEGER, MSIM_CMD_GET, - "dsn", MSIM_TYPE_INTEGER, MG_OWN_MYSPACE_INFO_DSN, - "lid", MSIM_TYPE_INTEGER, MG_OWN_MYSPACE_INFO_LID, - "rid", MSIM_TYPE_INTEGER, session->next_rid++, - "UserID", MSIM_TYPE_INTEGER, session->userid, - "body", MSIM_TYPE_DICTIONARY, body, - NULL); - - /* Request MySpace info about ourself. */ - msim_send(session, - "persist", MSIM_TYPE_INTEGER, 1, - "sesskey", MSIM_TYPE_INTEGER, session->sesskey, - "cmd", MSIM_TYPE_INTEGER, MSIM_CMD_GET, - "dsn", MSIM_TYPE_INTEGER, MG_OWN_IM_INFO_DSN, - "lid", MSIM_TYPE_INTEGER, MG_OWN_IM_INFO_LID, - "rid", MSIM_TYPE_INTEGER, session->next_rid++, - "body", MSIM_TYPE_STRING, g_strdup(""), - NULL); - - /* TODO: set options (persist cmd=514,dsn=1,lid=10) */ - /* TODO: set blocklist */ - - /* Notify servers of our current status. */ - purple_debug_info("msim", "msim_we_are_logged_on: notifying servers of status\n"); - msim_set_status(session->account, - purple_account_get_active_status(session->account)); - - /* TODO: setinfo */ - /* - body = msim_msg_new( - "TotalFriends", MSIM_TYPE_INTEGER, 666, - NULL); - msim_send(session, - "setinfo", MSIM_TYPE_BOOLEAN, TRUE, - "sesskey", MSIM_TYPE_INTEGER, session->sesskey, - "info", MSIM_TYPE_DICTIONARY, body, - NULL); - */ - - /* Disable due to problems with timeouts. TODO: fix. */ -#ifdef MSIM_USE_KEEPALIVE - purple_timeout_add_seconds(MSIM_KEEPALIVE_INTERVAL_CHECK, - (GSourceFunc)msim_check_alive, session); -#endif - - /* Check mail if they want to. */ - if (purple_account_get_check_mail(session->account)) { - session->inbox_handle = purple_timeout_add(MSIM_MAIL_INTERVAL_CHECK, - (GSourceFunc)msim_check_inbox, session); - msim_check_inbox(session); - } - - msim_get_contact_list(session, MSIM_CONTACT_LIST_INITIAL_FRIENDS); - - return TRUE; -} - -/** - * Record the client version in the buddy list, from an incoming message. - */ -static gboolean -msim_incoming_bm_record_cv(MsimSession *session, MsimMessage *msg) -{ - gchar *username, *cv; - gboolean ret; - MsimUser *user; - - username = msim_msg_get_string(msg, "_username"); - cv = msim_msg_get_string(msg, "cv"); - - g_return_val_if_fail(username != NULL, FALSE); - if (!cv) { - /* No client version to record, don't worry about it. */ - g_free(username); - return FALSE; - } - - user = msim_find_user(session, username); - - if (user) { - user->client_cv = atol(cv); - ret = TRUE; - } else { - ret = FALSE; - } - - g_free(username); - g_free(cv); - - return ret; -} - -#ifdef MSIM_SEND_CLIENT_VERSION -/** - * Send our client version to another unofficial client that understands it. - */ -static gboolean -msim_send_unofficial_client(MsimSession *session, gchar *username) -{ - gchar *our_info; - gboolean ret; - - our_info = g_strdup_printf("Libpurple %d.%d.%d - msimprpl %s", - PURPLE_MAJOR_VERSION, - PURPLE_MINOR_VERSION, - PURPLE_MICRO_VERSION, - MSIM_PRPL_VERSION_STRING); - - ret = msim_send_bm(session, username, our_info, MSIM_BM_UNOFFICIAL_CLIENT); - - return ret; -} -#endif -/** - * Process incoming status mood messages. - * - * @param session - * @param msg Status mood update message. Caller frees. - * - * @return TRUE if successful. - */ -static gboolean -msim_incoming_status_mood(MsimSession *session, MsimMessage *msg) { - /* TODO: I dont know too much about this yet, - * so until I see how the official client handles - * this and decide if libpurple should as well, - * well just say we used it - */ - gchar *ss; - ss = msim_msg_get_string(msg, "msg"); - purple_debug_info("msim", "Incoming Status Message: %s", ss ? ss : "(NULL)"); - g_free(ss); - return TRUE; -} - -/** - * Process incoming status messages. - * - * @param session - * @param msg Status update message. Caller frees. - * - * @return TRUE if successful. - */ -static gboolean -msim_incoming_status(MsimSession *session, MsimMessage *msg) -{ - MsimUser *user; - GList *list; - gchar *status_headline, *status_headline_escaped; - gint status_code, purple_status_code; - gchar *username; - gchar *unrecognized_msg; - - g_return_val_if_fail(msg != NULL, FALSE); - - /* Helpfully looked up by msim_incoming_resolve() for us. */ - username = msim_msg_get_string(msg, "_username"); - g_return_val_if_fail(username != NULL, FALSE); - - { - gchar *ss; - - ss = msim_msg_get_string(msg, "msg"); - purple_debug_info("msim", - "msim_status: updating status for <%s> to <%s>\n", - username, ss ? ss : "(NULL)"); - g_free(ss); - } - - /* Example fields: - * |s|0|ss|Offline - * |s|1|ss|:-)|ls||ip|0|p|0 - */ - list = msim_msg_get_list(msg, "msg"); - - 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 = msim_msg_get_string_from_element(g_list_nth_data(list, MSIM_STATUS_ORDINAL_HEADLINE)); - - /* Add buddy if not found. - * TODO: Could this be responsible for #3444? */ - user = msim_find_user(session, username); - if (!user) { - PurpleBuddy *buddy; - - purple_debug_info("msim", - "msim_status: making new buddy for %s\n", username); - buddy = purple_buddy_new(session->account, username, NULL); - purple_blist_add_buddy(buddy, NULL, NULL, NULL); - - user = msim_get_user_from_buddy(buddy, TRUE); - user->id = msim_msg_get_integer(msg, "f"); - - /* Keep track of the user ID across sessions */ - purple_blist_node_set_int(PURPLE_BLIST_NODE(buddy), "UserID", user->id); - - msim_store_user_info(session, msg, NULL); - } else { - purple_debug_info("msim", "msim_status: found buddy %s\n", username); - } - - if (status_headline && strcmp(status_headline, "") != 0) { - /* The status headline is plaintext, but libpurple treats it as HTML, - * so escape any HTML characters to their entity equivalents. */ - status_headline_escaped = g_markup_escape_text(status_headline, -1); - } else { - status_headline_escaped = NULL; - } - - g_free(status_headline); - - /* don't copy; let the MsimUser own the headline, memory-wise */ - g_free(user->headline); - user->headline = status_headline_escaped; - - /* Set user status */ - switch (status_code) { - case MSIM_STATUS_CODE_OFFLINE_OR_HIDDEN: - purple_status_code = PURPLE_STATUS_OFFLINE; - break; - - case MSIM_STATUS_CODE_ONLINE: - purple_status_code = PURPLE_STATUS_AVAILABLE; - break; - - case MSIM_STATUS_CODE_AWAY: - purple_status_code = PURPLE_STATUS_AWAY; - break; - - case MSIM_STATUS_CODE_IDLE: - /* Treat idle as an available status. */ - purple_status_code = PURPLE_STATUS_AVAILABLE; - break; - - default: - purple_debug_info("msim", "msim_incoming_status for %s, unknown status code %d, treating as available\n", - username, status_code); - purple_status_code = PURPLE_STATUS_AVAILABLE; - - unrecognized_msg = g_strdup_printf("msim_incoming_status, unrecognized status code: %d\n", - status_code); - msim_unrecognized(session, NULL, unrecognized_msg); - g_free(unrecognized_msg); - } - - purple_prpl_got_user_status(session->account, username, purple_primitive_get_id_from_type(purple_status_code), NULL); - - if (status_code == MSIM_STATUS_CODE_IDLE) { - purple_debug_info("msim", "msim_status: got idle: %s\n", username); - purple_prpl_got_user_idle(session->account, username, TRUE, 0); - } else { - /* All other statuses indicate going back to non-idle. */ - purple_prpl_got_user_idle(session->account, username, FALSE, 0); - } - -#ifdef MSIM_SEND_CLIENT_VERSION - if (status_code == MSIM_STATUS_CODE_ONLINE) { - /* Secretly whisper to unofficial clients our own version as they come online */ - msim_send_unofficial_client(session, username); - } -#endif - - if (status_code != MSIM_STATUS_CODE_OFFLINE_OR_HIDDEN) { - /* Get information when they come online. - * TODO: periodically refresh? - */ - purple_debug_info("msim_incoming_status", "%s came online, looking up\n", username); - msim_lookup_user(session, username, NULL, NULL); - } - - g_free(username); - msim_msg_list_free(list); - - return TRUE; -} - -/** - * Handle an incoming instant message. - * - * @param session The session - * @param msg Message from the server, containing 'f' (userid from) and 'msg'. - * Should also contain username in _username from preprocessing. - * - * @return TRUE if successful. - */ -static gboolean -msim_incoming_im(MsimSession *session, MsimMessage *msg, const gchar *username) -{ - gchar *msg_msim_markup, *msg_purple_markup; - gchar *userid; - time_t time_received; - PurpleConversation *conv; - - /* I know this isn't really a string... but we need it to be one for - * purple_find_conversation_with_account(). */ - userid = msim_msg_get_string(msg, "f"); - - purple_debug_info("msim_incoming_im", "UserID is %s", userid); - - if (msim_is_userid(username)) { - purple_debug_info("msim", "Ignoring message from spambot (%s) on account %s\n", - username, purple_account_get_username(session->account)); - return FALSE; - } - - /* See if a conversation with their UID already exists...*/ - conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, userid, session->account); - if (conv) { - /* Since the conversation exists... We need to normalize it */ - purple_conversation_set_name(conv, username); - } - - msg_msim_markup = msim_msg_get_string(msg, "msg"); - g_return_val_if_fail(msg_msim_markup != NULL, FALSE); - - msg_purple_markup = msim_markup_to_html(session, msg_msim_markup); - g_free(msg_msim_markup); - - time_received = msim_msg_get_integer(msg, "date"); - if (!time_received) { - purple_debug_info("msim_incoming_im", "date in message not set.\n"); - time_received = time(NULL); - } - - serv_got_im(session->gc, username, msg_purple_markup, PURPLE_MESSAGE_RECV, time_received); - - g_free(msg_purple_markup); - - return TRUE; -} - -/** - * Handle an incoming action message or an IM. - * - * @param session - * @param msg - * - * @return TRUE if successful. - */ -static gboolean -msim_incoming_action_or_im(MsimSession *session, MsimMessage *msg) -{ - gchar *msg_text, *username; - gboolean rc; - - g_return_val_if_fail(msg != NULL, FALSE); - - msg_text = msim_msg_get_string(msg, "msg"); - g_return_val_if_fail(msg_text != NULL, FALSE); - - username = msim_msg_get_string(msg, "_username"); - g_return_val_if_fail(username != NULL, FALSE); - - purple_debug_info("msim", - "msim_incoming_action_or_im: action <%s> from <%s>\n", - msg_text, username); - - if (g_str_equal(msg_text, "%typing%")) { - serv_got_typing(session->gc, username, 0, PURPLE_TYPING); - rc = TRUE; - } else if (g_str_equal(msg_text, "%stoptyping%")) { - serv_got_typing_stopped(session->gc, username); - rc = TRUE; - } else if (strstr(msg_text, "!!!ZAP_SEND!!!=RTE_BTN_ZAPS_")) { - rc = msim_incoming_zap(session, msg); - } else if (strstr(msg_text, "!!!GroupCount=")) { - /* TODO: support group chats. I think the number in msg_text has - * something to do with the 'gid' field. */ - purple_debug_info("msim", - "msim_incoming_action_or_im: " - "TODO: implement #4691, group chats: %s\n", msg_text); - - rc = TRUE; - } else if (strstr(msg_text, "!!!Offline=")) { - /* TODO: support group chats. This one might mean a user - * went offline or exited the chat. */ - purple_debug_info("msim", "msim_incoming_action_or_im: " - "TODO: implement #4691, group chats: %s\n", msg_text); - - rc = TRUE; - } else if (msim_msg_get_integer(msg, "aid") != 0) { - purple_debug_info("msim", "TODO: implement #4691, group chat from %d on %d: %s\n", - msim_msg_get_integer(msg, "aid"), - msim_msg_get_integer(msg, "f"), - msg_text); - - rc = TRUE; - } else { - rc = msim_incoming_im(session, msg, username); - } - - g_free(msg_text); - g_free(username); - - return rc; -} - -/** - * Process an incoming media (message background?) message. - */ -static gboolean -msim_incoming_media(MsimSession *session, MsimMessage *msg) -{ - gchar *username, *text; - - username = msim_msg_get_string(msg, "_username"); - text = msim_msg_get_string(msg, "msg"); - - g_return_val_if_fail(username != NULL, FALSE); - g_return_val_if_fail(text != NULL, FALSE); - - purple_debug_info("msim", "msim_incoming_media: from %s, got msg=%s\n", username, text); - - /* Media messages are sent when the user opens a window to someone. - * Tell libpurple they started typing and stopped typing, to inform the Psychic - * Mode plugin so it too can open a window to the user. */ - serv_got_typing(session->gc, username, 0, PURPLE_TYPING); - serv_got_typing_stopped(session->gc, username); - - g_free(username); - - return TRUE; -} - -/** - * Process an incoming "unofficial client" message. The plugin for - * Miranda IM sends this message with the plugin information. - */ -static gboolean -msim_incoming_unofficial_client(MsimSession *session, MsimMessage *msg) -{ - MsimUser *user; - gchar *username, *client_info; - - username = msim_msg_get_string(msg, "_username"); - client_info = msim_msg_get_string(msg, "msg"); - - g_return_val_if_fail(username != NULL, FALSE); - g_return_val_if_fail(client_info != NULL, FALSE); - - purple_debug_info("msim", "msim_incoming_unofficial_client: %s is using client %s\n", - username, client_info); - - user = msim_find_user(session, username); - - g_return_val_if_fail(user != NULL, FALSE); - - if (user->client_info) { - g_free(user->client_info); - } - user->client_info = client_info; - - g_free(username); - /* Do not free client_info - the MsimUser now owns it. */ - - return TRUE; -} - -/** - * Handle an incoming buddy message. - */ -static gboolean -msim_incoming_bm(MsimSession *session, MsimMessage *msg) -{ - guint bm; - - bm = msim_msg_get_integer(msg, "bm"); - - msim_incoming_bm_record_cv(session, msg); - - switch (bm) { - case MSIM_BM_STATUS: - return msim_incoming_status(session, msg); - case MSIM_BM_ACTION_OR_IM_DELAYABLE: - case MSIM_BM_ACTION_OR_IM_INSTANT: - return msim_incoming_action_or_im(session, msg); - case MSIM_BM_MEDIA: - return msim_incoming_media(session, msg); - case MSIM_BM_UNOFFICIAL_CLIENT: - return msim_incoming_unofficial_client(session, msg); - case MSIM_BM_STATUS_MOOD: - return msim_incoming_status_mood(session, msg); - default: - /* - * Unknown message type! We used to call - * msim_incoming_action_or_im(session, msg); - * for these, but that doesn't help anything, and it means - * we'll show broken gibberish if MySpace starts sending us - * other message types. - */ - purple_debug_warning("myspace", "Received unknown imcoming " - "message, bm=%u\n", bm); - return TRUE; - } -} - -/** - * Process the initial server information from the server. - */ -static gboolean -msim_process_server_info(MsimSession *session, MsimMessage *msg) -{ - MsimMessage *body; - - body = msim_msg_get_dictionary(msg, "body"); - g_return_val_if_fail(body != NULL, FALSE); - - /* Example body: -AdUnitRefreshInterval=10. -AlertPollInterval=360. -AllowChatRoomEmoticonSharing=False. -ChatRoomUserIDs=78744676;163733130;1300326231;123521495;142663391. -CurClientVersion=673. -EnableIMBrowse=True. -EnableIMStuffAvatars=False. -EnableIMStuffZaps=False. -MaxAddAllFriends=100. -MaxContacts=1000. -MinClientVersion=594. -MySpaceIM_ENGLISH=78744676. -MySpaceNowTimer=720. -PersistenceDataTimeout=900. -UseWebChallenge=1. -WebTicketGoHome=False - - Anything useful? TODO: use what is useful, and use it. -*/ - purple_debug_info("msim_process_server_info", - "maximum contacts: %d\n", - msim_msg_get_integer(body, "MaxContacts")); - - session->server_info = body; - /* session->server_info freed in msim_session_destroy */ - - return TRUE; -} - -/** - * Process a web challenge, used to login to the web site. - */ -static gboolean -msim_web_challenge(MsimSession *session, MsimMessage *msg) -{ - /* TODO: web challenge, store token. #2659. */ - return FALSE; -} - -/** - * Process a persistance message reply from the server. - * - * @param session - * @param msg Message reply from server. - * - * @return TRUE if successful. - * - * msim_lookup_user sets callback for here - */ -static gboolean -msim_process_reply(MsimSession *session, MsimMessage *msg) -{ - MSIM_USER_LOOKUP_CB cb; - gpointer data; - guint rid, cmd, dsn, lid; - - g_return_val_if_fail(msg != NULL, FALSE); - - msim_store_user_info(session, msg, NULL); - - rid = msim_msg_get_integer(msg, "rid"); - cmd = msim_msg_get_integer(msg, "cmd"); - dsn = msim_msg_get_integer(msg, "dsn"); - lid = msim_msg_get_integer(msg, "lid"); - - /* Unsolicited messages */ - if (cmd == (guint)(MSIM_CMD_BIT_REPLY | MSIM_CMD_GET)) { - if (dsn == (guint)MG_SERVER_INFO_DSN && lid == (guint)MG_SERVER_INFO_LID) { - return msim_process_server_info(session, msg); - } else if (dsn == (guint)MG_WEB_CHALLENGE_DSN && lid == (guint)MG_WEB_CHALLENGE_LID) { - return msim_web_challenge(session, msg); - } - } - - /* If a callback is registered for this userid lookup, call it. */ - cb = g_hash_table_lookup(session->user_lookup_cb, GUINT_TO_POINTER(rid)); - data = g_hash_table_lookup(session->user_lookup_cb_data, GUINT_TO_POINTER(rid)); - - if (cb) { - purple_debug_info("msim", "msim_process_reply: calling callback now\n"); - /* Clone message, so that the callback 'cb' can use it (needs to free it also). */ - cb(session, msg, data); - g_hash_table_remove(session->user_lookup_cb, GUINT_TO_POINTER(rid)); - g_hash_table_remove(session->user_lookup_cb_data, GUINT_TO_POINTER(rid)); - } else { - purple_debug_info("msim", - "msim_process_reply: no callback for rid %d\n", rid); - } - - return TRUE; -} - -/** - * Handle an error from the server. - * - * @param session - * @param msg The message. - * - * @return TRUE if successfully reported error. - */ -static gboolean -msim_error(MsimSession *session, MsimMessage *msg) -{ - gchar *errmsg, *full_errmsg; - guint err; - - g_return_val_if_fail(msg != NULL, FALSE); - - err = msim_msg_get_integer(msg, "err"); - errmsg = msim_msg_get_string(msg, "errmsg"); - - full_errmsg = g_strdup_printf(_("Protocol error, code %d: %s"), err, - errmsg ? errmsg : "no 'errmsg' given"); - - g_free(errmsg); - - purple_debug_info("msim", "msim_error (sesskey=%d): %s\n", - session->sesskey, full_errmsg); - - /* Destroy session if fatal. */ - if (msim_msg_get(msg, "fatal")) { - PurpleConnectionError reason = PURPLE_CONNECTION_ERROR_NETWORK_ERROR; - purple_debug_info("msim", "fatal error, closing\n"); - - switch (err) { - case MSIM_ERROR_INCORRECT_PASSWORD: /* Incorrect password */ - reason = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED; - if (!purple_account_get_remember_password(session->account)) - purple_account_set_password(session->account, NULL); -#ifdef MSIM_MAX_PASSWORD_LENGTH - if (session->account->password && (strlen(session->account->password) > MSIM_MAX_PASSWORD_LENGTH)) { - gchar *suggestion; - - suggestion = g_strdup_printf(_("%s Your password is " - "%zu characters, which is longer than the " - "maximum length of %d. Please shorten your " - "password at http://profileedit.myspace.com/index.cfm?fuseaction=accountSettings.changePassword and try again."), - full_errmsg, - strlen(session->account->password), - MSIM_MAX_PASSWORD_LENGTH); - - /* Replace full_errmsg. */ - g_free(full_errmsg); - full_errmsg = suggestion; - } else { - g_free(full_errmsg); - full_errmsg = g_strdup(_("Incorrect username or password")); - } -#endif - break; - case MSIM_ERROR_LOGGED_IN_ELSEWHERE: /* Logged in elsewhere */ - reason = PURPLE_CONNECTION_ERROR_NAME_IN_USE; - if (!purple_account_get_remember_password(session->account)) - purple_account_set_password(session->account, NULL); - break; - } - purple_connection_error_reason(session->gc, reason, full_errmsg); - } else { - purple_notify_error(session->account, _("MySpaceIM Error"), full_errmsg, NULL); - } - - g_free(full_errmsg); - - return TRUE; -} - -/** - * Process a message. - * - * @param session - * @param msg A message from the server, ready for processing (possibly with resolved username information attached). Caller frees. - * - * @return TRUE if successful. FALSE if processing failed. - */ -static gboolean -msim_process(MsimSession *session, MsimMessage *msg) -{ - g_return_val_if_fail(session != NULL, FALSE); - g_return_val_if_fail(msg != NULL, FALSE); - - if (msim_msg_get_integer(msg, "lc") == 1) { - return msim_login_challenge(session, msg); - } else if (msim_msg_get_integer(msg, "lc") == 2) { - /* return msim_we_are_logged_on(session, msg); */ - if (msim_is_username_set(session, msg)) { - return msim_we_are_logged_on(session); - } else { - /* No username is set... We'll wait for the callbacks to do their work */ - /* When they're all done, the last one will call msim_we_are_logged_on() and pick up where we left off */ - return FALSE; - } - } else if (msim_msg_get(msg, "bm")) { - return msim_incoming_bm(session, msg); - } else if (msim_msg_get(msg, "rid")) { - return msim_process_reply(session, msg); - } else if (msim_msg_get(msg, "error")) { - return msim_error(session, msg); - } else if (msim_msg_get(msg, "ka")) { - return TRUE; - } else { - msim_unrecognized(session, msg, "in msim_process"); - return FALSE; - } -} - -/** - * After a uid is resolved to username, tag it with the username and submit for processing. - * - * @param session - * @param userinfo Response messsage to resolving request. - * @param data MsimMessage *, the message to attach information to. - */ -static void -msim_incoming_resolved(MsimSession *session, const MsimMessage *userinfo, - gpointer data) -{ - gchar *username; - MsimMessage *msg, *body; - - g_return_if_fail(userinfo != NULL); - - body = msim_msg_get_dictionary(userinfo, "body"); - g_return_if_fail(body != NULL); - - username = msim_msg_get_string(body, "UserName"); - g_return_if_fail(username != NULL); - /* Note: username will be owned by 'msg' below. */ - - msg = (MsimMessage *)data; - g_return_if_fail(msg != NULL); - - /* TODO: more elegant solution than below. attach whole message? */ - /* Special elements name beginning with '_', we'll use internally within the - * program (did not come directly from the wire). */ - msg = msim_msg_append(msg, "_username", MSIM_TYPE_STRING, username); /* This makes 'msg' the owner of 'username' */ - - /* TODO: attach more useful information, like ImageURL */ - - msim_process(session, msg); - - msim_msg_free(msg); - msim_msg_free(body); -} - -/** - * Preprocess incoming messages, resolving as needed, calling - * msim_process() when ready to process. - * - * @param session - * @param msg MsimMessage *, freed by caller. - */ -static gboolean -msim_preprocess_incoming(MsimSession *session, MsimMessage *msg) -{ - g_return_val_if_fail(msg != NULL, FALSE); - - if (msim_msg_get(msg, "bm") && msim_msg_get(msg, "f")) { - guint uid; - const gchar *username; - - /* 'f' = userid message is from, in buddy messages */ - uid = msim_msg_get_integer(msg, "f"); - - username = msim_uid2username_from_blist(session->account, uid); - - if (username) { - /* Know username already, use it. */ - purple_debug_info("msim", "msim_preprocess_incoming: tagging with _username=%s\n", - username); - msg = msim_msg_append(msg, "_username", MSIM_TYPE_STRING, g_strdup(username)); - return msim_process(session, msg); - - } else { - gchar *from; - - /* Send lookup request. */ - /* XXX: where is msim_msg_get_string() freed? make _strdup and _nonstrdup. */ - purple_debug_info("msim", "msim_incoming: sending lookup, setting up callback\n"); - from = msim_msg_get_string(msg, "f"); - msim_lookup_user(session, from, msim_incoming_resolved, msim_msg_clone(msg)); - g_free(from); - - /* indeterminate */ - return TRUE; - } - } else { - /* Nothing to resolve - send directly to processing. */ - return msim_process(session, msg); - } -} - -/** - * Callback when input available. - * - * @param gc_uncasted A PurpleConnection pointer. - * @param source File descriptor. - * @param cond PURPLE_INPUT_READ - * - * Reads the input, and calls msim_preprocess_incoming() to handle it. - */ -static void -msim_input_cb(gpointer gc_uncasted, gint source, PurpleInputCondition cond) -{ - PurpleConnection *gc; - MsimSession *session; - gchar *end; - int n; - - g_return_if_fail(gc_uncasted != NULL); - g_return_if_fail(source >= 0); /* Note: 0 is a valid fd */ - - gc = (PurpleConnection *)(gc_uncasted); - session = gc->proto_data; - - /* libpurple/eventloop.h only defines these two */ - if (cond != PURPLE_INPUT_READ && cond != PURPLE_INPUT_WRITE) { - purple_debug_info("msim_input_cb", "unknown condition=%d\n", cond); - purple_connection_error_reason (gc, - PURPLE_CONNECTION_ERROR_NETWORK_ERROR, - _("Invalid input condition")); - return; - } - - g_return_if_fail(cond == PURPLE_INPUT_READ); - - /* Mark down that we got data, so we don't timeout. */ - session->last_comm = time(NULL); - - /* If approaching end of buffer, reallocate some more memory. */ - if (session->rxsize < session->rxoff + MSIM_READ_BUF_SIZE) { - purple_debug_info("msim", - "msim_input_cb: %d-byte read buffer full, rxoff=%d, " "growing by %d bytes\n", - session->rxsize, session->rxoff, MSIM_READ_BUF_SIZE); - session->rxsize += MSIM_READ_BUF_SIZE; - session->rxbuf = g_realloc(session->rxbuf, session->rxsize); - - return; - } - - purple_debug_info("msim", "dynamic buffer at %d (max %d), reading up to %d\n", - session->rxoff, session->rxsize, - MSIM_READ_BUF_SIZE - session->rxoff - 1); - - /* Read into buffer. On Win32, need recv() not read(). session->fd also holds - * the file descriptor, but it sometimes differs from the 'source' parameter. - */ - n = recv(session->fd, - session->rxbuf + session->rxoff, - session->rxsize - session->rxoff - 1, 0); - - if (n < 0) { - gchar *tmp; - - if (errno == EAGAIN) - /* No worries */ - return; - - tmp = g_strdup_printf(_("Lost connection with server: %s"), - g_strerror(errno)); - purple_connection_error_reason(gc, - PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp); - g_free(tmp); - return; - } else if (n == 0) { - purple_connection_error_reason(gc, - PURPLE_CONNECTION_ERROR_NETWORK_ERROR, - _("Server closed the connection")); - return; - } - - /* Null terminate */ - purple_debug_info("msim", "msim_input_cb: going to null terminate " - "at n=%d\n", n); - session->rxbuf[session->rxoff + n] = 0; - -#ifdef MSIM_CHECK_EMBEDDED_NULLS - /* Check for embedded NULs. I don't handle them, and they shouldn't occur. */ - if (strlen(session->rxbuf + session->rxoff) != n) { - /* Occurs after login, but it is not a null byte. */ - purple_debug_info("msim", "msim_input_cb: strlen=%d, but read %d bytes" - "--null byte encountered?\n", - strlen(session->rxbuf + session->rxoff), n); - /*purple_connection_error_reason (gc, - PURPLE_CONNECTION_ERROR_NETWORK_ERROR, - "Invalid message - null byte on input"); */ - return; - } -#endif - - session->rxoff += n; - purple_debug_info("msim", "msim_input_cb: read=%d\n", n); - -#ifdef MSIM_DEBUG_RXBUF - purple_debug_info("msim", "buf=<%s>\n", session->rxbuf); -#endif - - /* Look for \\final\\ end markers. If found, process message. */ - while((end = strstr(session->rxbuf, MSIM_FINAL_STRING))) { - MsimMessage *msg; - -#ifdef MSIM_DEBUG_RXBUF - purple_debug_info("msim", "in loop: buf=<%s>\n", session->rxbuf); -#endif - *end = 0; - msg = msim_parse(session->rxbuf); - if (!msg) { - purple_debug_info("msim", "msim_input_cb: couldn't parse rxbuf\n"); - purple_connection_error_reason (gc, - PURPLE_CONNECTION_ERROR_NETWORK_ERROR, - _("Unable to parse message")); - break; - } else { - /* Process message and then free it (processing function should - * clone message if it wants to keep it afterwards.) */ - if (!msim_preprocess_incoming(session, msg)) { - msim_msg_dump("msim_input_cb: preprocessing message failed on msg: %s\n", msg); - } - msim_msg_free(msg); - } - - /* Move remaining part of buffer to beginning. */ - session->rxoff -= strlen(session->rxbuf) + strlen(MSIM_FINAL_STRING); - memmove(session->rxbuf, end + strlen(MSIM_FINAL_STRING), - session->rxsize - (end + strlen(MSIM_FINAL_STRING) - session->rxbuf)); - - /* Clear end of buffer - * memset(end, 0, MSIM_READ_BUF_SIZE - (end - session->rxbuf)); - */ - } -} - -/** - * Callback when connected. Sets up input handlers. - * - * @param data A PurpleConnection pointer. - * @param source File descriptor. - * @param error_message - */ -static void -msim_connect_cb(gpointer data, gint source, const gchar *error_message) -{ - PurpleConnection *gc; - MsimSession *session; - - g_return_if_fail(data != NULL); - - gc = (PurpleConnection *)data; - session = (MsimSession *)gc->proto_data; - - if (source < 0) { - gchar *tmp = g_strdup_printf(_("Unable to connect: %s"), - error_message); - purple_connection_error_reason (gc, - PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp); - g_free(tmp); - return; - } - - session->fd = source; - - gc->inpa = purple_input_add(source, PURPLE_INPUT_READ, msim_input_cb, gc); -} - -/** - * Start logging in to the MSIM servers. - * - * @param acct Account information to use to login. - */ -static void -msim_login(PurpleAccount *acct) -{ - PurpleConnection *gc; - const gchar *host; - int port; - - g_return_if_fail(acct != NULL); - g_return_if_fail(acct->username != NULL); - - purple_debug_info("msim", "logging in %s\n", acct->username); - - gc = purple_account_get_connection(acct); - gc->proto_data = msim_session_new(acct); - gc->flags |= PURPLE_CONNECTION_HTML | PURPLE_CONNECTION_NO_URLDESC; - - /* - * Lets wipe out our local list of blocked buddies. We'll get a - * list of all blocked buddies from the server, and we shouldn't - * have stuff in the local list that isn't on the server list. - */ - while (acct->deny != NULL) - purple_privacy_deny_remove(acct, acct->deny->data, TRUE); - - /* 1. connect to server */ - purple_connection_update_progress(gc, _("Connecting"), - 0, /* which connection step this is */ - 4); /* total number of steps */ - - host = purple_account_get_string(acct, "server", MSIM_SERVER); - port = purple_account_get_int(acct, "port", MSIM_PORT); - - /* From purple.sf.net/api: - * """Note that this function name can be misleading--although it is called - * "proxy connect," it is used for establishing any outgoing TCP connection, - * whether through a proxy or not.""" */ - - /* Calls msim_connect_cb when connected. */ - if (!purple_proxy_connect(gc, acct, host, port, msim_connect_cb, gc)) { - /* TODO: try other ports if in auto mode, then save - * working port and try that first next time. */ - purple_connection_error_reason (gc, - PURPLE_CONNECTION_ERROR_NETWORK_ERROR, - _("Unable to connect")); - return; - } -} - -static void -msim_buddy_free(PurpleBuddy *buddy) -{ - msim_user_free(purple_buddy_get_protocol_data(buddy)); - purple_buddy_set_protocol_data(buddy, NULL); -} - -/** - * Close the connection. - * - * @param gc The connection. - */ -static void -msim_close(PurpleConnection *gc) -{ - GSList *buddies; - MsimSession *session; - - if (gc == NULL) { - return; - } - - /* - * Free our protocol-specific buddy data. It almost seems like libpurple - * should call our buddy_free prpl callback so that we don't need to do - * this... but it doesn't, so we do. - */ - buddies = purple_find_buddies(purple_connection_get_account(gc), NULL); - while (buddies != NULL) { - msim_buddy_free(buddies->data); - buddies = g_slist_delete_link(buddies, buddies); - } - - session = (MsimSession *)gc->proto_data; - if (session == NULL) - return; - - gc->proto_data = NULL; - - if (session->gc->inpa) { - purple_input_remove(session->gc->inpa); - } - if (session->fd >= 0) { - close(session->fd); - session->fd = -1; - } - - msim_session_destroy(session); -} - -/** - * Schedule an IM to be sent once the user ID is looked up. - * - * @param gc Connection. - * @param who A user id, email, or username to send the message to. - * @param message Instant message text to send. - * @param flags Flags. - * - * @return 1 if successful or postponed, -1 if failed - * - * Allows sending to a user by username, email address, or userid. If - * a username or email address is given, the userid must be looked up. - * This function does that by calling msim_postprocess_outgoing(). - */ -static int -msim_send_im(PurpleConnection *gc, const gchar *who, const gchar *message, - PurpleMessageFlags flags) -{ - MsimSession *session; - gchar *message_msim; - int rc; - - g_return_val_if_fail(gc != NULL, -1); - g_return_val_if_fail(who != NULL, -1); - g_return_val_if_fail(message != NULL, -1); - - /* 'flags' has many options, not used here. */ - - session = (MsimSession *)gc->proto_data; - - message_msim = html_to_msim_markup(session, message); - - if (msim_send_bm(session, who, message_msim, MSIM_BM_ACTION_OR_IM_DELAYABLE)) { - /* Return 1 to have Purple show this IM as being sent, 0 to not. I always - * return 1 even if the message could not be sent, since I don't know if - * it has failed yet--because the IM is only sent after the userid is - * retrieved from the server (which happens after this function returns). - * If an error does occur, it should be logged to the IM window. - */ - rc = 1; - } else { - rc = -1; - } - - g_free(message_msim); - - return rc; -} - -/** - * Handle when our user starts or stops typing to another user. - * - * @param gc - * @param name The buddy name to which our user is typing to - * @param state PURPLE_TYPING, PURPLE_TYPED, PURPLE_NOT_TYPING - * - * @return 0 - */ -static unsigned int -msim_send_typing(PurpleConnection *gc, const gchar *name, - PurpleTypingState state) -{ - const gchar *typing_str; - MsimSession *session; - - g_return_val_if_fail(gc != NULL, 0); - g_return_val_if_fail(name != NULL, 0); - - session = (MsimSession *)gc->proto_data; - - switch (state) { - case PURPLE_TYPING: - typing_str = "%typing%"; - break; - - case PURPLE_TYPED: - case PURPLE_NOT_TYPING: - default: - typing_str = "%stoptyping%"; - break; - } - - purple_debug_info("msim", "msim_send_typing(%s): %d (%s)\n", name, state, typing_str); - msim_send_bm(session, name, typing_str, MSIM_BM_ACTION_OR_IM_INSTANT); - return 0; -} - -/** - * Callback for msim_get_info(), for when user info is received. - */ -static void -msim_get_info_cb(MsimSession *session, const MsimMessage *user_info_msg, - gpointer data) -{ - MsimMessage *msg; - gchar *username; - PurpleNotifyUserInfo *user_info; - MsimUser *user; - - /* Get user{name,id} from msim_get_info, passed as an MsimMessage for - orthogonality. */ - msg = (MsimMessage *)data; - g_return_if_fail(msg != NULL); - - username = msim_msg_get_string(msg, "user"); - if (!username) { - purple_debug_info("msim", "msim_get_info_cb: no 'user' in msg\n"); - return; - } - - msim_msg_free(msg); - purple_debug_info("msim", "msim_get_info_cb: got for user: %s\n", username); - - user = msim_find_user(session, username); - - if (!user) { - /* User isn't on blist, create a temporary user to store info. */ - user = g_new0(MsimUser, 1); - user->temporary_user = TRUE; - } - - /* Update user structure with new information */ - msim_store_user_info(session, user_info_msg, user); - - user_info = purple_notify_user_info_new(); - - /* Append data from MsimUser to PurpleNotifyUserInfo for display, full */ - msim_append_user_info(session, user_info, user, TRUE); - - purple_notify_userinfo(session->gc, username, user_info, NULL, NULL); - purple_debug_info("msim", "msim_get_info_cb: username=%s\n", username); - - purple_notify_user_info_destroy(user_info); - - if (user->temporary_user) - msim_user_free(user); - g_free(username); -} - -/** - * Retrieve a user's profile. - * @param username Username, user ID, or email address to lookup. - */ -static void -msim_get_info(PurpleConnection *gc, const gchar *username) -{ - MsimSession *session; - MsimUser *user; - gchar *user_to_lookup; - MsimMessage *user_msg; - - g_return_if_fail(gc != NULL); - g_return_if_fail(username != NULL); - - session = (MsimSession *)gc->proto_data; - - /* Obtain uid of buddy. */ - user = msim_find_user(session, username); - - /* If is on buddy list, lookup by uid since it is faster. */ - if (user && user->id) { - user_to_lookup = g_strdup_printf("%d", user->id); - } else { - /* Looking up buddy not on blist. Lookup by whatever user entered. */ - user_to_lookup = g_strdup(username); - } - - /* Pass the username to msim_get_info_cb(), because since we lookup - * by userid, the userinfo message will only contain the uid (not - * the username) but it would be useful to display the username too. - */ - user_msg = msim_msg_new( - "user", MSIM_TYPE_STRING, g_strdup(username), - NULL); - purple_debug_info("msim", "msim_get_info, setting up lookup, user=%s\n", username); - - msim_lookup_user(session, user_to_lookup, msim_get_info_cb, user_msg); - - g_free(user_to_lookup); -} - -/** - * Set status using an MSIM_STATUS_CODE_* value. - * @param status_code An MSIM_STATUS_CODE_* value. - * @param statstring Status string, must be a dynamic string (will be freed by msim_send). - */ -static void -msim_set_status_code(MsimSession *session, guint status_code, gchar *statstring) -{ - g_return_if_fail(statstring != NULL); - - purple_debug_info("msim", "msim_set_status_code: going to set status to code=%d,str=%s\n", - status_code, statstring); - - if (!msim_send(session, - "status", MSIM_TYPE_INTEGER, status_code, - "sesskey", MSIM_TYPE_INTEGER, session->sesskey, - "statstring", MSIM_TYPE_STRING, statstring, - "locstring", MSIM_TYPE_STRING, g_strdup(""), - NULL)) - { - purple_debug_info("msim", "msim_set_status: failed to set status\n"); - } -} - -/** - * Set your status - callback for when user manually sets it. - */ -static void -msim_set_status(PurpleAccount *account, PurpleStatus *status) -{ - PurpleStatusType *type; - PurplePresence *pres; - MsimSession *session; - guint status_code; - const gchar *message; - gchar *stripped; - gchar *unrecognized_msg; - - session = (MsimSession *)account->gc->proto_data; - - type = purple_status_get_type(status); - pres = purple_status_get_presence(status); - - switch (purple_status_type_get_primitive(type)) { - case PURPLE_STATUS_AVAILABLE: - purple_debug_info("msim", "msim_set_status: available (%d->%d)\n", PURPLE_STATUS_AVAILABLE, - MSIM_STATUS_CODE_ONLINE); - status_code = MSIM_STATUS_CODE_ONLINE; - break; - - case PURPLE_STATUS_INVISIBLE: - purple_debug_info("msim", "msim_set_status: invisible (%d->%d)\n", PURPLE_STATUS_INVISIBLE, - MSIM_STATUS_CODE_OFFLINE_OR_HIDDEN); - status_code = MSIM_STATUS_CODE_OFFLINE_OR_HIDDEN; - break; - - case PURPLE_STATUS_AWAY: - purple_debug_info("msim", "msim_set_status: away (%d->%d)\n", PURPLE_STATUS_AWAY, - MSIM_STATUS_CODE_AWAY); - status_code = MSIM_STATUS_CODE_AWAY; - break; - - default: - purple_debug_info("msim", "msim_set_status: unknown " - "status interpreting as online"); - status_code = MSIM_STATUS_CODE_ONLINE; - - unrecognized_msg = g_strdup_printf("msim_set_status, unrecognized status type: %d\n", - purple_status_type_get_primitive(type)); - msim_unrecognized(session, NULL, unrecognized_msg); - g_free(unrecognized_msg); - - break; - } - - message = purple_status_get_attr_string(status, "message"); - - /* Status strings are plain text. */ - if (message != NULL) - stripped = purple_markup_strip_html(message); - else - stripped = g_strdup(""); - - msim_set_status_code(session, status_code, stripped); - - /* If we should be idle, set that status. Time is irrelevant here. */ - if (purple_presence_is_idle(pres) && status_code != MSIM_STATUS_CODE_OFFLINE_OR_HIDDEN) - msim_set_idle(account->gc, 1); -} - -/** - * Go idle. - */ -static void -msim_set_idle(PurpleConnection *gc, int time) -{ - MsimSession *session; - PurpleStatus *status; - - g_return_if_fail(gc != NULL); - - session = (MsimSession *)gc->proto_data; - - status = purple_account_get_active_status(session->account); - - if (time == 0) { - /* Going back from idle. In msim, idle is mutually exclusive - * from the other states (you can only be away or idle, but not - * both, for example), so by going non-idle I go back to what - * libpurple says I should be. - */ - msim_set_status(session->account, status); - } else { - const gchar *message; - gchar *stripped; - - /* Set the idle message to the status message from the real - * current status. - */ - message = purple_status_get_attr_string(status, "message"); - if (message != NULL) - stripped = purple_markup_strip_html(message); - else - stripped = g_strdup(""); - - /* msim doesn't support idle time, so just go idle */ - msim_set_status_code(session, MSIM_STATUS_CODE_IDLE, stripped); - } -} - -/** - * @return TRUE if everything was ok, FALSE if something went awry. - */ -static gboolean -msim_update_blocklist_for_buddy(MsimSession *session, const char *name, gboolean allow, gboolean block) -{ - MsimMessage *msg; - GList *list; - - list = NULL; - list = g_list_prepend(list, allow ? "a+" : "a-"); - list = g_list_prepend(list, "<uid>"); - list = g_list_prepend(list, block ? "b+" : "b-"); - list = g_list_prepend(list, "<uid>"); - list = g_list_reverse(list); - - msg = msim_msg_new( - "blocklist", MSIM_TYPE_BOOLEAN, TRUE, - "sesskey", MSIM_TYPE_INTEGER, session->sesskey, - /* TODO: MsimMessage lists. Currently <uid> isn't replaced in lists. */ - /* "idlist", MSIM_TYPE_STRING, g_strdup("a-|<uid>|b-|<uid>"), */ - "idlist", MSIM_TYPE_LIST, list, - NULL); - - if (!msim_postprocess_outgoing(session, msg, name, "idlist", NULL)) { - purple_debug_error("myspace", - "blocklist command failed for %s, allow=%d, block=%d\n", - name, allow, block); - msim_msg_free(msg); - return FALSE; - } - - msim_msg_free(msg); - - return TRUE; -} - -/** - * Add a buddy to user's buddy list. - */ -static void -msim_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group) -{ - MsimSession *session; - MsimMessage *msg; - MsimMessage *msg_persist; - MsimMessage *body; - const char *name, *gname; - - session = (MsimSession *)gc->proto_data; - name = purple_buddy_get_name(buddy); - gname = group ? purple_group_get_name(group) : NULL; - - if (msim_get_user_from_buddy(buddy, FALSE) != NULL) - return; - - purple_debug_info("msim", "msim_add_buddy: want to add %s to %s\n", - name, gname ? gname : "(no group)"); - - msg = msim_msg_new( - "addbuddy", MSIM_TYPE_BOOLEAN, TRUE, - "sesskey", MSIM_TYPE_INTEGER, session->sesskey, - /* "newprofileid" will be inserted here with uid. */ - "reason", MSIM_TYPE_STRING, g_strdup(""), - NULL); - - if (!msim_postprocess_outgoing(session, msg, name, "newprofileid", "reason")) { - purple_notify_error(NULL, NULL, _("Failed to add buddy"), _("'addbuddy' command failed.")); - msim_msg_free(msg); - return; - } - msim_msg_free(msg); - - /* TODO: if addbuddy fails ('error' message is returned), delete added buddy from - * buddy list since Purple adds it locally. */ - - body = msim_msg_new( - "ContactID", MSIM_TYPE_STRING, g_strdup("<uid>"), - "GroupName", MSIM_TYPE_STRING, g_strdup(gname), - "Position", MSIM_TYPE_INTEGER, 1000, - "Visibility", MSIM_TYPE_INTEGER, 1, - "NickName", MSIM_TYPE_STRING, g_strdup(""), - "NameSelect", MSIM_TYPE_INTEGER, 0, - NULL); - - /* TODO: Update blocklist. */ - - msg_persist = msim_msg_new( - "persist", MSIM_TYPE_INTEGER, 1, - "sesskey", MSIM_TYPE_INTEGER, session->sesskey, - "cmd", MSIM_TYPE_INTEGER, MSIM_CMD_BIT_ACTION | MSIM_CMD_PUT, - "dsn", MSIM_TYPE_INTEGER, MC_CONTACT_INFO_DSN, - "uid", MSIM_TYPE_INTEGER, session->userid, - "lid", MSIM_TYPE_INTEGER, MC_CONTACT_INFO_LID, - /* TODO: Use msim_new_reply_callback to get rid. */ - "rid", MSIM_TYPE_INTEGER, session->next_rid++, - "body", MSIM_TYPE_DICTIONARY, body, - NULL); - - if (!msim_postprocess_outgoing(session, msg_persist, name, "body", NULL)) - { - purple_notify_error(NULL, NULL, _("Failed to add buddy"), _("persist command failed")); - msim_msg_free(msg_persist); - return; - } - msim_msg_free(msg_persist); - - /* Add to allow list, remove from block list */ - msim_update_blocklist_for_buddy(session, name, TRUE, FALSE); -} - -/** - * Remove a buddy from the user's buddy list. - */ -static void -msim_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group) -{ - MsimSession *session; - MsimMessage *delbuddy_msg; - MsimMessage *persist_msg; - const char *name; - - session = (MsimSession *)gc->proto_data; - name = purple_buddy_get_name(buddy); - - delbuddy_msg = msim_msg_new( - "delbuddy", MSIM_TYPE_BOOLEAN, TRUE, - "sesskey", MSIM_TYPE_INTEGER, session->sesskey, - /* 'delprofileid' with uid will be inserted here. */ - NULL); - - if (!msim_postprocess_outgoing(session, delbuddy_msg, name, "delprofileid", NULL)) { - purple_notify_error(NULL, NULL, _("Failed to remove buddy"), _("'delbuddy' command failed")); - msim_msg_free(delbuddy_msg); - return; - } - msim_msg_free(delbuddy_msg); - - persist_msg = msim_msg_new( - "persist", MSIM_TYPE_INTEGER, 1, - "sesskey", MSIM_TYPE_INTEGER, session->sesskey, - "cmd", MSIM_TYPE_INTEGER, MSIM_CMD_BIT_ACTION | MSIM_CMD_DELETE, - "dsn", MSIM_TYPE_INTEGER, MD_DELETE_BUDDY_DSN, - "lid", MSIM_TYPE_INTEGER, MD_DELETE_BUDDY_LID, - "uid", MSIM_TYPE_INTEGER, session->userid, - "rid", MSIM_TYPE_INTEGER, session->next_rid++, - /* <uid> will be replaced by postprocessing */ - "body", MSIM_TYPE_STRING, g_strdup("ContactID=<uid>"), - NULL); - - if (!msim_postprocess_outgoing(session, persist_msg, name, "body", NULL)) { - purple_notify_error(NULL, NULL, _("Failed to remove buddy"), _("persist command failed")); - msim_msg_free(persist_msg); - return; - } - msim_msg_free(persist_msg); - - /* - * Remove from our approve list and from our block list (this - * doesn't seem like it would be necessary, but the official client - * does it) - */ - if (!msim_update_blocklist_for_buddy(session, name, FALSE, FALSE)) { - purple_notify_error(NULL, NULL, - _("Failed to remove buddy"), _("blocklist command failed")); - return; - } - msim_buddy_free(buddy); -} - -/** - * Remove a buddy from the user's buddy list and add them to the block list. - */ -static void -msim_add_deny(PurpleConnection *gc, const char *name) -{ - MsimSession *session; - MsimMessage *msg, *body; - - session = (MsimSession *)gc->proto_data; - - /* Remove from buddy list */ - msg = msim_msg_new( - "delbuddy", MSIM_TYPE_BOOLEAN, TRUE, - "sesskey", MSIM_TYPE_INTEGER, session->sesskey, - /* 'delprofileid' with uid will be inserted here. */ - NULL); - if (!msim_postprocess_outgoing(session, msg, name, "delprofileid", NULL)) - purple_debug_error("myspace", "delbuddy command failed\n"); - msim_msg_free(msg); - - /* Remove from our approve list and add to our block list */ - msim_update_blocklist_for_buddy(session, name, FALSE, TRUE); - - /* - * Add the buddy to our list of blocked contacts, so we know they - * are blocked if we log in with another client - */ - body = msim_msg_new( - "ContactID", MSIM_TYPE_STRING, g_strdup("<uid>"), - "Visibility", MSIM_TYPE_INTEGER, 2, - NULL); - msg = msim_msg_new( - "persist", MSIM_TYPE_INTEGER, 1, - "sesskey", MSIM_TYPE_INTEGER, session->sesskey, - "cmd", MSIM_TYPE_INTEGER, MSIM_CMD_BIT_ACTION | MSIM_CMD_PUT, - "dsn", MSIM_TYPE_INTEGER, MC_CONTACT_INFO_DSN, - "lid", MSIM_TYPE_INTEGER, MC_CONTACT_INFO_LID, - "rid", MSIM_TYPE_INTEGER, session->next_rid++, - "body", MSIM_TYPE_DICTIONARY, body, - NULL); - if (!msim_postprocess_outgoing(session, msg, name, "body", NULL)) - purple_debug_error("myspace", "add to block list command failed\n"); - msim_msg_free(msg); - - /* - * TODO: MySpace doesn't allow blocked buddies on our buddy list, - * do they? If not then we need to remove the buddy from - * libpurple's buddy list. - */ -} - -/** - * Remove a buddy from the user's block list. - */ -static void -msim_rem_deny(PurpleConnection *gc, const char *name) -{ - MsimSession *session; - MsimMessage *msg, *body; - - session = (MsimSession *)gc->proto_data; - - /* - * Remove from our list of blocked contacts, so we know they - * are no longer blocked if we log in with another client - */ - body = msim_msg_new( - "ContactID", MSIM_TYPE_STRING, g_strdup("<uid>"), - NULL); - msg = msim_msg_new( - "persist", MSIM_TYPE_INTEGER, 1, - "sesskey", MSIM_TYPE_INTEGER, session->sesskey, - "cmd", MSIM_TYPE_INTEGER, MSIM_CMD_BIT_ACTION | MSIM_CMD_DELETE, - "dsn", MSIM_TYPE_INTEGER, MC_DELETE_CONTACT_INFO_DSN, - "lid", MSIM_TYPE_INTEGER, MC_DELETE_CONTACT_INFO_LID, - "rid", MSIM_TYPE_INTEGER, session->next_rid++, - "body", MSIM_TYPE_DICTIONARY, body, - NULL); - if (!msim_postprocess_outgoing(session, msg, name, "body", NULL)) - purple_debug_error("myspace", "remove from block list command failed\n"); - msim_msg_free(msg); - - /* Remove from our approve list and our block list */ - msim_update_blocklist_for_buddy(session, name, FALSE, FALSE); -} - -/** - * Returns a string of a username in canonical form. Basically removes all the - * spaces, lowercases the string, and looks up user IDs to usernames. - * Normalizing tom, TOM, Tom, and 6221 wil all return 'tom'. - * - * Borrowed this code from oscar_normalize. Added checking for - * "if userid, get name before normalizing" - */ -static const char *msim_normalize(const PurpleAccount *account, const char *str) { - static char normalized[BUF_LEN]; - char *tmp1, *tmp2; - int i, j; - guint id; - - g_return_val_if_fail(str != NULL, NULL); - - if (msim_is_userid(str)) { - /* Have user ID, we need to get their username first :) */ - const char *username; - - /* If the account does not exist, we can't look up the user. */ - if (!account || !account->gc) - return str; - - id = atol(str); - username = msim_uid2username_from_blist((PurpleAccount *)account, id); - if (!username) { - /* Not in buddy list... scheisse... TODO: Manual Lookup! Bug #4631 */ - /* Note: manual lookup using msim_lookup_user() is a problem inside - * msim_normalize(), because msim_lookup_user() calls a callback function - * when the user information has been looked up, but msim_normalize() expects - * the result immediately. */ - strncpy(normalized, str, BUF_LEN); - } else { - strncpy(normalized, username, BUF_LEN); - } - } else { - /* Have username. */ - strncpy(normalized, str, BUF_LEN); - } - - /* Strip spaces. */ - for (i=0, j=0; normalized[j]; j++) { - if (normalized[j] != ' ') - normalized[i++] = normalized[j]; - } - normalized[i] = '\0'; - - /* Lowercase and perform UTF-8 normalization. */ - tmp1 = g_utf8_strdown(normalized, -1); - tmp2 = g_utf8_normalize(tmp1, -1, G_NORMALIZE_DEFAULT); - g_snprintf(normalized, sizeof(normalized), "%s", tmp2); - g_free(tmp2); - g_free(tmp1); - - /* TODO: re-add caps and spacing back to what the user wanted. - * User can format their own names, for example 'msimprpl' is shown - * as 'MsIm PrPl' in the official client. - * - * TODO: file a ticket to add this enhancement. - */ - - return normalized; -} - -/** - * Return whether the buddy can be messaged while offline. - * - * The protocol supports offline messages in just the same way as online - * messages. - */ -static gboolean -msim_offline_message(const PurpleBuddy *buddy) -{ - return TRUE; -} - -/** - * Send raw data to the server, possibly with embedded NULs. - * - * Used in prpl_info struct, so that plugins can have the most possible - * control of what is sent over the connection. Inside this prpl, - * msim_send_raw() is used, since it sends NUL-terminated strings (easier). - * - * @param gc PurpleConnection - * @param buf Buffer to send - * @param total_bytes Size of buffer to send - * - * @return Bytes successfully sent, or -1 on error. - */ -/* - * TODO: This needs to do non-blocking writes and use a watcher to check - * when the fd is available to be written to. - */ -static int -msim_send_really_raw(PurpleConnection *gc, const char *buf, int total_bytes) -{ - int total_bytes_sent; - MsimSession *session; - - g_return_val_if_fail(gc != NULL, -1); - g_return_val_if_fail(buf != NULL, -1); - g_return_val_if_fail(total_bytes >= 0, -1); - - session = (MsimSession *)gc->proto_data; - - /* Loop until all data is sent, or a failure occurs. */ - total_bytes_sent = 0; - do { - int bytes_sent; - - bytes_sent = send(session->fd, buf + total_bytes_sent, - total_bytes - total_bytes_sent, 0); - - if (bytes_sent < 0) { - purple_debug_info("msim", "msim_send_raw(%s): send() failed: %s\n", - buf, g_strerror(errno)); - return total_bytes_sent; - } - total_bytes_sent += bytes_sent; - - } while(total_bytes_sent < total_bytes); - - return total_bytes_sent; -} - -/** - * Send raw data (given as a NUL-terminated string) to the server. - * - * @param session - * @param msg The raw data to send, in a NUL-terminated string. - * - * @return TRUE if succeeded, FALSE if not. - * - */ -gboolean -msim_send_raw(MsimSession *session, const gchar *msg) -{ - size_t len; - - g_return_val_if_fail(msg != NULL, FALSE); - - purple_debug_info("msim", "msim_send_raw: writing <%s>\n", msg); - len = strlen(msg); - - return msim_send_really_raw(session->gc, msg, len) == (int)len; -} - -static GHashTable * -msim_get_account_text_table(PurpleAccount *unused) -{ - GHashTable *table; - - table = g_hash_table_new(g_str_hash, g_str_equal); - - g_hash_table_insert(table, "login_label", (gpointer)_("Email Address...")); - - return table; -} - -/** - * Callbacks called by Purple, to access this plugin. - */ -static PurplePluginProtocolInfo prpl_info = { - /* options */ - OPT_PROTO_USE_POINTSIZE /* specify font size in sane point size */ - | OPT_PROTO_MAIL_CHECK, - - /* | OPT_PROTO_IM_IMAGE - TODO: direct images. */ - NULL, /* user_splits */ - NULL, /* protocol_options */ - NO_BUDDY_ICONS, /* icon_spec - TODO: eventually should add this */ - msim_list_icon, /* list_icon */ - NULL, /* list_emblems */ - msim_status_text, /* status_text */ - msim_tooltip_text, /* tooltip_text */ - msim_status_types, /* status_types */ - msim_blist_node_menu, /* blist_node_menu */ - NULL, /* chat_info */ - NULL, /* chat_info_defaults */ - msim_login, /* login */ - msim_close, /* close */ - msim_send_im, /* send_im */ - NULL, /* set_info */ - msim_send_typing, /* send_typing */ - msim_get_info, /* get_info */ - msim_set_status, /* set_status */ - msim_set_idle, /* set_idle */ - NULL, /* change_passwd */ - msim_add_buddy, /* add_buddy */ - NULL, /* add_buddies */ - msim_remove_buddy, /* remove_buddy */ - NULL, /* remove_buddies */ - NULL, /* add_permit */ - msim_add_deny, /* add_deny */ - NULL, /* rem_permit */ - msim_rem_deny, /* rem_deny */ - NULL, /* set_permit_deny */ - NULL, /* join_chat */ - NULL, /* reject chat invite */ - NULL, /* get_chat_name */ - NULL, /* chat_invite */ - NULL, /* chat_leave */ - NULL, /* chat_whisper */ - NULL, /* chat_send */ - NULL, /* keepalive */ - NULL, /* register_user */ - NULL, /* get_cb_info */ - NULL, /* get_cb_away */ - NULL, /* alias_buddy */ - NULL, /* group_buddy */ - NULL, /* rename_group */ - msim_buddy_free, /* buddy_free */ - NULL, /* convo_closed */ - msim_normalize, /* normalize */ - NULL, /* set_buddy_icon */ - NULL, /* remove_group */ - NULL, /* get_cb_real_name */ - NULL, /* set_chat_topic */ - NULL, /* find_blist_chat */ - NULL, /* roomlist_get_list */ - NULL, /* roomlist_cancel */ - NULL, /* roomlist_expand_category */ - NULL, /* can_receive_file */ - NULL, /* send_file */ - NULL, /* new_xfer */ - msim_offline_message, /* offline_message */ - NULL, /* whiteboard_prpl_ops */ - msim_send_really_raw, /* send_raw */ - NULL, /* roomlist_room_serialize */ - NULL, /* unregister_user */ - msim_send_attention, /* send_attention */ - msim_attention_types, /* attention_types */ - sizeof(PurplePluginProtocolInfo), /* struct_size */ - msim_get_account_text_table, /* get_account_text_table */ - NULL, /* initiate_media */ - NULL, /* get_media_caps */ - NULL, /* get_moods */ - NULL, /* set_public_alias */ - NULL, /* get_public_alias */ - NULL, /* add_buddy_with_invite */ - NULL /* add_buddies_with_invite */ -}; - -/** - * Load the plugin. - */ -static gboolean -msim_load(PurplePlugin *plugin) -{ - /* If compiled to use RC4 from libpurple, check if it is really there. */ - if (!purple_ciphers_find_cipher("rc4")) { - purple_debug_error("msim", "rc4 not in libpurple, but it is required - not loading MySpaceIM plugin!\n"); - purple_notify_error(plugin, _("Missing Cipher"), - _("The RC4 cipher could not be found"), - _("Upgrade " - "to a libpurple with RC4 support (>= 2.0.1). MySpaceIM " - "plugin will not be loaded.")); - return FALSE; - } - return TRUE; -} - -/** - * Called when friends have been imported to buddy list on server. - */ -static void -msim_import_friends_cb(MsimSession *session, const MsimMessage *reply, gpointer user_data) -{ - MsimMessage *body; - gchar *completed; - - /* Check if the friends were imported successfully. */ - body = msim_msg_get_dictionary(reply, "body"); - g_return_if_fail(body != NULL); - completed = msim_msg_get_string(body, "Completed"); - msim_msg_free(body); - g_return_if_fail(completed != NULL); - if (!g_str_equal(completed, "True")) - { - purple_debug_info("msim_import_friends_cb", - "failed to import friends: %s", completed); - purple_notify_error(session->account, _("Add friends from MySpace.com"), - _("Importing friends failed"), NULL); - g_free(completed); - return; - } - g_free(completed); - - purple_debug_info("msim_import_friends_cb", - "added friends to server-side buddy list, requesting new contacts from server"); - - msim_get_contact_list(session, MSIM_CONTACT_LIST_IMPORT_ALL_FRIENDS); - - /* TODO: show, X friends have been added */ -} - -/** - * Import friends from myspace.com. - */ -static void msim_import_friends(PurplePluginAction *action) -{ - PurpleConnection *gc; - MsimSession *session; - gchar *group_name; - - gc = (PurpleConnection *)action->context; - session = (MsimSession *)gc->proto_data; - - group_name = "MySpace Friends"; - - g_return_if_fail(msim_send(session, - "persist", MSIM_TYPE_INTEGER, 1, - "sesskey", MSIM_TYPE_INTEGER, session->sesskey, - "cmd", MSIM_TYPE_INTEGER, MSIM_CMD_PUT, - "dsn", MSIM_TYPE_INTEGER, MC_IMPORT_ALL_FRIENDS_DSN, - "lid", MSIM_TYPE_INTEGER, MC_IMPORT_ALL_FRIENDS_LID, - "uid", MSIM_TYPE_INTEGER, session->userid, - "rid", MSIM_TYPE_INTEGER, - msim_new_reply_callback(session, msim_import_friends_cb, NULL), - "body", MSIM_TYPE_STRING, - g_strdup_printf("GroupName=%s", group_name), - NULL)); -} - -/** - * Actions menu for account. - */ -static GList * -msim_actions(PurplePlugin *plugin, gpointer context /* PurpleConnection* */) -{ - GList *menu; - PurplePluginAction *act; - - menu = NULL; - -#if 0 - /* TODO: find out how */ - act = purple_plugin_action_new(_("Find people..."), msim_); - menu = g_list_append(menu, act); - - act = purple_plugin_action_new(_("Change IM name..."), NULL); - menu = g_list_append(menu, act); -#endif - - act = purple_plugin_action_new(_("Add friends from MySpace.com"), msim_import_friends); - menu = g_list_append(menu, act); - - return menu; -} - -/** - * Based on MSN's plugin info comments. - */ -static PurplePluginInfo info = { - PURPLE_PLUGIN_MAGIC, - PURPLE_MAJOR_VERSION, - PURPLE_MINOR_VERSION, - PURPLE_PLUGIN_PROTOCOL, /**< type */ - NULL, /**< ui_requirement */ - 0, /**< flags */ - NULL, /**< dependencies */ - PURPLE_PRIORITY_DEFAULT, /**< priority */ - - "prpl-myspace", /**< id */ - "MySpaceIM", /**< name */ - MSIM_PRPL_VERSION_STRING, /**< version */ - /** summary */ - "MySpaceIM Protocol Plugin", - /** description */ - "MySpaceIM Protocol Plugin", - "Jeff Connelly <jeff2@soc.pidgin.im>", /**< author */ - "http://developer.pidgin.im/wiki/MySpaceIM/", /**< homepage */ - - msim_load, /**< load */ - NULL, /**< unload */ - NULL, /**< destroy */ - NULL, /**< ui_info */ - &prpl_info, /**< extra_info */ - NULL, /**< prefs_info */ - msim_actions, /**< msim_actions */ - NULL, /**< reserved1 */ - NULL, /**< reserved2 */ - NULL, /**< reserved3 */ - NULL /**< reserved4 */ -}; - -#ifdef MSIM_SELF_TEST -/* - * Test functions. - * Used to test or try out the internal workings of msimprpl. If you're reading - * this code for the first time, these functions can be instructive in learning - * how msimprpl is architected. - */ - -/** - * Test MsimMessage for basic functionality. - */ -static int -msim_test_msg(void) -{ - MsimMessage *msg, *msg_cloned, *msg2; - GList *list; - gchar *packed, *packed_expected, *packed_cloned; - guint failures; - - failures = 0; - - purple_debug_info("msim", "\n\nTesting MsimMessage\n"); - msg = msim_msg_new(NULL); /* Create a new, empty message. */ - - /* Append some new elements. */ - msg = msim_msg_append(msg, "bx", MSIM_TYPE_BINARY, g_string_new_len("XXX", 3)); - msg = msim_msg_append(msg, "k1", MSIM_TYPE_STRING, g_strdup("v1")); - msg = msim_msg_append(msg, "k1", MSIM_TYPE_INTEGER, GUINT_TO_POINTER(42)); - msg = msim_msg_append(msg, "k1", MSIM_TYPE_STRING, g_strdup("v43")); - msg = msim_msg_append(msg, "k1", MSIM_TYPE_STRING, g_strdup("v52/xxx\\yyy")); - msg = msim_msg_append(msg, "k1", MSIM_TYPE_STRING, g_strdup("v7")); - msim_msg_dump("msg debug str=%s\n", msg); - packed = msim_msg_pack(msg); - - purple_debug_info("msim", "msg packed=%s\n", packed); - - packed_expected = "\\bx\\WFhY\\k1\\v1\\k1\\42\\k1" - "\\v43\\k1\\v52/1xxx/2yyy\\k1\\v7\\final\\"; - - if (!g_str_equal(packed, packed_expected)) { - purple_debug_info("msim", "!!!(%d), msim_msg_pack not what expected: %s != %s\n", - ++failures, packed, packed_expected); - } - - - msg_cloned = msim_msg_clone(msg); - packed_cloned = msim_msg_pack(msg_cloned); - - purple_debug_info("msim", "msg cloned=%s\n", packed_cloned); - if (!g_str_equal(packed, packed_cloned)) { - purple_debug_info("msim", "!!!(%d), msim_msg_pack on cloned message not equal to original: %s != %s\n", - ++failures, packed_cloned, packed); - } - - g_free(packed); - g_free(packed_cloned); - msim_msg_free(msg_cloned); - msim_msg_free(msg); - - /* Try some of the more advanced functionality */ - list = NULL; - - list = g_list_prepend(list, "item3"); - list = g_list_prepend(list, "item2"); - list = g_list_prepend(list, "item1"); - list = g_list_prepend(list, "item0"); - - msg = msim_msg_new(NULL); - msg = msim_msg_append(msg, "string", MSIM_TYPE_STRING, g_strdup("string value")); - msg = msim_msg_append(msg, "raw", MSIM_TYPE_RAW, g_strdup("raw value")); - msg = msim_msg_append(msg, "integer", MSIM_TYPE_INTEGER, GUINT_TO_POINTER(3140)); - msg = msim_msg_append(msg, "boolean", MSIM_TYPE_BOOLEAN, GUINT_TO_POINTER(FALSE)); - msg = msim_msg_append(msg, "list", MSIM_TYPE_LIST, list); - - msim_msg_dump("msg with list=%s\n", msg); - purple_debug_info("msim", "msg with list packed=%s\n", msim_msg_pack(msg)); - - msg2 = msim_msg_new(NULL); - msg2 = msim_msg_append(msg2, "outer", MSIM_TYPE_STRING, g_strdup("outer value")); - msg2 = msim_msg_append(msg2, "body", MSIM_TYPE_DICTIONARY, msg); - msim_msg_dump("msg with dict=%s\n", msg2); /* msg2 now 'owns' msg */ - purple_debug_info("msim", "msg with dict packed=%s\n", msim_msg_pack(msg2)); - - msim_msg_free(msg2); - - return failures; -} - -/** - * Test protocol-level escaping/unescaping. - */ -static int -msim_test_escaping(void) -{ - guint failures; - gchar *raw, *escaped, *unescaped, *expected; - - failures = 0; - - purple_debug_info("msim", "\n\nTesting escaping\n"); - - raw = "hello/world\\hello/world"; - - escaped = msim_escape(raw); - purple_debug_info("msim", "msim_test_escaping: raw=%s, escaped=%s\n", raw, escaped); - expected = "hello/1world/2hello/1world"; - if (!g_str_equal(escaped, expected)) { - purple_debug_info("msim", "!!!(%d), msim_escape failed: %s != %s\n", - ++failures, escaped, expected); - } - - - unescaped = msim_unescape(escaped); - g_free(escaped); - purple_debug_info("msim", "msim_test_escaping: unescaped=%s\n", unescaped); - if (!g_str_equal(raw, unescaped)) { - purple_debug_info("msim", "!!!(%d), msim_unescape failed: %s != %s\n", - ++failures, raw, unescaped); - } - - return failures; -} - -static void -msim_test_all(void) -{ - guint failures; - - failures = 0; - failures += msim_test_msg(); - failures += msim_test_escaping(); - - if (failures) { - purple_debug_info("msim", "msim_test_all HAD FAILURES: %d\n", failures); - } else { - purple_debug_info("msim", "msim_test_all - all tests passed!\n"); - } - exit(0); -} -#endif - -#ifdef MSIM_CHECK_NEWER_VERSION -/** - * Callback for when a currentversion.txt has been downloaded. - */ -static void -msim_check_newer_version_cb(PurpleUtilFetchUrlData *url_data, - gpointer user_data, - const gchar *url_text, - gsize len, - const gchar *error_message) -{ - GKeyFile *keyfile; - GError *error; - GString *data; - gchar *newest_filever; - - if (!url_text) { - purple_debug_info("msim_check_newer_version_cb", - "got error: %s\n", error_message); - return; - } - - purple_debug_info("msim_check_newer_version_cb", - "url_text=%s\n", url_text ? url_text : "(NULL)"); - - /* Prepend [group] so that GKeyFile can parse it (requires a group). */ - data = g_string_new(url_text); - purple_debug_info("msim", "data=%s\n", data->str - ? data->str : "(NULL)"); - data = g_string_prepend(data, "[group]\n"); - - purple_debug_info("msim", "data=%s\n", data->str - ? data->str : "(NULL)"); - - /* url_text is variable=data\n...†*/ - - /* Check FILEVER, 1.0.716.0. 716 is build, MSIM_CLIENT_VERSION */ - /* New (english) version can be downloaded from SETUPURL+SETUPFILE */ - - error = NULL; - keyfile = g_key_file_new(); - - /* Default list seperator is ;, but currentversion.txt doesn't have - * these, so set to an unused character to avoid parsing problems. */ - g_key_file_set_list_separator(keyfile, '\0'); - - g_key_file_load_from_data(keyfile, data->str, data->len, - G_KEY_FILE_NONE, &error); - g_string_free(data, TRUE); - - if (error != NULL) { - purple_debug_info("msim_check_newer_version_cb", - "couldn't parse, error: %d %d %s\n", - error->domain, error->code, error->message); - g_error_free(error); - return; - } - - gchar **ks; - guint n; - ks = g_key_file_get_keys(keyfile, "group", &n, NULL); - purple_debug_info("msim", "n=%d\n", n); - guint i; - for (i = 0; ks[i] != NULL; ++i) - { - purple_debug_info("msim", "%d=%s\n", i, ks[i]); - } - - newest_filever = g_key_file_get_string(keyfile, "group", - "FILEVER", &error); - - purple_debug_info("msim_check_newer_version_cb", - "newest filever: %s\n", newest_filever ? - newest_filever : "(NULL)"); - if (error != NULL) { - purple_debug_info("msim_check_newer_version_cb", - "error: %d %d %s\n", - error->domain, error->code, error->message); - g_error_free(error); - } - - g_key_file_free(keyfile); - - exit(0); -} -#endif - -/** - Handle a myim:addContact command, after username has been looked up. - */ -static void -msim_uri_handler_addContact_cb(MsimSession *session, MsimMessage *userinfo, gpointer data) -{ - MsimMessage *body; - gchar *username; - - body = msim_msg_get_dictionary(userinfo, "body"); - username = msim_msg_get_string(body, "UserName"); - msim_msg_free(body); - - if (!username) { - guint uid; - - uid = msim_msg_get_integer(userinfo, "UserID"); - g_return_if_fail(uid != 0); - - username = g_strdup_printf("%d", uid); - } - - purple_blist_request_add_buddy(session->account, username, _("Buddies"), NULL); - - g_free(username); -} - -/* TODO: move uid->username resolving to IM sending and buddy adding functions, - * so that user can manually add or IM by userid and username automatically - * looked up if possible? */ - -/** - * Handle a myim:sendIM URI command, after username has been looked up. - */ -static void -msim_uri_handler_sendIM_cb(MsimSession *session, MsimMessage *userinfo, gpointer data) -{ - PurpleConversation *conv; - MsimMessage *body; - gchar *username; - - body = msim_msg_get_dictionary(userinfo, "body"); - username = msim_msg_get_string(body, "UserName"); - msim_msg_free(body); - - if (!username) { - guint uid; - - uid = msim_msg_get_integer(userinfo, "UserID"); - g_return_if_fail(uid != 0); - - username = g_strdup_printf("%d", uid); - } - - - conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, username, session->account); - if (!conv) { - purple_debug_info("msim_uri_handler", "creating new conversation for %s\n", username); - conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, session->account, username); - } - - /* Just open the window so the user can send an IM. */ - purple_conversation_present(conv); - - g_free(username); -} - -static gboolean -msim_uri_handler(const gchar *proto, const gchar *cmd, GHashTable *params) -{ - PurpleAccount *account; - MsimSession *session; - GList *l; - gchar *uid_str, *cid_str; - guint uid, cid; - - if (g_ascii_strcasecmp(proto, "myim")) - return FALSE; - - /* Parameters are case-insensitive. */ - 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; - - /* Need a contact. */ - g_return_val_if_fail(cid != 0, FALSE); - - /* TODO: if auto=true, "Add all the people on this page to my IM List!", on - * http://collect.myspace.com/index.cfm?fuseaction=im.friendslist. Don't need a cid. */ - - /* Convert numeric contact ID back to a string. Needed for looking up. Don't just - * directly use cid directly from parameters, because it might not be numeric. - * It is trivial to change this to allow cID to be a username, but that's not how - * the official MySpaceIM client works, so don't provide that functionality. */ - cid_str = g_strdup_printf("%d", cid); - - - /* 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) == (int)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.")); - g_free(cid_str); - return FALSE; - } - - session = (MsimSession *)account->gc->proto_data; - g_return_val_if_fail(session != NULL, FALSE); - - /* Lookup userid to username. TODO: push this down, to IM sending/contact - * adding functions. */ - - /* myim:sendIM?uID=USERID&cID=CONTACTID */ - if (!g_ascii_strcasecmp(cmd, "sendIM")) { - msim_lookup_user(session, cid_str, (MSIM_USER_LOOKUP_CB)msim_uri_handler_sendIM_cb, NULL); - g_free(cid_str); - return TRUE; - - /* myim:addContact?uID=USERID&cID=CONTACTID */ - } else if (!g_ascii_strcasecmp(cmd, "addContact")) { - msim_lookup_user(session, cid_str, (MSIM_USER_LOOKUP_CB)msim_uri_handler_addContact_cb, NULL); - g_free(cid_str); - return TRUE; - } - - return FALSE; -} - -/** - * Initialize plugin. - */ -static void -init_plugin(PurplePlugin *plugin) -{ -#ifdef MSIM_SELF_TEST - msim_test_all(); - exit(0); -#endif /* MSIM_SELF_TEST */ - - PurpleAccountOption *option; - static gboolean initialized = FALSE; - -#ifdef MSIM_CHECK_NEWER_VERSION - /* PROBLEM: MySpace's servers always return Content-Location, and - * libpurple redirects to it, infinitely, even though it is the same - * location we requested! */ - purple_util_fetch_url("http://im.myspace.com/nsis/currentversion.txt", - FALSE, /* not full URL */ - "MSIMAutoUpdateAgent", /* user agent */ - TRUE, /* use HTTP/1.1 */ - msim_check_newer_version_cb, NULL); -#endif - - /* TODO: default to automatically try different ports. Make the user be - * able to set the first port to try (like LastConnectedPort in Windows client). */ - option = purple_account_option_string_new(_("Connect server"), "server", MSIM_SERVER); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); - - option = purple_account_option_int_new(_("Connect port"), "port", MSIM_PORT); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); - -#ifdef MSIM_USER_WANTS_TO_CONFIGURE_STATUS_TEXT - option = purple_account_option_bool_new(_("Show display name in status text"), "show_display_name", TRUE); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); - - option = purple_account_option_bool_new(_("Show headline in status text"), "show_headline", TRUE); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); -#endif - -#ifdef MSIM_USER_WANTS_TO_DISABLE_EMOTICONS - option = purple_account_option_bool_new(_("Send emoticons"), "emoticons", TRUE); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); -#endif - -#ifdef MSIM_USER_REALLY_CARES_ABOUT_PRECISE_FONT_SIZES - option = purple_account_option_int_new(_("Screen resolution (dots per inch)"), "dpi", MSIM_DEFAULT_DPI); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); - - option = purple_account_option_int_new(_("Base font size (points)"), "base_font_size", MSIM_BASE_FONT_POINT_SIZE); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); -#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); diff --git a/libpurple/protocols/myspace/myspace.h b/libpurple/protocols/myspace/myspace.h deleted file mode 100644 index eecfc20db9..0000000000 --- a/libpurple/protocols/myspace/myspace.h +++ /dev/null @@ -1,203 +0,0 @@ -/* MySpaceIM Protocol Plugin, header file - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ - -#ifndef _MYSPACE_MYSPACE_H -#define _MYSPACE_MYSPACE_H - -#include "internal.h" - -/* Other includes */ -#include <string.h> -#include <errno.h>/* for EAGAIN */ -#include <stdarg.h> -#include <math.h> - -#include <glib.h> - -#ifdef _WIN32 -#include "win32dep.h" -#else -/* For recv() and send(); needed to match Win32 */ -#include <sys/types.h> -#include <sys/socket.h> -#endif - -#include "notify.h" -#include "plugin.h" -#include "accountopt.h" -#include "version.h" -#include "cipher.h" /* for SHA-1 */ -#include "util.h" /* for base64 */ -#include "debug.h" /* for purple_debug_info */ -#include "request.h" /* For dialogs used in setting the username */ -#include "xmlnode.h" -#include "core.h" -#include "conversation.h" /* For late normalization */ - -/* 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) */ -/*#define MSIM_SEND_CLIENT_VERSION */ - -/* Debugging options */ -/* Low-level and rarely needed */ -/*#define MSIM_DEBUG_PARSE */ -/*#define MSIM_DEBUG_LOGIN_CHALLENGE*/ -/*#define MSIM_DEBUG_RXBUF */ - -/* Encode unknown HTML tags from IM clients in messages as [tag], instead of - * ignoring. Useful for debugging */ -/*#define MSIM_MARKUP_SHOW_UNKNOWN_TAGS */ - -/* Define to cause init_plugin() to run some tests and print - * the results to the Purple debug log, then exit. Useful to - * run with 'pidgin -d' to see the output. Don't define if - * you want to actually use the plugin! */ -/*#define MSIM_SELF_TEST */ - -/* Constants */ - -/* Maximum length of a password that is acceptable. This is the limit - * on the official client (build 679) and on the 'new password' field at - * http://settings.myspace.com/index.cfm?fuseaction=user.changepassword - * (though curiously, not on the 'current password' field). */ - -/* After login fails, if password is greater than this many characters, - * warn user that it may be too long. */ -#define MSIM_MAX_PASSWORD_LENGTH 10 - -/* Maximum length of usernames, when setting. */ -#define MSIM_MAX_USERNAME_LENGTH 25 - -/* Build version of MySpaceIM to report to servers (1.0.xxx.0) */ -#define MSIM_CLIENT_VERSION 697 - -/* Check for a newer official MySpaceIM client on startup? - * (Mostly useful for developers) */ -/*#define MSIM_CHECK_NEWER_VERSION*/ - -/* Language codes from http://www.microsoft.com/globaldev/reference/oslocversion.mspx */ -#define MSIM_LANGUAGE_ID_ENGLISH 1033 -#define MSIM_LANGUAGE_NAME_ENGLISH "ENGLISH" - -/* msimprpl version string of this plugin */ -#define MSIM_PRPL_VERSION_STRING "0.18" - -/* Default server */ -#define MSIM_SERVER "im.myspace.akadns.net" -#define MSIM_PORT 1863 /* TODO: alternate ports and automatic */ - -/* Time between keepalives (seconds) - if no data within this time, is dead. */ -#define MSIM_KEEPALIVE_INTERVAL (3 * 60) -/*#define MSIM_USE_KEEPALIVE*/ - -/* Time to check if alive (seconds) */ -#define MSIM_KEEPALIVE_INTERVAL_CHECK 30 - -/* Time to check for new mail (milliseconds) */ -#define MSIM_MAIL_INTERVAL_CHECK (60 * 1000) - -/* Constants */ -#define HASH_SIZE 0x14 /**< Size of SHA-1 hash for login */ -#define NONCE_SIZE 0x20 /**< Half of decoded 'nc' field */ -#define MSIM_READ_BUF_SIZE (15 * 1024) /**< Receive buffer size */ -#define MSIM_FINAL_STRING "\\final\\" /**< Message end marker */ - -/* Messages */ -#define MSIM_BM_ACTION_OR_IM_DELAYABLE 1 -#define MSIM_BM_STATUS 100 -#define MSIM_BM_ACTION_OR_IM_INSTANT 121 -#define MSIM_BM_MEDIA 122 -#define MSIM_BM_PROFILE 124 -#define MSIM_BM_STATUS_MOOD 126 -#define MSIM_BM_UNOFFICIAL_CLIENT 200 - -/* Authentication algorithm for login2 */ -#define MSIM_AUTH_ALGORITHM 196610 - -/* Recognized challenge length */ -#define MSIM_AUTH_CHALLENGE_LENGTH 0x40 - -#ifdef SEND_OUR_IP_ADDRESSES -/* TODO: obtain IPs of network interfaces from user's machine, instead of - * hardcoding these values below (used in msim_compute_login_response). - * This is not immediately - * important because you can still connect and perform basic - * functions of the protocol. There is also a high chance that the addreses - * are RFC1918 private, so the servers couldn't do anything with them - * anyways except make note of that fact. Probably important for any - * kind of direct connection, or file transfer functionality. - */ - -#define MSIM_LOGIN_IP_LIST "\x00\x00\x00\x00\x05\x7f\x00\x00\x01\x00\x00\x00\x00\x0a\x00\x00\x40\xc0\xa8\x58\x01\xc0\xa8\x3c\x01" -#define MSIM_LOGIN_IP_LIST_LEN 25 -#endif /* SEND_OUR_IP_ADDRESSES */ - -/* Indexes into status string (0|1|2|3|..., but 0 always empty) */ -#define MSIM_STATUS_ORDINAL_EMPTY 0 -#define MSIM_STATUS_ORDINAL_UNKNOWNs 1 -#define MSIM_STATUS_ORDINAL_ONLINE 2 -#define MSIM_STATUS_ORDINAL_UNKNOWNss 3 -#define MSIM_STATUS_ORDINAL_HEADLINE 4 -#define MSIM_STATUS_ORDINAL_UNKNOWNls 5 -#define MSIM_STATUS_ORDINAL_UNKNOWN 6 -#define MSIM_STATUS_ORDINAL_UNKNOWN1 7 -#define MSIM_STATUS_ORDINAL_UNKNOWNp 8 -#define MSIM_STATUS_ORDINAL_UNKNOWN2 9 - -/* Status codes - states a buddy (or you!) can be in. */ -#define MSIM_STATUS_CODE_OFFLINE_OR_HIDDEN 0 -#define MSIM_STATUS_CODE_ONLINE 1 -#define MSIM_STATUS_CODE_IDLE 2 -#define MSIM_STATUS_CODE_AWAY 5 - -/* Inbox status bitfield values for MsimSession.inbox_status. */ -#define MSIM_INBOX_MAIL (1 << 0) -#define MSIM_INBOX_BLOG_COMMENT (1 << 1) -#define MSIM_INBOX_PROFILE_COMMENT (1 << 2) -#define MSIM_INBOX_FRIEND_REQUEST (1 << 3) -#define MSIM_INBOX_PICTURE_COMMENT (1 << 4) - -/* Codes for msim_got_contact_list(), to tell what to do afterwards. */ -#define MSIM_CONTACT_LIST_INITIAL_FRIENDS 0 -#define MSIM_CONTACT_LIST_IMPORT_ALL_FRIENDS 1 -#define MSIM_CONTACT_LIST_IMPORT_TOP_FRIENDS 2 - -/* Error codes */ -#define MSIM_ERROR_INCORRECT_PASSWORD 260 -#define MSIM_ERROR_LOGGED_IN_ELSEWHERE 6 - -/* Functions */ -gboolean msim_send_raw(MsimSession *session, const gchar *msg); - -gboolean msim_send_bm(MsimSession *session, const gchar *who, const gchar *text, int type); - -gboolean msim_we_are_logged_on(MsimSession *session); - -void msim_unrecognized(MsimSession *session, MsimMessage *msg, gchar *note); -guint msim_new_reply_callback(MsimSession *session, MSIM_USER_LOOKUP_CB cb, gpointer data); - -#endif /* !_MYSPACE_MYSPACE_H */ diff --git a/libpurple/protocols/myspace/persist.h b/libpurple/protocols/myspace/persist.h deleted file mode 100644 index 09712c9143..0000000000 --- a/libpurple/protocols/myspace/persist.h +++ /dev/null @@ -1,92 +0,0 @@ -/* MySpaceIM Protocol Plugin, persist commands - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ - -#ifndef _MYSPACE_PERSIST_H -#define _MYSPACE_PERSIST_H - -/** Command codes */ -#define MSIM_CMD_GET 1 -#define MSIM_CMD_PUT 2 -#define MSIM_CMD_DELETE 3 - -/** Command bit fields */ -#define MSIM_CMD_BIT_CODE 255 /*< Bits specifying command code */ -#define MSIM_CMD_BIT_REPLY 256 /**< 1=reply, 0=request */ -#define MSIM_CMD_BIT_ACTION 512 /**< 1=action, 0=information */ -#define MSIM_CMD_BIT_ERROR 1024 /**< 1=error, 0=normal */ - -/** Macros to read cmd bitfield. */ -#define MSIM_CMD_GET_CODE(x) (x & MSIM_CMD_BIT_CODE) -#define MSIM_CMD_IS_REPLY(x) (x & MSIM_CMD_BIT_REPLY) -#define MSIM_CMD_IS_REQUEST(x) !(x & MSIM_CMD_BIT_REPLY) -#define MSIM_CMD_IS_ACTION(x) (x & MSIM_CMD_BIT_ACTION) -#define MSIM_CMD_IS_INFO(x) !(x & MSIM_CMD_BIT_ACTION) -#define MSIM_CMD_IS_ERROR(x) (x & MSIM_CMD_BIT_ERROR) -#define MSIM_CMD_IS_NORMAL(x) !(x & MSIM_CMD_BIT_ERROR) - -/** Define a set of _DSN and _LID constants for a persistance request. */ -#define MSIM_PERSIST_DSN_LID(name,dsn,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 - * In file included from myspace.c:37: - * persist.h:56: error: expected ')' before numeric constant - * So instead, I define const ints above. -#define MSIM_PERSIST_DSN_LID(name,dsn,lid) \ - #define name##_DSN dsn \ - #define name##_LID lid -#endif -*/ - -/** Messages to Get information dsn lid */ -MSIM_PERSIST_DSN_LID(MG_LIST_ALL_CONTACTS, 0, 1) -MSIM_PERSIST_DSN_LID(MG_USER_INFO_BY_ID, 0, 2) -MSIM_PERSIST_DSN_LID(MG_OWN_IM_INFO, 1, 4) -MSIM_PERSIST_DSN_LID(MG_IM_INFO_BY_ID, 1, 17) -MSIM_PERSIST_DSN_LID(MG_LIST_ALL_GROUPS, 2, 6) -MSIM_PERSIST_DSN_LID(MG_MYSPACE_INFO_BY_ID, 4, 3) -MSIM_PERSIST_DSN_LID(MG_OWN_MYSPACE_INFO, 4, 5) -MSIM_PERSIST_DSN_LID(MG_MYSPACE_INFO_BY_STRING, 5, 7) -MSIM_PERSIST_DSN_LID(MG_CHECK_MAIL, 7, 18) -MSIM_PERSIST_DSN_LID(MG_WEB_CHALLENGE, 17, 26) -MSIM_PERSIST_DSN_LID(MG_USER_SONG, 21, 28) -MSIM_PERSIST_DSN_LID(MG_SERVER_INFO, 101, 20) - -/** Messages to Change/send information */ -MSIM_PERSIST_DSN_LID(MC_USER_PREFERENCES, 1, 10) -MSIM_PERSIST_DSN_LID(MC_DELETE_CONTACT_INFO, 0, 8) -MSIM_PERSIST_DSN_LID(MC_CONTACT_INFO, 0, 9) -MSIM_PERSIST_DSN_LID(MC_SET_USERNAME, 9, 14) -MSIM_PERSIST_DSN_LID(MC_IMPORT_ALL_FRIENDS, 14, 21) -MSIM_PERSIST_DSN_LID(MC_INVITE, 16, 25) - -/** Messages to Delete information */ -MSIM_PERSIST_DSN_LID(MD_DELETE_BUDDY, 0, 8) - -/** Error codes */ -#define MERR_PARSE 1 -#define MERR_NOT_LOGGED_IN 2 -#define MERR_ANOTHER_LOGIN 6 -#define MERR_BAD_EMAIL 259 -#define MERR_BAD_PASSWORD 260 -#define MERR_BAD_UID_IN_PERSISTR 4352 - -#endif /* !_MYSPACE_PERSIST_H */ diff --git a/libpurple/protocols/myspace/session.c b/libpurple/protocols/myspace/session.c deleted file mode 100644 index 2d6e6c98d4..0000000000 --- a/libpurple/protocols/myspace/session.c +++ /dev/null @@ -1,98 +0,0 @@ -/* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 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->rxsize = MSIM_READ_BUF_SIZE; - session->rxbuf = g_new0(gchar, session->rxsize); - session->next_rid = 1; - session->last_comm = time(NULL); - session->inbox_status = 0; - session->inbox_handle = 0; - - return session; -} - -/** - * Free a session. - * - * @param session The session to destroy. - */ -void -msim_session_destroy(MsimSession *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); - } - - /* Stop checking the inbox at the end of the session. */ - if (session->inbox_handle) { - purple_timeout_remove(session->inbox_handle); - } - - g_free(session); -} diff --git a/libpurple/protocols/myspace/session.h b/libpurple/protocols/myspace/session.h deleted file mode 100644 index 34e1ba96cd..0000000000 --- a/libpurple/protocols/myspace/session.h +++ /dev/null @@ -1,60 +0,0 @@ -/* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ - -#ifndef _MYSPACE_SESSION_H -#define _MYSPACE_SESSION_H - -#include "account.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 */ - gboolean show_only_to_list; - int privacy_mode; /**< This is a bitmask */ - int offline_message_mode; - 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 rxsize; /**< Receive buffer size */ - guint next_rid; /**< Next request/response ID */ - time_t last_comm; /**< Time received last communication */ - guint inbox_status; /**< Bit field of inbox notifications */ - guint inbox_handle; /**< The handle for the mail check timer */ -} MsimSession; - -MsimSession *msim_session_new(PurpleAccount *acct); -void msim_session_destroy(MsimSession *session); - -#endif /* !_MYSPACE_SESSION_H */ diff --git a/libpurple/protocols/myspace/user.c b/libpurple/protocols/myspace/user.c deleted file mode 100644 index a2b65fc972..0000000000 --- a/libpurple/protocols/myspace/user.c +++ /dev/null @@ -1,892 +0,0 @@ -/* MySpaceIM Protocol Plugin, header file - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ - -#include "myspace.h" - -static void msim_check_username_availability_cb(PurpleConnection *gc, const char *username_to_check); - -static char *msim_username_to_set; - -/** - * 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(const gchar *band, const gchar *song) -{ - if ((band && *band) || (song && *song)) { - return g_strdup_printf("%s - %s", - (band && *band) ? band : "Unknown Artist", - (song && *song) ? song : "Unknown Song"); - } else { - return NULL; - } -} - -/** - * Get the MsimUser from a PurpleBuddy, optionally creating it if needed. - */ -MsimUser * -msim_get_user_from_buddy(PurpleBuddy *buddy, gboolean create) -{ - MsimUser *user; - - if (!buddy) { - return NULL; - } - - user = purple_buddy_get_protocol_data(buddy); - if (create && !user) { - PurpleBlistNode *node = PURPLE_BLIST_NODE(buddy); - - /* No MsimUser for this buddy; make one. */ - - user = g_new0(MsimUser, 1); - user->buddy = buddy; - user->id = purple_blist_node_get_int(node, "UserID"); - purple_buddy_set_protocol_data(buddy, user); - } - - return user; -} - -void msim_user_free(MsimUser *user) -{ - if (!user) - return; - - if (user->url_data != NULL) - purple_util_fetch_url_cancel(user->url_data); - - g_free(user->client_info); - g_free(user->gender); - g_free(user->location); - g_free(user->headline); - g_free(user->display_name); - g_free(user->username); - g_free(user->band_name); - g_free(user->song_name); - g_free(user->image_url); - g_free(user); -} - -/** - * Find and return an MsimUser * representing a user on the buddy list, or NULL. - */ -MsimUser * -msim_find_user(MsimSession *session, const gchar *username) -{ - PurpleBuddy *buddy; - - buddy = purple_find_buddy(session->account, username); - if (!buddy) { - return NULL; - } - - return msim_get_user_from_buddy(buddy, TRUE); -} - -/** - * Append user information to a PurpleNotifyUserInfo, given an MsimUser. - * Used by msim_tooltip_text() and msim_get_info_cb() to show a user's profile. - */ -void -msim_append_user_info(MsimSession *session, PurpleNotifyUserInfo *user_info, MsimUser *user, gboolean full) -{ - PurplePresence *presence; - gchar *str; - 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); - } - - /* a/s/l...the vitals */ - if (user->age) { - char age[16]; - g_snprintf(age, sizeof(age), "%d", user->age); - purple_notify_user_info_add_pair(user_info, _("Age"), age); - } - - if (user->gender && *user->gender) { - purple_notify_user_info_add_pair(user_info, _("Gender"), user->gender); - } - - if (user->location && *user->location) { - purple_notify_user_info_add_pair(user_info, _("Location"), user->location); - } - - /* Other information */ - if (user->headline && *user->headline) { - purple_notify_user_info_add_pair(user_info, _("Headline"), user->headline); - } - - if (user->buddy != NULL) { - presence = purple_buddy_get_presence(user->buddy); - - if (purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_TUNE)) { - PurpleStatus *status; - const char *artist, *title; - - status = purple_presence_get_status(presence, "tune"); - title = purple_status_get_attr_string(status, PURPLE_TUNE_TITLE); - artist = purple_status_get_attr_string(status, PURPLE_TUNE_ARTIST); - - str = msim_format_now_playing(artist, title); - if (str && *str) { - purple_notify_user_info_add_pair(user_info, _("Song"), str); - } - g_free(str); - } - } - - /* Note: total friends only available if looked up by uid, not username. */ - if (user->total_friends) { - char friends[16]; - g_snprintf(friends, sizeof(friends), "%d", user->total_friends); - purple_notify_user_info_add_pair(user_info, _("Total Friends"), friends); - } - - if (full) { - /* Client information */ - char *client = NULL; - - str = user->client_info; - cv = user->client_cv; - - if (str && cv != 0) { - client = g_strdup_printf("%s (build %d)", str, cv); - } else if (str) { - client = g_strdup(str); - } else if (cv) { - client = g_strdup_printf("Build %d", cv); - } - if (client && *client) - purple_notify_user_info_add_pair(user_info, _("Client Version"), client); - g_free(client); - } - - if (full && user->id) { - /* TODO: link to username, if available */ - char *profile; - purple_notify_user_info_add_section_break(user_info); - if (user->buddy != NULL) - profile = g_strdup_printf("<a href=\"http://myspace.com/%s\">%s</a>", - purple_buddy_get_name(user->buddy), _("View web profile")); - else - profile = g_strdup_printf("<a href=\"http://myspace.com/%d\">%s</a>", - user->id, _("View web profile")); - purple_notify_user_info_add_pair(user_info, NULL, profile); - g_free(profile); - } -} - -/** - * 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 = (MsimUser *)user_data; - const char *name = purple_buddy_get_name(user->buddy); - PurpleAccount *account; - - user->url_data = NULL; - - purple_debug_info("msim_downloaded_buddy_icon", - "Downloaded %" G_GSIZE_FORMAT " bytes\n", len); - - if (!url_text) { - purple_debug_info("msim_downloaded_buddy_icon", - "failed to download icon for %s", - name); - return; - } - - account = purple_buddy_get_account(user->buddy); - purple_buddy_icons_set_for_user(account, name, - g_memdup((gchar *)url_text, len), len, - /* Use URL itself as buddy icon "checksum" (TODO: ETag) */ - user->image_url); /* checksum */ -} - -/** - * Set the currently playing song artist and or title. - * - * @param user User associated with the now playing information. - * - * @param new_artist New artist to set, or NULL/empty to not change artist. - * - * @param new_title New title to set, or NULL/empty to not change title. - * - * If new_artist and new_title are NULL/empty, deactivate PURPLE_STATUS_TUNE. - * - * This function is useful because it lets you set the artist or title - * individually, which purple_prpl_got_user_status() doesn't do. - */ -static void msim_set_artist_or_title(MsimUser *user, const char *new_artist, const char *new_title) -{ - PurplePresence *presence; - PurpleAccount *account; - const char *prev_artist, *prev_title; - const char *name; - - if (user->buddy == NULL) - /* User not on buddy list so nothing to do */ - return; - - prev_artist = NULL; - prev_title = NULL; - - if (new_artist && !*new_artist) - new_artist = NULL; - if (new_title && !*new_title) - new_title = NULL; - - account = purple_buddy_get_account(user->buddy); - name = purple_buddy_get_name(user->buddy); - - if (!new_artist && !new_title) { - purple_prpl_got_user_status_deactive(account, name, "tune"); - return; - } - - presence = purple_buddy_get_presence(user->buddy); - - if (purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_TUNE)) { - PurpleStatus *status; - - status = purple_presence_get_status(presence, "tune"); - prev_title = purple_status_get_attr_string(status, PURPLE_TUNE_TITLE); - prev_artist = purple_status_get_attr_string(status, PURPLE_TUNE_ARTIST); - } - - if (!new_artist) - new_artist = prev_artist; - - if (!new_title) - new_title = prev_title; - - purple_prpl_got_user_status(account, name, "tune", - PURPLE_TUNE_TITLE, new_title, - PURPLE_TUNE_ARTIST, new_artist, - NULL); -} - -/** - * Store a field of information about a buddy. - * - * @param key_str Key to store. - * @param value_str Value string, either user takes ownership of this string - * or it is freed if MsimUser doesn't store the string. - * @param user User to store data in. Existing data will be replaced. - */ -static void -msim_store_user_info_each(const gchar *key_str, gchar *value_str, MsimUser *user) -{ - const char *name = user->buddy ? purple_buddy_get_name(user->buddy) : NULL; - - 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(). */ - user->id = atol(value_str); - g_free(value_str); - if (user->buddy) - { - purple_debug_info("msim", "associating uid %s with username %s\n", key_str, name); - purple_blist_node_set_int(PURPLE_BLIST_NODE(user->buddy), "UserID", user->id); - } - /* Need to store in MsimUser, too? What if not on blist? */ - } else if (g_str_equal(key_str, "Age")) { - user->age = atol(value_str); - g_free(value_str); - } else if (g_str_equal(key_str, "Gender")) { - g_free(user->gender); - user->gender = value_str; - } else if (g_str_equal(key_str, "Location")) { - g_free(user->location); - user->location = value_str; - } else if (g_str_equal(key_str, "TotalFriends")) { - user->total_friends = atol(value_str); - g_free(value_str); - } else if (g_str_equal(key_str, "DisplayName")) { - g_free(user->display_name); - user->display_name = value_str; - } else if (g_str_equal(key_str, "BandName")) { - msim_set_artist_or_title(user, value_str, NULL); - g_free(value_str); - } else if (g_str_equal(key_str, "SongName")) { - msim_set_artist_or_title(user, NULL, value_str); - g_free(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 */ - g_free(value_str); - } else if (g_str_equal(key_str, "ImageURL") || g_str_equal(key_str, "AvatarURL")) { - const gchar *previous_url; - - if (user->temporary_user) { - /* This user will be destroyed soon; don't try to look up its image or avatar, - * since that won't return immediately and we will end up accessing freed data. - */ - g_free(value_str); - return; - } - - g_free(user->image_url); - - user->image_url = 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(purple_buddy_get_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)) { - if (user->url_data != NULL) - purple_util_fetch_url_cancel(user->url_data); - user->url_data = 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); - g_free(value_str); - } else if (g_str_equal(key_str, "Headline")) { - g_free(user->headline); - user->headline = 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); - g_free(value_str); - - msim_unrecognized(NULL, NULL, msg); - - g_free(msg); - } -} - -/** - * Save buddy information to the buddy list from a user info reply message. - * - * @param session - * @param msg The user information reply, with any amount of information. - * @param user The structure to save to, or NULL to save in PurpleBuddy->proto_data. - * - * Variable information is saved to the passed MsimUser structure. Permanent - * information (UserID) is stored in the blist node of the buddy list (and - * ends up in blist.xml, persisted to disk) if it exists. - * - * If the function has no buddy information, this function - * is a no-op (and returns FALSE). - */ -gboolean -msim_store_user_info(MsimSession *session, const MsimMessage *msg, MsimUser *user) -{ - gchar *username; - MsimMessage *body, *body_node; - - g_return_val_if_fail(msg != NULL, FALSE); - - body = msim_msg_get_dictionary(msg, "body"); - if (!body) { - return FALSE; - } - - if (msim_msg_get_integer(msg, "dsn") == (guint)MG_OWN_IM_INFO_DSN && - msim_msg_get_integer(msg, "lid") == (guint)MG_OWN_IM_INFO_LID) - { - /* - * Some of this info will be available on the buddy list if the - * user has themselves as their own buddy. - * - * Much of the info is already available in MsimSession, - * stored in msim_we_are_logged_on(). - */ - gchar *tmpstr; - - tmpstr = msim_msg_get_string(body, "ShowOnlyToList"); - if (tmpstr != NULL) { - session->show_only_to_list = g_str_equal(tmpstr, "True"); - g_free(tmpstr); - } - - session->privacy_mode = msim_msg_get_integer(body, "PrivacyMode"); - session->offline_message_mode = msim_msg_get_integer(body, "OfflineMessageMode"); - - msim_send(session, - "blocklist", MSIM_TYPE_BOOLEAN, TRUE, - "sesskey", MSIM_TYPE_INTEGER, session->sesskey, - "idlist", MSIM_TYPE_STRING, - g_strdup_printf("w%d|c%d", - session->show_only_to_list ? 1 : 0, - session->privacy_mode & 1), - NULL); - } else if (msim_msg_get_integer(msg, "dsn") == (guint)MG_OWN_MYSPACE_INFO_DSN && - msim_msg_get_integer(msg, "lid") == (guint)MG_OWN_MYSPACE_INFO_LID) { - /* TODO: same as above, but for MySpace info. */ - } - - 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); - } - - msim_msg_free(body); - g_free(username); - - return TRUE; -} - -#if 0 -/** - * Return whether a given username is syntactically valid. - * Note: does not actually check that the user exists. - */ -static gboolean -msim_is_valid_username(const gchar *user) -{ - return !msim_is_userid(user) && /* Not all numeric */ - strlen(user) <= MSIM_MAX_USERNAME_LENGTH - && strspn(user, "0123456789" - "abcdefghijklmnopqrstuvwxyz" - "_" - "ABCDEFGHIJKLMNOPQRSTUVWXYZ") == strlen(user); -} -#endif - -/** - * Check if a string is a userid (all numeric). - * - * @param user The user id, email, or name. - * - * @return TRUE if is userid, FALSE if not. - */ -gboolean -msim_is_userid(const gchar *user) -{ - g_return_val_if_fail(user != NULL, FALSE); - - return strspn(user, "0123456789") == strlen(user); -} - -/** - * Check if a string is an email address (contains an @). - * - * @param user The user id, email, or name. - * - * @return TRUE if is an email, FALSE if not. - * - * This function is not intended to be used as a generic - * means of validating email addresses, but to distinguish - * between a user represented by an email address from - * other forms of identification. - */ -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 */ -void -msim_lookup_user(MsimSession *session, const gchar *user, MSIM_USER_LOOKUP_CB cb, gpointer data) -{ - MsimMessage *body; - gchar *field_name; - guint rid, dsn, lid; - - 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); - - /* Setup callback. Response will be associated with request using 'rid'. */ - rid = msim_new_reply_callback(session, cb, data); - - /* Send request */ - - 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, MSIM_CMD_GET, - "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)); -} - -/** - * Called after username is set. - */ -static void msim_username_is_set_cb(MsimSession *session, const MsimMessage *userinfo, gpointer data) -{ - gchar *username; - const gchar *errmsg; - MsimMessage *body; - - guint rid; - gint cmd,dsn,lid,code; - /* \persistr\\cmd\258\dsn\9\uid\204084363\lid\14\rid\369\body\UserName=TheAlbinoRhino1.Code=0\final\ */ - - purple_debug_info("msim","username_is_set made\n"); - - cmd = msim_msg_get_integer(userinfo, "cmd"); - dsn = msim_msg_get_integer(userinfo, "dsn"); -#if 0 - uid = msim_msg_get_integer(userinfo, "uid"); -#endif - lid = msim_msg_get_integer(userinfo, "lid"); - body = msim_msg_get_dictionary(userinfo, "body"); - errmsg = _("An error occurred while trying to set the username. " - "Please try again, or visit http://editprofile.myspace.com/index.cfm?" - "fuseaction=profile.username to set your username."); - - if (!body) { - purple_debug_info("msim_username_is_set_cb", "No body"); - /* Error: No body! */ - purple_connection_error_reason(session->gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR, errmsg); - } - username = msim_msg_get_string(body, "UserName"); - code = msim_msg_get_integer(body,"Code"); - - msim_msg_free(body); - - purple_debug_info("msim_username_is_set_cb", - "cmd = %d, dsn = %d, lid = %d, code = %d, username = %s\n", - cmd, dsn, lid, code, username); - - if (cmd == (MSIM_CMD_BIT_REPLY | MSIM_CMD_PUT) - && dsn == MC_SET_USERNAME_DSN - && lid == MC_SET_USERNAME_LID) - { - purple_debug_info("msim_username_is_set_cb", "Proper cmd,dsn,lid for username_is_set!\n"); - purple_debug_info("msim_username_is_set_cb", "Username Set with return code %d\n",code); - if (code == 0) { - /* Good! */ - session->username = username; - msim_we_are_logged_on(session); - } else { - purple_debug_info("msim_username_is_set", "code is %d",code); - /* TODO: what to do here? */ - } - } else if (cmd == (MSIM_CMD_BIT_REPLY | MSIM_CMD_GET) - && dsn == MG_MYSPACE_INFO_BY_STRING_DSN - && lid == MG_MYSPACE_INFO_BY_STRING_LID) { - /* Not quite done... ONE MORE STEP :) */ - rid = msim_new_reply_callback(session, msim_username_is_set_cb, data); - body = msim_msg_new("UserName", MSIM_TYPE_STRING, g_strdup(username), NULL); - if (!msim_send(session, "persist", MSIM_TYPE_INTEGER, 1, - "sesskey", MSIM_TYPE_INTEGER, session->sesskey, - "cmd", MSIM_TYPE_INTEGER, MSIM_CMD_PUT, - "dsn", MSIM_TYPE_INTEGER, MC_SET_USERNAME_DSN, - "uid", MSIM_TYPE_INTEGER, session->userid, - "lid", MSIM_TYPE_INTEGER, MC_SET_USERNAME_LID, - "rid", MSIM_TYPE_INTEGER, rid, - "body", MSIM_TYPE_DICTIONARY, body, - NULL)) { - /* Error! */ - /* Can't set... Disconnect */ - purple_connection_error_reason(session->gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR, errmsg); - } - - } else { - /* Error! */ - purple_debug_info("msim","username_is_set Error: Invalid cmd/dsn/lid combination"); - purple_connection_error_reason(session->gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR, errmsg); - } -} - -/** - * Asynchronously set new username, calling callback when receive result. - * - * @param session - * @param username The username we're setting for ourselves. Not freed. - * @param cb Callback, called with user information when available. - * @param data An arbitray data pointer passed to the callback. - */ -static void -msim_set_username(MsimSession *session, const gchar *username, - MSIM_USER_LOOKUP_CB cb, gpointer data) -{ - MsimMessage *body; - guint rid; - - g_return_if_fail(username != NULL); - g_return_if_fail(cb != NULL); - - purple_debug_info("msim", "msim_set_username: " - "Setting username %s\n", username); - - /* Setup callback. Response will be associated with request using 'rid'. */ - rid = msim_new_reply_callback(session, cb, data); - - /* TODO: I dont know if the ContactType is -/ALWAYS/- 1 */ - - body = msim_msg_new("UserName", MSIM_TYPE_STRING, g_strdup(username),NULL); -/* \setinfo\\sesskey\469958979\info\Age=21.AvatarUrl=.BandName=.ContactType=1.DisplayName=Msim.Gender=M.ImageURL=http:/1/1x.myspace.com/1images/1no_pic.gif.LastLogin=128335268400000000.Location=US.ShowAvatar=False.SongName=.TotalFriends=1.UserName=msimprpl2\final\ -*/ - - /* Send request */ - g_return_if_fail(msim_send(session, - "setinfo", MSIM_TYPE_BOOLEAN, TRUE, - "sesskey", MSIM_TYPE_INTEGER, session->sesskey, - "info", MSIM_TYPE_DICTIONARY, body, - NULL)); - body = msim_msg_new("UserName", MSIM_TYPE_STRING, g_strdup(username),NULL); - g_return_if_fail(msim_send(session, - "persist", MSIM_TYPE_INTEGER, 1, - "sesskey", MSIM_TYPE_INTEGER, session->sesskey, - "cmd", MSIM_TYPE_INTEGER, MSIM_CMD_GET, - "dsn", MSIM_TYPE_INTEGER, MG_MYSPACE_INFO_BY_STRING_DSN, - "uid", MSIM_TYPE_INTEGER, session->userid, - "lid", MSIM_TYPE_INTEGER, MG_MYSPACE_INFO_BY_STRING_LID, - "rid", MSIM_TYPE_INTEGER, rid, - "body", MSIM_TYPE_DICTIONARY, body, - NULL)); -} - -/** - * They've confirmed that username that was available, Lets make the call to set it - */ -static void msim_set_username_confirmed_cb(PurpleConnection *gc) -{ - MsimMessage *user_msg; - MsimSession *session; - - g_return_if_fail(gc != NULL); - - session = (MsimSession *)gc->proto_data; - - user_msg = msim_msg_new( - "user", MSIM_TYPE_STRING, g_strdup(msim_username_to_set), - NULL); - - purple_debug_info("msim_set_username_confirmed_cb", "Setting username to %s\n", msim_username_to_set); - - /* Sets our username... keep your fingers crossed :) */ - msim_set_username(session, msim_username_to_set, msim_username_is_set_cb, user_msg); - g_free(msim_username_to_set); -} - -/** - * This is where we do a bit more than merely prompt the user. - * Now we have some real data to tell us the state of their requested username - * \persistr\\cmd\257\dsn\5\uid\204084363\lid\7\rid\367\body\UserName=TheAlbinoRhino1\final\ - */ -static void msim_username_is_available_cb(MsimSession *session, const MsimMessage *userinfo, gpointer data) -{ - MsimMessage *msg; - gchar *username; - MsimMessage *body; - gint userid; - - purple_debug_info("msim_username_is_available_cb", "Look up username callback made\n"); - - msg = (MsimMessage *)data; - g_return_if_fail(msg != NULL); - - username = msim_msg_get_string(msg, "user"); - body = msim_msg_get_dictionary(userinfo, "body"); - - if (!body) { - purple_debug_info("msim_username_is_available_cb", "No body for %s?!\n", username); - purple_connection_error_reason(session->gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR, - _("An error occurred while trying to set the username. " - "Please try again, or visit http://editprofile.myspace.com/index.cfm?" - "fuseaction=profile.username to set your username.")); - return; - } - - userid = msim_msg_get_integer(body, "UserID"); - - purple_debug_info("msim_username_is_available_cb", "Returned username is %s and userid is %d\n", username, userid); - msim_msg_free(body); - msim_msg_free(msg); - - /* The response for a free username will ONLY have the UserName in it.. - * thus making UserID return 0 when we msg_get_integer it */ - if (userid == 0) { - /* This username is currently unused */ - purple_debug_info("msim_username_is_available_cb", "Username available. Prompting to Confirm.\n"); - msim_username_to_set = g_strdup(username); - g_free(username); - purple_request_yes_no(session->gc, - _("MySpaceIM - Username Available"), - _("This username is available. Would you like to set it?"), - _("ONCE SET, THIS CANNOT BE CHANGED!"), - 0, - session->account, - NULL, - NULL, - session->gc, - G_CALLBACK(msim_set_username_confirmed_cb), - G_CALLBACK(msim_do_not_set_username_cb)); - } else { - /* Looks like its in use or we have an invalid response */ - purple_debug_info("msim_username_is_available_cb", "Username unavaiable. Prompting for new entry.\n"); - purple_request_input(session->gc, _("MySpaceIM - Please Set a Username"), - _("This username is unavailable."), - _("Please try another username:"), - "", FALSE, FALSE, NULL, - _("OK"), G_CALLBACK(msim_check_username_availability_cb), - _("Cancel"), G_CALLBACK(msim_do_not_set_username_cb), - session->account, - NULL, - NULL, - session->gc); - } -} - -/** - * Once they've submitted their desired new username, - * check if it is available here. - */ -static void msim_check_username_availability_cb(PurpleConnection *gc, const char *username_to_check) -{ - MsimMessage *user_msg; - MsimSession *session; - - g_return_if_fail(gc != NULL); - - session = (MsimSession *)gc->proto_data; - - purple_debug_info("msim_check_username_availability_cb", "Checking username: %s\n", username_to_check); - - user_msg = msim_msg_new( - "user", MSIM_TYPE_STRING, g_strdup(username_to_check), - NULL); - - /* 25 characters: letters, numbers, underscores */ - /* TODO: VERIFY ABOVE */ - - /* \persist\1\sesskey\288500516\cmd\1\dsn\5\uid\204084363\lid\7\rid\367\body\UserName=Jaywalker\final\ */ - /* Official client uses a standard lookup... So do we! */ - msim_lookup_user(session, username_to_check, msim_username_is_available_cb, user_msg); -} - -/*** - * If they hit cancel or no at any point in the Setting Username process, - * we come here. Currently we're safe letting them get by without - * setting it, unless we hear otherwise. So for now give them a menu. - * If this becomes an issue with the official client then boot them here. - */ -void msim_do_not_set_username_cb(PurpleConnection *gc) -{ - purple_debug_info("msim", "Don't set username"); - - /* Protocol won't log in now without a username set.. Disconnect */ - purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED, _("No username set")); -} - -/** - * They've decided to set a username! Yay! - */ -void msim_set_username_cb(PurpleConnection *gc) -{ - g_return_if_fail(gc != NULL); - purple_debug_info("msim","Set username\n"); - purple_request_input(gc, _("MySpaceIM - Please Set a Username"), - _("Please enter a username to check its availability:"), - NULL, - "", FALSE, FALSE, NULL, - _("OK"), G_CALLBACK(msim_check_username_availability_cb), - _("Cancel"), G_CALLBACK(msim_do_not_set_username_cb), - purple_connection_get_account(gc), - NULL, - NULL, - gc); -} diff --git a/libpurple/protocols/myspace/user.h b/libpurple/protocols/myspace/user.h deleted file mode 100644 index acc0c2604d..0000000000 --- a/libpurple/protocols/myspace/user.h +++ /dev/null @@ -1,60 +0,0 @@ -/* MySpaceIM Protocol Plugin, header file - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ - -#ifndef _MYSPACE_USER_H -#define _MYSPACE_USER_H - -/* Hold ephemeral information about buddies, for proto_data of PurpleBuddy. */ -/* GHashTable? */ -typedef struct _MsimUser -{ - PurpleBuddy *buddy; - /* Note: id is also &buddy->node (set_blist_node_int), when buddy is non-NULL */ - int id; - guint client_cv; - gchar *client_info; - guint age; - gchar *gender; - gchar *location; - guint total_friends; - gchar *headline; - gchar *display_name; - gchar *username; - gchar *band_name, *song_name; - gchar *image_url; - guint last_image_updated; - gboolean temporary_user; - PurpleUtilFetchUrlData *url_data; -} MsimUser; - -/* Callback function pointer type for when a user's information is received, - * initiated from a user lookup. */ -typedef void (*MSIM_USER_LOOKUP_CB)(MsimSession *session, const MsimMessage *userinfo, gpointer data); - -MsimUser *msim_get_user_from_buddy(PurpleBuddy *buddy, gboolean create); -void msim_user_free(MsimUser *user); -MsimUser *msim_find_user(MsimSession *session, const gchar *username); -void msim_append_user_info(MsimSession *session, PurpleNotifyUserInfo *user_info, MsimUser *user, gboolean full); -gboolean msim_store_user_info(MsimSession *session, const MsimMessage *msg, MsimUser *user); -gboolean msim_is_userid(const gchar *user); -void msim_lookup_user(MsimSession *session, const gchar *user, MSIM_USER_LOOKUP_CB cb, gpointer data); -void msim_set_username_cb(PurpleConnection *gc); -void msim_do_not_set_username_cb(PurpleConnection *gc); - -#endif /* !_MYSPACE_USER_H */ diff --git a/libpurple/protocols/myspace/zap.c b/libpurple/protocols/myspace/zap.c deleted file mode 100644 index 509b48949d..0000000000 --- a/libpurple/protocols/myspace/zap.c +++ /dev/null @@ -1,245 +0,0 @@ -/* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ - -#include "myspace.h" -#include "zap.h" - -/** Get zap types. */ -GList * -msim_attention_types(PurpleAccount *acct) -{ - static GList *types = NULL; - PurpleAttentionType* attn; - - if (!types) { -#define _MSIM_ADD_NEW_ATTENTION(icn, ulname, nme, incoming, outgoing) \ - attn = purple_attention_type_new(ulname, nme, incoming, outgoing); \ - purple_attention_type_set_icon_name(attn, icn); \ - types = g_list_append(types, attn); - - /* TODO: icons for each zap */ - - /* Lots of comments for translators: */ - - /* Zap means "to strike suddenly and forcefully as if with a - * projectile or weapon." This term often has an electrical - * connotation, for example, "he was zapped by electricity when - * he put a fork in the toaster." */ - _MSIM_ADD_NEW_ATTENTION(NULL, "Zap", _("Zap"), _("%s has zapped you!"), - _("Zapping %s...")); - - /* Whack means "to hit or strike someone with a sharp blow" */ - _MSIM_ADD_NEW_ATTENTION(NULL, "Whack", _("Whack"), - _("%s has whacked you!"), _("Whacking %s...")); - - /* Torch means "to set on fire." Don't worry, this doesn't - * make a whole lot of sense in English, either. Feel free - * to translate it literally. */ - _MSIM_ADD_NEW_ATTENTION(NULL, "Torch", _("Torch"), - _("%s has torched you!"), _("Torching %s...")); - - /* Smooch means "to kiss someone, often enthusiastically" */ - _MSIM_ADD_NEW_ATTENTION(NULL, "Smooch", _("Smooch"), - _("%s has smooched you!"), _("Smooching %s...")); - - /* A hug is a display of affection; wrapping your arms around someone */ - _MSIM_ADD_NEW_ATTENTION(NULL, "Hug", _("Hug"), _("%s has hugged you!"), - _("Hugging %s...")); - - /* Slap means "to hit someone with an open/flat hand" */ - _MSIM_ADD_NEW_ATTENTION(NULL, "Slap", _("Slap"), - _("%s has slapped you!"), _("Slapping %s...")); - - /* Goose means "to pinch someone on their butt" */ - _MSIM_ADD_NEW_ATTENTION(NULL, "Goose", _("Goose"), - _("%s has goosed you!"), _("Goosing %s...")); - - /* A high-five is when two people's hands slap each other - * in the air above their heads. It is done to celebrate - * something, often a victory, or to congratulate someone. */ - _MSIM_ADD_NEW_ATTENTION(NULL, "High-five", _("High-five"), - _("%s has high-fived you!"), _("High-fiving %s...")); - - /* We're not entirely sure what the MySpace people mean by - * this... but we think it's the equivalent of "prank." Or, for - * someone to perform a mischievous trick or practical joke. */ - _MSIM_ADD_NEW_ATTENTION(NULL, "Punk", _("Punk"), - _("%s has punk'd you!"), _("Punking %s...")); - - /* Raspberry is a slang term for the vibrating sound made - * when you stick your tongue out of your mouth with your - * lips closed and blow. It is typically done when - * gloating or bragging. Nowadays it's a pretty silly - * gesture, so it does not carry a harsh negative - * connotation. It is generally used in a playful tone - * with friends. */ - _MSIM_ADD_NEW_ATTENTION(NULL, "Raspberry", _("Raspberry"), - _("%s has raspberried you!"), _("Raspberrying %s...")); - } - - return types; -} - -/** Send a zap to a user. */ -static gboolean -msim_send_zap(MsimSession *session, const gchar *username, guint code) -{ - gchar *zap_string; - gboolean rc; - - g_return_val_if_fail(session != NULL, FALSE); - g_return_val_if_fail(username != NULL, FALSE); - - /* 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_OR_IM_INSTANT)) { - purple_debug_info("msim_send_zap", - "msim_send_bm failed: zapping %s with %s\n", - username, zap_string); - rc = FALSE; - } else { - rc = TRUE; - } - - g_free(zap_string); - - return rc; -} - -/** Send a zap */ -gboolean -msim_send_attention(PurpleConnection *gc, const gchar *username, guint code) -{ - GList *types; - MsimSession *session; - PurpleAttentionType *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 = (PurpleAttentionType *)g_list_nth_data(types, code); - - if (!attn) { - purple_debug_info("msim_send_attention", "got invalid zap code %d\n", code); - return FALSE; - } - - buddy = purple_find_buddy(session->account, username); - if (!buddy) { - return FALSE; - } - - msim_send_zap(session, username, code); - - return TRUE; -} - -/** 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 = purple_buddy_get_account(buddy); - gc = purple_account_get_connection(account); - session = (MsimSession *)gc->proto_data; - - zap = GPOINTER_TO_INT(zap_num_ptr); - - purple_prpl_send_attention(session->gc, purple_buddy_get_name(buddy), 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; - guint i; - - if (!PURPLE_BLIST_NODE_IS_BUDDY(node)) { - /* Only know about buddies for now. */ - return NULL; - } - - zap_menu = NULL; - - /* TODO: get rid of once is accessible directly in GUI */ - types = msim_attention_types(NULL); - i = 0; - for (; types; types = g_list_next(types)) { - PurpleAttentionType *attn; - - attn = (PurpleAttentionType *)types->data; - - act = purple_menu_action_new(purple_attention_type_get_name(attn), - PURPLE_CALLBACK(msim_send_zap_from_menu), GUINT_TO_POINTER(i), NULL); - zap_menu = g_list_append(zap_menu, act); - - ++i; - } - - act = purple_menu_action_new(_("Zap"), NULL, NULL, zap_menu); - menu = g_list_append(NULL, act); - - return menu; -} - -/** Process an incoming zap. */ -gboolean -msim_incoming_zap(MsimSession *session, MsimMessage *msg) -{ - gchar *msg_text, *username; - gint zap; - - msg_text = msim_msg_get_string(msg, "msg"); - username = msim_msg_get_string(msg, "_username"); - - g_return_val_if_fail(msg_text != NULL, FALSE); - g_return_val_if_fail(username != NULL, FALSE); - - g_return_val_if_fail(sscanf(msg_text, "!!!ZAP_SEND!!!=RTE_BTN_ZAPS_%d", &zap) == 1, FALSE); - - zap = CLAMP(zap, 0, 9); - - purple_prpl_got_attention(session->gc, username, zap); - - g_free(msg_text); - g_free(username); - - return TRUE; -} diff --git a/libpurple/protocols/myspace/zap.h b/libpurple/protocols/myspace/zap.h deleted file mode 100644 index d6bf52c364..0000000000 --- a/libpurple/protocols/myspace/zap.h +++ /dev/null @@ -1,28 +0,0 @@ -/* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 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 */ diff --git a/libpurple/prpl.h b/libpurple/prpl.h index 9584aa370e..2a7471119e 100644 --- a/libpurple/prpl.h +++ b/libpurple/prpl.h @@ -922,7 +922,7 @@ GList *purple_prpl_get_statuses(PurpleAccount *account, PurplePresence *presence * @param who Whose attention to request. * @param type_code An index into the prpl's attention_types list determining the type * of the attention request command to send. 0 if prpl only defines one - * (for example, Yahoo), but some protocols define more (MySpaceIM). + * (for example, Yahoo), but protocols are allowed to define more. * * Note that you can't send arbitrary PurpleAttentionType's, because there is * only a fixed set of attention commands. diff --git a/libpurple/purple-url-handler b/libpurple/purple-url-handler index 9c269abefa..74e635d909 100755 --- a/libpurple/purple-url-handler +++ b/libpurple/purple-url-handler @@ -224,11 +224,6 @@ def irc(uri): channel = "#" + channel gochat(account, {"server": server, "channel": channel, "password": params.get("key", "")}, params.get("msg")) -def myim(uri): - protocol = "prpl-myspace" - print "TODO: send uri: ", uri - assert False, "Not implemented" - def sip(uri): protocol = "prpl-simple" match = re.match(r"^sip:(.*)", uri) @@ -353,8 +348,6 @@ def main(argv=sys.argv): icq(uri) elif type == "irc": irc(uri) - elif type == "myim": - myim(uri) elif type == "sip": sip(uri) elif type == "xmpp": diff --git a/libpurple/server.h b/libpurple/server.h index f2c5134624..aa10ed10bb 100644 --- a/libpurple/server.h +++ b/libpurple/server.h @@ -69,7 +69,7 @@ PurpleAttentionType *purple_get_attention_type_from_code(PurpleAccount *account, * @param who Whose attention to request. * @param type_code An index into the prpl's attention_types list determining the type * of the attention request command to send. 0 if prpl only defines one - * (for example, Yahoo), but some protocols define more (MySpaceIM). + * (for example, Yahoo), but protocols are allowed to define more. * * Note that you can't send arbitrary PurpleAttentionType's, because there is * only a fixed set of attention commands. diff --git a/pidgin/pixmaps/Makefile.am b/pidgin/pixmaps/Makefile.am index 4b32dcad4d..8a112c0f50 100644 --- a/pidgin/pixmaps/Makefile.am +++ b/pidgin/pixmaps/Makefile.am @@ -225,7 +225,6 @@ PROTOCOLS_16 = \ protocols/16/irc.png \ protocols/16/jabber.png \ protocols/16/meanwhile.png \ - protocols/16/myspace.png \ protocols/16/silc.png \ protocols/16/simple.png \ protocols/16/yahoo.png \ @@ -277,7 +276,6 @@ PROTOCOLS_22 = \ protocols/22/irc.png \ protocols/22/jabber.png \ protocols/22/meanwhile.png \ - protocols/22/myspace.png \ protocols/22/silc.png \ protocols/22/simple.png \ protocols/22/yahoo.png \ @@ -293,7 +291,6 @@ PROTOCOLS_48 = \ protocols/48/irc.png \ protocols/48/jabber.png \ protocols/48/meanwhile.png \ - protocols/48/myspace.png \ protocols/48/silc.png \ protocols/48/simple.png \ protocols/48/yahoo.png \ diff --git a/pidgin/pixmaps/emotes/default/24/default.theme.in b/pidgin/pixmaps/emotes/default/24/default.theme.in index dad4508a39..c0a7f7273b 100644 --- a/pidgin/pixmaps/emotes/default/24/default.theme.in +++ b/pidgin/pixmaps/emotes/default/24/default.theme.in @@ -422,39 +422,6 @@ male-fighter2.png o=> O=> female-fighter.png o-+ O-+ yin-yang.png (%) - -# Following MySpaceIM Beta 1.0.697.0 -[MySpaceIM] -excited.png :D :-D -devil.png }:) -confused.png :Z -glasses-nerdy.png B) -bulgy-eyes.png %) -freaked-out.png :E -happy.png :) :-) -amorous.png :X -laugh.png :)) -mohawk.png -: -mad-tongue.png X( -messed.png X) -glasses-nerdy.png Q) -doh.png :G -pirate.png P) -shocked.png :O -sidefrown.png :{ -sinister.png :B -smirk.png :, -neutral.png :| -tongue.png :P :p -pissed-off.png B| -wink.png ;-) ;) -sad.png :[ -kiss.png :x -! skywalker.png C:-) c:-) C:) c:) -! monkey.png :-(|) :(|) 8-|) -! cyclops.png O-) o-) - - # MXit standard emoticons [MXit] happy.png :-) :) diff --git a/pidgin/pixmaps/emotes/small/16/small.theme.in b/pidgin/pixmaps/emotes/small/16/small.theme.in index 36aa59cc1c..366ccfdc49 100644 --- a/pidgin/pixmaps/emotes/small/16/small.theme.in +++ b/pidgin/pixmaps/emotes/small/16/small.theme.in @@ -213,23 +213,6 @@ shame.png [-X [-x musical-note.png :-" star.png (*) - -# Following MySpaceIM Beta 1.0.697.0 -[MySpaceIM] -excited.png :D :-D -devil.png }:) -confused.png :Z -happy.png :) :-) -amorous.png :X -pirate.png P) -shocked.png :O -neutral.png :| -tongue.png :P :p -pissed-off.png B| -wink.png ;-) ;) -sad.png :[ -kiss.png :x - # MXit standard emoticons [MXit] happy.png :-) :) diff --git a/pidgin/pixmaps/protocols/16/myspace.png b/pidgin/pixmaps/protocols/16/myspace.png Binary files differdeleted file mode 100644 index e7c3034cc9..0000000000 --- a/pidgin/pixmaps/protocols/16/myspace.png +++ /dev/null diff --git a/pidgin/pixmaps/protocols/22/myspace.png b/pidgin/pixmaps/protocols/22/myspace.png Binary files differdeleted file mode 100644 index 2b6020b58c..0000000000 --- a/pidgin/pixmaps/protocols/22/myspace.png +++ /dev/null diff --git a/pidgin/pixmaps/protocols/48/myspace.png b/pidgin/pixmaps/protocols/48/myspace.png Binary files differdeleted file mode 100644 index d9740ef5ff..0000000000 --- a/pidgin/pixmaps/protocols/48/myspace.png +++ /dev/null diff --git a/pidgin/plugins/disco/xmppdisco.c b/pidgin/plugins/disco/xmppdisco.c index 07efa34fc4..50bc368bdd 100644 --- a/pidgin/plugins/disco/xmppdisco.c +++ b/pidgin/plugins/disco/xmppdisco.c @@ -252,7 +252,6 @@ static const struct { } disco_type_mappings[] = { { "gadu-gadu", "gadu-gadu" }, /* the prpl is prpl-gg, but list_icon returns "gadu-gadu" */ { "sametime", "meanwhile" }, - { "myspaceim", "myspace" }, { "xmpp", "jabber" }, /* prpl-jabber (mentioned in case the prpl is renamed so this line will match) */ { NULL, NULL } }; diff --git a/pidgin/win32/nsis/pidgin-installer.nsi b/pidgin/win32/nsis/pidgin-installer.nsi index 601f90ebed..a18d4f7922 100644 --- a/pidgin/win32/nsis/pidgin-installer.nsi +++ b/pidgin/win32/nsis/pidgin-installer.nsi @@ -388,7 +388,6 @@ SectionGroupEnd !macroend SectionGroup /e $(URIHANDLERSSECTIONTITLE) SecURIHandlers !insertmacro URI_SECTION "aim" - !insertmacro URI_SECTION "myim" !insertmacro URI_SECTION "ymsgr" !insertmacro URI_SECTION "xmpp" SectionGroupEnd diff --git a/po/POTFILES.in b/po/POTFILES.in index 5e93928af2..a2773188d4 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -107,9 +107,6 @@ libpurple/protocols/jabber/si.c libpurple/protocols/jabber/usermood.c libpurple/protocols/jabber/usernick.c libpurple/protocols/jabber/xdata.c -libpurple/protocols/myspace/myspace.c -libpurple/protocols/myspace/user.c -libpurple/protocols/myspace/zap.c libpurple/protocols/novell/nmuser.c libpurple/protocols/novell/novell.c libpurple/protocols/oscar/authorization.c |