# HG changeset patch # User Mark Doliner # Date 1171350231 0 # Node ID 2f0f563b3579a2afb5c3a1f67082e77614b85d45 # Parent 66dc2367b137a7dbdd496607e63ad2c78b667f07# Parent 2261750cf4e910934089b126811cf6f5ab7d7971 merge of '7822ac971c736a7e6545da828b1b79db67ad732f' and 'edc9bd8e3fe860539f170f241e440e214993fcaf' diff -r 66dc2367b137 -r 2f0f563b3579 COPYRIGHT --- a/COPYRIGHT Tue Feb 13 05:00:49 2007 +0000 +++ b/COPYRIGHT Tue Feb 13 07:03:51 2007 +0000 @@ -245,6 +245,7 @@ Havoc Pennington Ted Percival Eduardo Pérez +Matt Perry Celso Pinto Joao Luís Marques Pinto Aleksander Piotrowski diff -r 66dc2367b137 -r 2f0f563b3579 ChangeLog --- a/ChangeLog Tue Feb 13 05:00:49 2007 +0000 +++ b/ChangeLog Tue Feb 13 07:03:51 2007 +0000 @@ -193,7 +193,7 @@ * Sametime protocol support Requires the meanwhile library: http://meanwhile.sourceforge.net * QQ protocol support (Mark Huetsch, Google Summer of Code) - * Removed support for the Napster and TOC protocols + * Removed the Napster and TOC protocols plugins Other Noteworthy Changes: * UPnP and NAT traversal support (Adam J. Warrington, Google Summer of @@ -1809,7 +1809,7 @@ * Redesigned preferences dialog * Redesigned conversation dialog * Removed the Lag-O-Meter (Lag-O-Meter is now a plugin) - * SOCKS 4a/5 proxy works + * SOCKS 4/5 proxy works * Buddy Pounces are now saved in .gaimrc * Buddy Chats are now saved in .gaimrc * Ability to merge gaim, aim2, aim4 buddylists. Thanks again bmiller! @@ -1879,7 +1879,7 @@ lists, unfortunately). (Thanks Syd) * Font selection dialog * Small changes to the Oscar/libfaim stuff (see libfaim/README.gaim) - * SOCKS 4a proxy support + * SOCKS 4 proxy support * Better proxy support overall (you can get people's info now! :) ) * Two-way file transfer (you can get and send files, but you still can't initiate either) diff -r 66dc2367b137 -r 2f0f563b3579 ChangeLog.API --- a/ChangeLog.API Tue Feb 13 05:00:49 2007 +0000 +++ b/ChangeLog.API Tue Feb 13 07:03:51 2007 +0000 @@ -256,6 +256,7 @@ * gaim_account_supports_offline_message() * gaim_conversation_close_logs(), to force a conversation's log(s) to be closed. New logs will be opened as necessary. + * gaim_got_protocol_handler_uri() * gaim_plugin_get_id() * gaim_plugin_get_name() * gaim_plugin_get_version() @@ -417,6 +418,7 @@ * "log-displaying" * "savedstatus-changed" * "sendto-extended-menu" + * "uri-handler" Signals - Removed: * "account-away": replaced by account-status-changed diff -r 66dc2367b137 -r 2f0f563b3579 configure.ac --- a/configure.ac Tue Feb 13 05:00:49 2007 +0000 +++ b/configure.ac Tue Feb 13 07:03:51 2007 +0000 @@ -1901,6 +1901,7 @@ doc/gaim-text.1 m4macros/Makefile pidgin/Makefile + pidgin/pidgin.pc pidgin/pixmaps/Makefile pidgin/pixmaps/animations/Makefile pidgin/pixmaps/animations/16/Makefile @@ -1945,7 +1946,9 @@ pidgin/plugins/perl/common/Makefile.PL pidgin/plugins/ticker/Makefile pidgin/sounds/Makefile + libpurple/example/Makefile libpurple/gconf/Makefile + libpurple/purple.pc libpurple/plugins/Makefile libpurple/plugins/mono/Makefile libpurple/plugins/mono/api/Makefile @@ -1978,6 +1981,7 @@ console/plugins/Makefile po/Makefile.in gaim.pc + gaim-uninstalled.pc gaim.spec ]) diff -r 66dc2367b137 -r 2f0f563b3579 console/libgnt/gnt.pc.in --- a/console/libgnt/gnt.pc.in Tue Feb 13 05:00:49 2007 +0000 +++ b/console/libgnt/gnt.pc.in Tue Feb 13 07:03:51 2007 +0000 @@ -2,11 +2,12 @@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=@includedir@ +datarootdir=@datarootdir@ datadir=@datadir@ sysconfdir=@sysconfdir@ Name: LibGNT -Description: Gaim Ncurses Toolkit is a collection of curses-widgets. +Description: Glib Ncurses Toolkit is a collection of curses-widgets. Version: @VERSION@ Requires: glib-2.0 Cflags: -I${includedir}/gnt diff -r 66dc2367b137 -r 2f0f563b3579 gaim-uninstalled.pc.in --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gaim-uninstalled.pc.in Tue Feb 13 07:03:51 2007 +0000 @@ -0,0 +1,14 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ +datarootdir=@datarootdir@ +datadir=@datadir@ +sysconfdir=@sysconfdir@ + +Name: Gaim +Description: Gaim is a GTK2-based instant messenger application. +Version: @VERSION@ +Requires: glib-2.0 +Cflags: -I${pc_top_builddir}/${pcfiledir}/libpurple -I${pc_top_builddir}/${pcfiledir}/pidgin +Libs: ${pc_top_builddir}/${pcfiledir}/libpurple/libpurple.la diff -r 66dc2367b137 -r 2f0f563b3579 gaim.pc.in --- a/gaim.pc.in Tue Feb 13 05:00:49 2007 +0000 +++ b/gaim.pc.in Tue Feb 13 07:03:51 2007 +0000 @@ -2,6 +2,7 @@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=@includedir@ +datarootdir=@datarootdir@ datadir=@datadir@ sysconfdir=@sysconfdir@ diff -r 66dc2367b137 -r 2f0f563b3579 libpurple/Makefile.am --- a/libpurple/Makefile.am Tue Feb 13 05:00:49 2007 +0000 +++ b/libpurple/Makefile.am Tue Feb 13 07:03:51 2007 +0000 @@ -6,6 +6,7 @@ gaim-send \ gaim-send-async \ gaim-url-handler \ + purple.pc.in \ Makefile.mingw \ win32/global.mak \ win32/libc_interface.c \ @@ -23,7 +24,10 @@ GCONF_DIR=gconf endif -SUBDIRS = $(GCONF_DIR) plugins protocols tests +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = purple.pc + +SUBDIRS = $(GCONF_DIR) plugins protocols tests . example gaim_coresources = \ account.c \ diff -r 66dc2367b137 -r 2f0f563b3579 libpurple/core.c --- a/libpurple/core.c Tue Feb 13 05:00:49 2007 +0000 +++ b/libpurple/core.c Tue Feb 13 07:03:51 2007 +0000 @@ -83,6 +83,13 @@ /* The signals subsystem is important and should be first. */ gaim_signals_init(); + gaim_signal_register(core, "uri-handler", + gaim_marshal_BOOLEAN__POINTER_POINTER_POINTER, + gaim_value_new(GAIM_TYPE_BOOLEAN), 3, + gaim_value_new(GAIM_TYPE_STRING), /* Protocol */ + gaim_value_new(GAIM_TYPE_STRING), /* Command */ + gaim_value_new(GAIM_TYPE_BOXED, "GHashTable *")); /* Parameters */ + gaim_signal_register(core, "quitting", gaim_marshal_VOID, NULL, 0); /* The prefs subsystem needs to be initialized before static protocols diff -r 66dc2367b137 -r 2f0f563b3579 libpurple/example/Makefile.am --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/example/Makefile.am Tue Feb 13 07:03:51 2007 +0000 @@ -0,0 +1,25 @@ +bin_PROGRAMS = nullclient + +nullclient_SOURCES = nullclient.c +nullclient_DEPENDENCIES = +nullclient_LDFLAGS = -export-dynamic +nullclient_LDADD = \ + $(DBUS_LIBS) \ + $(INTLLIBS) \ + $(GLIB_LIBS) \ + $(LIBXML_LIBS) \ + $(top_builddir)/libpurple/libpurple.la + +AM_CPPFLAGS = \ + -DSTANDALONE \ + -DBR_PTHREADS=0 \ + -DDATADIR=\"$(datadir)\" \ + -DLIBDIR=\"$(libdir)/libpurple/\" \ + -DLOCALEDIR=\"$(datadir)/locale\" \ + -DSYSCONFDIR=\"$(sysconfdir)\" \ + -I$(top_srcdir)/libpurple/ \ + -I$(top_srcdir) \ + $(DEBUG_CFLAGS) \ + $(GLIB_CFLAGS) \ + $(DBUS_CFLAGS) \ + $(LIBXML_CFLAGS) diff -r 66dc2367b137 -r 2f0f563b3579 libpurple/example/defines.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/example/defines.h Tue Feb 13 07:03:51 2007 +0000 @@ -0,0 +1,4 @@ +#define CUSTOM_USER_DIRECTORY "/dev/null" +#define CUSTOM_PLUGIN_PATH "" +#define PLUGIN_SAVE_PREF "/gaim/nullclient/plugins/saved" +#define UI_ID "nullclient" diff -r 66dc2367b137 -r 2f0f563b3579 libpurple/example/nullclient.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/example/nullclient.c Tue Feb 13 07:03:51 2007 +0000 @@ -0,0 +1,276 @@ +/* + * pidgin + * + * Pidgin is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#include "account.h" +#include "conversation.h" +#include "core.h" +#include "debug.h" +#include "eventloop.h" +#include "ft.h" +#include "log.h" +#include "notify.h" +#include "prefix.h" +#include "prefs.h" +#include "prpl.h" +#include "pounce.h" +#include "savedstatuses.h" +#include "sound.h" +#include "status.h" +#include "util.h" +#include "whiteboard.h" + +#include + +#include +#include + +#include "defines.h" + +/** + * The following eventloop functions are used in both pidgin and gaim-text. If your + * application uses glib mainloop, you can safely use this verbatim. + */ +#define GAIM_GLIB_READ_COND (G_IO_IN | G_IO_HUP | G_IO_ERR) +#define GAIM_GLIB_WRITE_COND (G_IO_OUT | G_IO_HUP | G_IO_ERR | G_IO_NVAL) + +typedef struct _GaimGLibIOClosure { + GaimInputFunction function; + guint result; + gpointer data; +} GaimGLibIOClosure; + +static void gaim_glib_io_destroy(gpointer data) +{ + g_free(data); +} + +static gboolean gaim_glib_io_invoke(GIOChannel *source, GIOCondition condition, gpointer data) +{ + GaimGLibIOClosure *closure = data; + GaimInputCondition gaim_cond = 0; + + if (condition & GAIM_GLIB_READ_COND) + gaim_cond |= GAIM_INPUT_READ; + if (condition & GAIM_GLIB_WRITE_COND) + gaim_cond |= GAIM_INPUT_WRITE; + + closure->function(closure->data, g_io_channel_unix_get_fd(source), + gaim_cond); + + return TRUE; +} + +static guint glib_input_add(gint fd, GaimInputCondition condition, GaimInputFunction function, + gpointer data) +{ + GaimGLibIOClosure *closure = g_new0(GaimGLibIOClosure, 1); + GIOChannel *channel; + GIOCondition cond = 0; + + closure->function = function; + closure->data = data; + + if (condition & GAIM_INPUT_READ) + cond |= GAIM_GLIB_READ_COND; + if (condition & GAIM_INPUT_WRITE) + cond |= GAIM_GLIB_WRITE_COND; + + channel = g_io_channel_unix_new(fd); + closure->result = g_io_add_watch_full(channel, G_PRIORITY_DEFAULT, cond, + gaim_glib_io_invoke, closure, gaim_glib_io_destroy); + + g_io_channel_unref(channel); + return closure->result; +} + +static GaimEventLoopUiOps glib_eventloops = +{ + g_timeout_add, + g_source_remove, + glib_input_add, + g_source_remove +}; +/*** End of the eventloop functions. ***/ + +/*** Conversation uiops ***/ +static void +null_write_conv(GaimConversation *conv, const char *who, const char *alias, + const char *message, GaimMessageFlags flags, time_t mtime) +{ + const char *name; + if (alias && *alias) + name = alias; + else if (who && *who) + name = who; + else + name = NULL; + + printf("(%s) %s %s: %s\n", gaim_conversation_get_name(conv), + gaim_utf8_strftime("(%H:%M:%S)", localtime(&mtime)), + name, message); +} + +static GaimConversationUiOps null_conv_uiops = +{ + .write_conv = null_write_conv +}; + +static void +null_ui_init() +{ + /** + * This should initialize the UI components for all the modules. Here we + * just initialize the UI for conversations. + */ + gaim_conversations_set_ui_ops(&null_conv_uiops); +} + +static GaimCoreUiOps null_core_uiops = +{ + NULL, + NULL, + null_ui_init, + NULL +}; + +static void +init_libpurple() +{ + /* Set a custom user directory (optional) */ + gaim_util_set_user_dir(CUSTOM_USER_DIRECTORY); + + /* We do not want any debugging for now to keep the noise to a minimum. */ + gaim_debug_set_enabled(FALSE); + + /* Set the core-uiops, which is used to + * - initialize the ui specific preferences. + * - initialize the debug ui. + * - initialize the ui components for all the modules. + * - uninitialize the ui components for all the modules when the core terminates. + */ + gaim_core_set_ui_ops(&null_core_uiops); + + /* Set the uiops for the eventloop. If your client is glib-based, you can safely + * copy this verbatim. */ + gaim_eventloop_set_ui_ops(&glib_eventloops); + + /* Set path to search for plugins. The core (libpurple) takes care of loading the + * core-plugins, which includes the protocol-plugins. So it is not essential to add + * any path here, but it might be desired, especially for ui-specific plugins. */ + gaim_plugins_add_search_path(CUSTOM_PLUGIN_PATH); + + /* Now that all the essential stuff has been set, let's try to init the core. It's + * necessary to provide a non-NULL name for the current ui to the core. This name + * is used by stuff that depends on this ui, for example the ui-specific plugins. */ + if (!gaim_core_init(UI_ID)) { + /* Initializing the core failed. Terminate. */ + fprintf(stderr, + "libpurple initialization failed. Dumping core.\n" + "Please report this!\n"); + abort(); + } + + /* Create and load the buddylist. */ + gaim_set_blist(gaim_blist_new()); + gaim_blist_load(); + + /* Load the preferences. */ + gaim_prefs_load(); + + /* Load the desired plugins. The client should save the list of loaded plugins in + * the preferences using gaim_plugins_save_loaded(PLUGIN_SAVE_PREF) */ + gaim_plugins_load_saved(PLUGIN_SAVE_PREF); + + /* Load the pounces. */ + gaim_pounces_load(); +} + +static void +signed_on(GaimConnection *gc, gpointer null) +{ + GaimAccount *account = gaim_connection_get_account(gc); + printf("Account connected: %s %s\n", account->username, account->protocol_id); +} + +static void +connect_to_signals_for_demonstration_purposes_only() +{ + static int handle; + gaim_signal_connect(gaim_connections_get_handle(), "signed-on", &handle, + GAIM_CALLBACK(signed_on), NULL); +} + +int main() +{ + GList *iter; + int i, num; + GList *names = NULL; + const char *prpl; + char name[128]; + char *password; + GMainLoop *loop = g_main_loop_new(NULL, FALSE); + GaimAccount *account; + GaimSavedStatus *status; + + init_libpurple(); + + printf("libpurple initialized.\n"); + + iter = gaim_plugins_get_protocols(); + for (i = 0; iter; iter = iter->next) { + GaimPlugin *plugin = iter->data; + GaimPluginInfo *info = plugin->info; + if (info && info->name) { + printf("\t%d: %s\n", i++, info->name); + names = g_list_append(names, info->id); + } + } + printf("Select the protocol [0-%d]: ", i-1); + fgets(name, sizeof(name), stdin); + sscanf(name, "%d", &num); + prpl = g_list_nth_data(names, num); + + printf("Username: "); + fgets(name, sizeof(name), stdin); + name[strlen(name) - 1] = 0; /* strip the \n at the end */ + + /* Create the account */ + account = gaim_account_new(name, prpl); + + /* Get the password for the account */ + password = getpass("Password: "); + gaim_account_set_password(account, password); + + /* It's necessary to enable the account first. */ + gaim_account_set_enabled(account, UI_ID, TRUE); + + /* Now, to connect the account(s), create a status and activate it. */ + status = gaim_savedstatus_new(NULL, GAIM_STATUS_AVAILABLE); + gaim_savedstatus_activate(status); + + connect_to_signals_for_demonstration_purposes_only(); + + g_main_loop_run(loop); + + return 0; +} + diff -r 66dc2367b137 -r 2f0f563b3579 libpurple/log.c --- a/libpurple/log.c Tue Feb 13 05:00:49 2007 +0000 +++ b/libpurple/log.c Tue Feb 13 07:03:51 2007 +0000 @@ -1016,7 +1016,9 @@ gboolean gaim_log_common_is_deletable(GaimLog *log) { GaimLogCommonLoggerData *data; +#ifndef _WIN32 gchar *dirname; +#endif g_return_val_if_fail(log != NULL, FALSE); @@ -1435,7 +1437,7 @@ static char *txt_logger_read(GaimLog *log, GaimLogReadFlags *flags) { - char *read, *minus_header, *minus_header2; + char *read, *minus_header; GaimLogCommonLoggerData *data = log->logger_data; *flags = 0; if (!data || !data->path) @@ -1745,7 +1747,7 @@ { struct old_logger_data *data = log->logger_data; FILE *file = g_fopen(gaim_stringref_value(data->pathref), "rb"); - char *tmp, *read = g_malloc(data->length + 1); + char *read = g_malloc(data->length + 1); fseek(file, data->offset, SEEK_SET); fread(read, data->length, 1, file); fclose(file); diff -r 66dc2367b137 -r 2f0f563b3579 libpurple/plugins/perl/common/Makefile.mingw --- a/libpurple/plugins/perl/common/Makefile.mingw Tue Feb 13 05:00:49 2007 +0000 +++ b/libpurple/plugins/perl/common/Makefile.mingw Tue Feb 13 07:03:51 2007 +0000 @@ -12,7 +12,7 @@ EXTUTILS ?= C:/perl/lib/ExtUtils PERL_PLUGIN_TOP := .. -CFLAGS += -Wno-comment +CFLAGS += -Wno-comment -Wno-unused ## ## INCLUDE PATHS diff -r 66dc2367b137 -r 2f0f563b3579 libpurple/protocols/msn/msn.c --- a/libpurple/protocols/msn/msn.c Tue Feb 13 05:00:49 2007 +0000 +++ b/libpurple/protocols/msn/msn.c Tue Feb 13 07:03:51 2007 +0000 @@ -35,6 +35,7 @@ #include "state.h" #include "util.h" #include "cmds.h" +#include "core.h" #include "prpl.h" #include "msn-utils.h" #include "version.h" @@ -1928,6 +1929,71 @@ return TRUE; } +static GaimAccount *find_acct(const char *prpl, const char *acct_id) +{ + GaimAccount *acct = NULL; + + /* If we have a specific acct, use it */ + if (acct_id) { + acct = gaim_accounts_find(acct_id, prpl); + if (acct && !gaim_account_is_connected(acct)) + acct = NULL; + } else { /* Otherwise find an active account for the protocol */ + GList *l = gaim_accounts_get_all(); + while (l) { + if (!strcmp(prpl, gaim_account_get_protocol_id(l->data)) + && gaim_account_is_connected(l->data)) { + acct = l->data; + break; + } + l = l->next; + } + } + + return acct; +} + +static gboolean msn_uri_handler(const char *proto, const char *cmd, GHashTable *params) +{ + char *acct_id = g_hash_table_lookup(params, "account"); + GaimAccount *acct; + + if (g_ascii_strcasecmp(proto, "msnim")) + return FALSE; + + acct = find_acct("prpl-msn", acct_id); + + if (!acct) + return FALSE; + + /* msnim:chat?contact=user@domain.tld */ + if (!g_ascii_strcasecmp(cmd, "Chat")) { + char *sname = g_hash_table_lookup(params, "contact"); + if (sname) { + GaimConversation *conv = gaim_find_conversation_with_account( + GAIM_CONV_TYPE_IM, sname, acct); + if (conv == NULL) + conv = gaim_conversation_new(GAIM_CONV_TYPE_IM, acct, sname); + gaim_conversation_present(conv); + } + /*else + **If pidgindialogs_im() was in the core, we could use it here. + * It is all gaim_request_* based, but I'm not sure it really belongs in the core + pidgindialogs_im();*/ + + return TRUE; + } + /* msnim:add?contact=user@domain.tld */ + else if (!g_ascii_strcasecmp(cmd, "Add")) { + char *name = g_hash_table_lookup(params, "contact"); + gaim_blist_request_add_buddy(acct, name, NULL, NULL); + return TRUE; + } + + return FALSE; +} + + static GaimPluginProtocolInfo prpl_info = { OPT_PROTO_MAIL_CHECK, @@ -2055,6 +2121,9 @@ _("nudge: nudge a user to get their attention"), NULL); gaim_prefs_remove("/plugins/prpl/msn"); + + gaim_signal_connect(gaim_get_core(), "uri-handler", plugin, + GAIM_CALLBACK(msn_uri_handler), NULL); } GAIM_INIT_PLUGIN(msn, init_plugin, info); diff -r 66dc2367b137 -r 2f0f563b3579 libpurple/protocols/oscar/family_oservice.c --- a/libpurple/protocols/oscar/family_oservice.c Tue Feb 13 05:00:49 2007 +0000 +++ b/libpurple/protocols/oscar/family_oservice.c Tue Feb 13 07:03:51 2007 +0000 @@ -779,8 +779,8 @@ return 1; } -/* - * Subtype 0x001e - Set various account settings (mostly ICQ related). +/** + * Subtype 0x001e - Extended Status/Extra Info. * * These settings are transient, not server-stored (i.e. they only * apply to this session, and must be re-set the next time you sign @@ -791,36 +791,76 @@ * if your status is visible on ICQ web sites, and you can set * your IP address info and what not. * + * You can also set your "available" message. This is currently + * only supported by iChat, Gaim and other 3rd party clients. + * * These are the same TLVs seen in user info. You can * also set 0x0008 and 0x000c. - * - * TODO: Combine this with the function below. */ int -aim_srv_setextstatus(OscarData *od, guint32 status) +aim_srv_setextrainfo(OscarData *od, + gboolean seticqstatus, guint32 icqstatus, + gboolean setavailmsg, const char *availmsg, const char *itmsurl) { FlapConnection *conn; FlapFrame *frame; aim_snacid_t snacid; aim_tlvlist_t *tl = NULL; - guint32 data; if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM))) return -EINVAL; - data = AIM_ICQ_STATE_HIDEIP | AIM_ICQ_STATE_DIRECTREQUIREAUTH | status; + if (seticqstatus) + { + aim_tlvlist_add_32(&tl, 0x0006, icqstatus | + AIM_ICQ_STATE_HIDEIP | AIM_ICQ_STATE_DIRECTREQUIREAUTH); + } + +#if 0 + if (other_stuff_that_isnt_implemented) + { + aim_tlvlist_add_raw(&tl, 0x000c, 0x0025, + chunk_of_x25_bytes_with_ip_address_etc); + aim_tlvlist_add_raw(&tl, 0x0011, 0x0005, unknown 0x01 61 10 f6 41); + aim_tlvlist_add_16(&tl, 0x0012, unknown 0x00 00); + } +#endif + + if (setavailmsg) + { + int availmsglen, itmsurllen; + ByteStream tmpbs; + + availmsglen = (availmsg != NULL) ? strlen(availmsg) : 0; + itmsurllen = (itmsurl != NULL) ? strlen(itmsurl) : 0; - frame = flap_frame_new(od, 0x02, 10 + 8); + byte_stream_new(&tmpbs, availmsglen + 8 + itmsurllen + 8); + byte_stream_put16(&tmpbs, 0x0002); + byte_stream_put8(&tmpbs, 0x04); /* Flags */ + byte_stream_put8(&tmpbs, availmsglen + 4); + byte_stream_put16(&tmpbs, availmsglen); + if (availmsglen > 0) + byte_stream_putstr(&tmpbs, availmsg); + byte_stream_put16(&tmpbs, 0x0000); + + byte_stream_put16(&tmpbs, 0x0009); + byte_stream_put8(&tmpbs, 0x04); /* Flags */ + byte_stream_put8(&tmpbs, itmsurllen + 4); + byte_stream_put16(&tmpbs, itmsurllen); + if (itmsurllen > 0) + byte_stream_putstr(&tmpbs, itmsurl); + byte_stream_put16(&tmpbs, 0x0000); + + aim_tlvlist_add_raw(&tl, 0x001d, + byte_stream_curpos(&tmpbs), tmpbs.data); + g_free(tmpbs.data); + } + + frame = flap_frame_new(od, 0x02, 10 + aim_tlvlist_size(&tl)); snacid = aim_cachesnac(od, 0x0001, 0x001e, 0x0000, NULL, 0); aim_putsnac(&frame->data, 0x0001, 0x001e, 0x0000, snacid); - aim_tlvlist_add_32(&tl, 0x0006, data); -#if 0 - aim_tlvlist_add_raw(&tl, 0x000c, 0x0025, chunk_of_x25_bytes_with_ip_address_etc); - aim_tlvlist_add_raw(&tl, 0x0011, 0x0005, unknown 0x01 61 10 f6 41); - aim_tlvlist_add_16(&tl, 0x0012, unknown 0x00 00); -#endif aim_tlvlist_write(&frame->data, &tl); aim_tlvlist_free(&tl); @@ -829,54 +869,7 @@ return 0; } -/* - * Subtype 0x001e - Extended Status. - * - * Sets your "available" message. This is currently only supported by iChat - * and Gaim. - * - * These are the same TLVs seen in user info. You can - * also set 0x0008 and 0x000c. - * - * TODO: Combine this with the above function. - */ -int -aim_srv_setstatusmsg(OscarData *od, const char *msg) -{ - FlapConnection *conn; - FlapFrame *frame; - aim_snacid_t snacid; - int msglen; - - if (!od || !(conn = flap_connection_findbygroup(od, 0x0004))) - return -EINVAL; - - if (msg == NULL) - msglen = 0; - else - msglen = strlen(msg); - - frame = flap_frame_new(od, 0x02, 10 + 4 + msglen + 8); - - snacid = aim_cachesnac(od, 0x0001, 0x001e, 0x0000, NULL, 0); - aim_putsnac(&frame->data, 0x0001, 0x001e, 0x0000, snacid); - - byte_stream_put16(&frame->data, 0x001d); /* userinfo TLV type */ - byte_stream_put16(&frame->data, msglen + 8); /* total length of userinfo TLV data */ - byte_stream_put16(&frame->data, 0x0002); - byte_stream_put8(&frame->data, 0x04); - byte_stream_put8(&frame->data, msglen+4); - byte_stream_put16(&frame->data, msglen); - if (msglen > 0) - byte_stream_putstr(&frame->data, msg); - byte_stream_put16(&frame->data, 0x0000); - - flap_connection_send(conn, frame); - - return 0; -} - -/* +/** * Starting this past week (26 Mar 2001, say), AOL has started sending * this nice little extra SNAC. AFAIK, it has never been used until now. * diff -r 66dc2367b137 -r 2f0f563b3579 libpurple/protocols/oscar/libaim.c --- a/libpurple/protocols/oscar/libaim.c Tue Feb 13 05:00:49 2007 +0000 +++ b/libpurple/protocols/oscar/libaim.c Tue Feb 13 07:03:51 2007 +0000 @@ -128,24 +128,7 @@ static void init_plugin(GaimPlugin *plugin) { - GaimAccountOption *option; - - option = gaim_account_option_string_new(_("Server"), "server", OSCAR_DEFAULT_LOGIN_SERVER); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); - - option = gaim_account_option_int_new(_("Port"), "port", OSCAR_DEFAULT_LOGIN_PORT); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); - - option = gaim_account_option_bool_new( - _("Always use AIM/ICQ proxy server for file transfers\n(slower, but does not reveal your IP address)"), "always_use_rv_proxy", - OSCAR_DEFAULT_ALWAYS_USE_RV_PROXY); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); - - /* Preferences */ - gaim_prefs_add_none("/plugins/prpl/oscar"); - gaim_prefs_add_bool("/plugins/prpl/oscar/recent_buddies", FALSE); - gaim_prefs_add_bool("/plugins/prpl/oscar/show_idle", FALSE); - gaim_prefs_remove("/plugins/prpl/oscar/always_use_rv_proxy"); + oscar_init(GAIM_PLUGIN_PROTOCOL_INFO(plugin)); } GAIM_INIT_PLUGIN(aim, init_plugin, info); diff -r 66dc2367b137 -r 2f0f563b3579 libpurple/protocols/oscar/libicq.c --- a/libpurple/protocols/oscar/libicq.c Tue Feb 13 05:00:49 2007 +0000 +++ b/libpurple/protocols/oscar/libicq.c Tue Feb 13 07:03:51 2007 +0000 @@ -130,25 +130,10 @@ { GaimAccountOption *option; - option = gaim_account_option_string_new(_("Server"), "server", OSCAR_DEFAULT_LOGIN_SERVER); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); - - option = gaim_account_option_int_new(_("Port"), "port", OSCAR_DEFAULT_LOGIN_PORT); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); + oscar_init(GAIM_PLUGIN_PROTOCOL_INFO(plugin)); option = gaim_account_option_string_new(_("Encoding"), "encoding", OSCAR_DEFAULT_CUSTOM_ENCODING); prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); - - option = gaim_account_option_bool_new( - _("Always use ICQ proxy server for file transfers\n(slower, but does not reveal your IP address)"), "always_use_rv_proxy", - OSCAR_DEFAULT_ALWAYS_USE_RV_PROXY); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); - - /* Preferences */ - gaim_prefs_add_none("/plugins/prpl/oscar"); - gaim_prefs_add_bool("/plugins/prpl/oscar/recent_buddies", FALSE); - gaim_prefs_add_bool("/plugins/prpl/oscar/show_idle", FALSE); - gaim_prefs_remove("/plugins/prpl/oscar/always_use_rv_proxy"); } GAIM_INIT_PLUGIN(icq, init_plugin, info); diff -r 66dc2367b137 -r 2f0f563b3579 libpurple/protocols/oscar/oscar.c --- a/libpurple/protocols/oscar/oscar.c Tue Feb 13 05:00:49 2007 +0000 +++ b/libpurple/protocols/oscar/oscar.c Tue Feb 13 07:03:51 2007 +0000 @@ -1786,14 +1786,14 @@ GaimBuddy *b = gaim_find_buddy(account, info->sn); GaimStatus *status; const char *active_status_id; - + status = gaim_presence_get_active_status(gaim_buddy_get_presence(b)); active_status_id = gaim_status_get_id(status); - + if (!active_status_id || strcmp(active_status_id, status_id)) gaim_prpl_got_user_status(account, info->sn, status_id, NULL); } - + /* Login time stuff */ if (info->present & AIM_USERINFO_PRESENT_ONLINESINCE) signon = info->onlinesince; @@ -3569,7 +3569,7 @@ GaimConnection *gc; GaimAccount *account; GaimStatus *status; - const char *message; + const char *message, *itmsurl; char *tmp; va_list ap; guint16 maxpermits, maxdenies; @@ -3605,7 +3605,8 @@ else message = NULL; tmp = gaim_markup_strip_html(message); - aim_srv_setstatusmsg(od, tmp); + itmsurl = gaim_status_get_attr_string(status, "itmsurl"); + aim_srv_setextrainfo(od, FALSE, 0, TRUE, tmp, itmsurl); g_free(tmp); aim_srv_setidle(od, 0); @@ -4411,7 +4412,7 @@ else if (!strcmp(status_id, OSCAR_STATUS_ID_CUSTOM)) data |= AIM_ICQ_STATE_OUT | AIM_ICQ_STATE_AWAY; - aim_srv_setextstatus(od, data); + aim_srv_setextrainfo(od, TRUE, data, FALSE, NULL, NULL); } static void @@ -4477,7 +4478,7 @@ } else if (primitive == GAIM_STATUS_AVAILABLE) { - const char *status_html; + const char *status_html, *itmsurl; char *status_text = NULL; status_html = gaim_status_get_attr_string(status, "message"); @@ -4491,8 +4492,9 @@ strcpy(tmp, "..."); } } - - aim_srv_setstatusmsg(od, status_text); + itmsurl = gaim_status_get_attr_string(status, "itmsurl"); + + aim_srv_setextrainfo(od, FALSE, 0, TRUE, status_text, itmsurl); g_free(status_text); /* This is needed for us to un-set any previous away message. */ @@ -5702,6 +5704,8 @@ OSCAR_STATUS_ID_AVAILABLE, NULL, TRUE, TRUE, FALSE, "message", _("Message"), + gaim_value_new(GAIM_TYPE_STRING), + "itmsurl", _("iTunes Music Store Link"), gaim_value_new(GAIM_TYPE_STRING), NULL); status_types = g_list_prepend(status_types, type); @@ -6514,3 +6518,130 @@ return (od->icq && aim_sn_is_icq(gaim_account_get_username(account))); } +/* TODO: Find somewhere to put this instead of including it in a bunch of places. + * Maybe just change gaim_accounts_find() to return anything for the prpl if there is no acct_id. + */ +static GaimAccount *find_acct(const char *prpl, const char *acct_id) +{ + GaimAccount *acct = NULL; + + /* If we have a specific acct, use it */ + if (acct_id) { + acct = gaim_accounts_find(acct_id, prpl); + if (acct && !gaim_account_is_connected(acct)) + acct = NULL; + } else { /* Otherwise find an active account for the protocol */ + GList *l = gaim_accounts_get_all(); + while (l) { + if (!strcmp(prpl, gaim_account_get_protocol_id(l->data)) + && gaim_account_is_connected(l->data)) { + acct = l->data; + break; + } + l = l->next; + } + } + + return acct; +} + + +static gboolean oscar_uri_handler(const char *proto, const char *cmd, GHashTable *params) +{ + char *acct_id = g_hash_table_lookup(params, "account"); + char prpl[11]; + GaimAccount *acct; + + if (g_ascii_strcasecmp(proto, "aim") && g_ascii_strcasecmp(proto, "icq")) + return FALSE; + + g_snprintf(prpl, sizeof(prpl), "prpl-%s", proto); + + acct = find_acct(prpl, acct_id); + + if (!acct) + return FALSE; + + /* aim:GoIM?screenname=SCREENNAME&message=MESSAGE */ + if (!g_ascii_strcasecmp(cmd, "GoIM")) { + char *sname = g_hash_table_lookup(params, "screenname"); + if (sname) { + char *message = g_hash_table_lookup(params, "message"); + + GaimConversation *conv = gaim_find_conversation_with_account( + GAIM_CONV_TYPE_IM, sname, acct); + if (conv == NULL) + conv = gaim_conversation_new(GAIM_CONV_TYPE_IM, acct, sname); + gaim_conversation_present(conv); + + if (message) { + /* Spaces are encoded as '+' */ + g_strdelimit(message, "+", ' '); + gaim_conv_im_send(GAIM_CONV_IM(conv), message); + } + } + /*else + **If pidgindialogs_im() was in the core, we could use it here. + * It is all gaim_request_* based, but I'm not sure it really belongs in the core + pidgindialogs_im();*/ + + return TRUE; + } + /* aim:GoChat?roomname=CHATROOMNAME&exchange=4 */ + else if (!g_ascii_strcasecmp(cmd, "GoChat")) { + char *rname = g_hash_table_lookup(params, "roomname"); + if (rname) { + /* This is somewhat hacky, but the params aren't useful after this command */ + g_hash_table_insert(params, g_strdup("exchange"), g_strdup("4")); + g_hash_table_insert(params, g_strdup("room"), g_strdup(rname)); + serv_join_chat(gaim_account_get_connection(acct), params); + } + /*else + ** Same as above (except that this would have to be re-written using gaim_request_*) + pidgin_blist_joinchat_show(); */ + + return TRUE; + } + /* aim:AddBuddy?screenname=SCREENNAME&groupname=GROUPNAME*/ + else if (!g_ascii_strcasecmp(cmd, "AddBuddy")) { + char *sname = g_hash_table_lookup(params, "screenname"); + char *gname = g_hash_table_lookup(params, "groupname"); + gaim_blist_request_add_buddy(acct, sname, gname, NULL); + return TRUE; + } + + return FALSE; +} + +void oscar_init(GaimPluginProtocolInfo *prpl_info) +{ + GaimAccountOption *option; + static gboolean init = FALSE; + + option = gaim_account_option_string_new(_("Server"), "server", OSCAR_DEFAULT_LOGIN_SERVER); + prpl_info->protocol_options = g_list_append(prpl_info->protocol_options, option); + + option = gaim_account_option_int_new(_("Port"), "port", OSCAR_DEFAULT_LOGIN_PORT); + prpl_info->protocol_options = g_list_append(prpl_info->protocol_options, option); + + option = gaim_account_option_bool_new( + _("Always use ICQ proxy server for file transfers\n(slower, but does not reveal your IP address)"), "always_use_rv_proxy", + OSCAR_DEFAULT_ALWAYS_USE_RV_PROXY); + prpl_info->protocol_options = g_list_append(prpl_info->protocol_options, option); + + if (init) + return; + init = TRUE; + + /* Preferences */ + gaim_prefs_add_none("/plugins/prpl/oscar"); + gaim_prefs_add_bool("/plugins/prpl/oscar/recent_buddies", FALSE); + gaim_prefs_add_bool("/plugins/prpl/oscar/show_idle", FALSE); + gaim_prefs_remove("/plugins/prpl/oscar/always_use_rv_proxy"); + + /* protocol handler */ + /* TODO: figure out a good instance to use here */ + gaim_signal_connect(gaim_get_core(), "uri-handler", &init, + GAIM_CALLBACK(oscar_uri_handler), NULL); +} + diff -r 66dc2367b137 -r 2f0f563b3579 libpurple/protocols/oscar/oscar.h --- a/libpurple/protocols/oscar/oscar.h Tue Feb 13 05:00:49 2007 +0000 +++ b/libpurple/protocols/oscar/oscar.h Tue Feb 13 07:03:51 2007 +0000 @@ -625,8 +625,7 @@ /* 0x0014 */ void aim_srv_setprivacyflags(OscarData *od, FlapConnection *conn, guint32); /* 0x0016 */ void aim_srv_nop(OscarData *od, FlapConnection *conn); /* 0x0017 */ void aim_srv_setversions(OscarData *od, FlapConnection *conn); -/* 0x001e */ int aim_srv_setstatusmsg(OscarData *od, const char *msg); -/* 0x001e */ int aim_srv_setextstatus(OscarData *od, guint32 status); +/* 0x001e */ int aim_srv_setextrainfo(OscarData *od, gboolean seticqstatus, guint32 icqstatus, gboolean setavailmsg, const char *availmsg, const char *itmsurl); void aim_bos_reqrights(OscarData *od, FlapConnection *conn); diff -r 66dc2367b137 -r 2f0f563b3579 libpurple/protocols/oscar/oscarcommon.h --- a/libpurple/protocols/oscar/oscarcommon.h Tue Feb 13 05:00:49 2007 +0000 +++ b/libpurple/protocols/oscar/oscarcommon.h Tue Feb 13 07:03:51 2007 +0000 @@ -87,3 +87,4 @@ GaimXfer *oscar_new_xfer(GaimConnection *gc, const char *who); gboolean oscar_offline_message(const GaimBuddy *buddy); GList *oscar_actions(GaimPlugin *plugin, gpointer context); +void oscar_init(GaimPluginProtocolInfo *prpl_info); diff -r 66dc2367b137 -r 2f0f563b3579 libpurple/protocols/yahoo/yahoo.c --- a/libpurple/protocols/yahoo/yahoo.c Tue Feb 13 05:00:49 2007 +0000 +++ b/libpurple/protocols/yahoo/yahoo.c Tue Feb 13 07:03:51 2007 +0000 @@ -28,6 +28,7 @@ #include "blist.h" #include "cipher.h" #include "cmds.h" +#include "core.h" #include "debug.h" #include "notify.h" #include "privacy.h" @@ -3794,6 +3795,107 @@ _("doodle: Request user to start a Doodle session"), NULL); } +static GaimAccount *find_acct(const char *prpl, const char *acct_id) +{ + GaimAccount *acct = NULL; + + /* If we have a specific acct, use it */ + if (acct_id) { + acct = gaim_accounts_find(acct_id, prpl); + if (acct && !gaim_account_is_connected(acct)) + acct = NULL; + } else { /* Otherwise find an active account for the protocol */ + GList *l = gaim_accounts_get_all(); + while (l) { + if (!strcmp(prpl, gaim_account_get_protocol_id(l->data)) + && gaim_account_is_connected(l->data)) { + acct = l->data; + break; + } + l = l->next; + } + } + + return acct; +} + +/* This may not be the best way to do this, but we find the first key w/o a value + * and assume it is the screenname */ +static void yahoo_find_uri_novalue_param(gpointer key, gpointer value, gpointer user_data) +{ + char **retval = user_data; + + if (value == NULL && *retval == NULL) { + *retval = key; + } +} + +static gboolean yahoo_uri_handler(const char *proto, const char *cmd, GHashTable *params) +{ + char *acct_id = g_hash_table_lookup(params, "account"); + GaimAccount *acct; + + if (g_ascii_strcasecmp(proto, "ymsgr")) + return FALSE; + + acct = find_acct(gaim_plugin_get_id(my_protocol), acct_id); + + if (!acct) + return FALSE; + + /* ymsgr:SendIM?screename&m=The+Message */ + if (!g_ascii_strcasecmp(cmd, "SendIM")) { + char *sname = NULL; + g_hash_table_foreach(params, yahoo_find_uri_novalue_param, &sname); + if (sname) { + char *message = g_hash_table_lookup(params, "m"); + + GaimConversation *conv = gaim_find_conversation_with_account( + GAIM_CONV_TYPE_IM, sname, acct); + if (conv == NULL) + conv = gaim_conversation_new(GAIM_CONV_TYPE_IM, acct, sname); + gaim_conversation_present(conv); + + if (message) { + /* Spaces are encoded as '+' */ + g_strdelimit(message, "+", ' '); + gaim_conv_im_send(GAIM_CONV_IM(conv), message); + } + } + /*else + **If pidgindialogs_im() was in the core, we could use it here. + * It is all gaim_request_* based, but I'm not sure it really belongs in the core + pidgindialogs_im();*/ + + return TRUE; + } + /* ymsgr:Chat?roomname */ + else if (!g_ascii_strcasecmp(cmd, "Chat")) { + char *rname = NULL; + g_hash_table_foreach(params, yahoo_find_uri_novalue_param, &rname); + if (rname) { + /* This is somewhat hacky, but the params aren't useful after this command */ + g_hash_table_insert(params, g_strdup("room"), g_strdup(rname)); + g_hash_table_insert(params, g_strdup("type"), g_strdup("Chat")); + serv_join_chat(gaim_account_get_connection(acct), params); + } + /*else + ** Same as above (except that this would have to be re-written using gaim_request_*) + pidgin_blist_joinchat_show(); */ + + return TRUE; + } + /* ymsgr:AddFriend?name */ + else if (!g_ascii_strcasecmp(cmd, "AddFriend")) { + char *name = NULL; + g_hash_table_foreach(params, yahoo_find_uri_novalue_param, &name); + gaim_blist_request_add_buddy(acct, name, NULL, NULL); + return TRUE; + } + + return FALSE; +} + static GaimWhiteboardPrplOps yahoo_whiteboard_prpl_ops = { yahoo_doodle_start, @@ -3950,6 +4052,9 @@ my_protocol = plugin; yahoogaim_register_commands(); yahoo_init_colorht(); + + gaim_signal_connect(gaim_get_core(), "uri-handler", plugin, + GAIM_CALLBACK(yahoo_uri_handler), NULL); } GAIM_INIT_PLUGIN(yahoo, init_plugin, info); diff -r 66dc2367b137 -r 2f0f563b3579 libpurple/purple.pc.in --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/purple.pc.in Tue Feb 13 07:03:51 2007 +0000 @@ -0,0 +1,14 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ +datadir=@datadir@ +sysconfdir=@sysconfdir@ + +Name: libpurple +Description: libpurple is a GLib-based instant messenger library. +Version: @VERSION@ +Requires: glib-2.0 +Cflags: -I${includedir}/libpurple +Libs: -L${libdir} -lpurple + diff -r 66dc2367b137 -r 2f0f563b3579 libpurple/upnp.c --- a/libpurple/upnp.c Tue Feb 13 05:00:49 2007 +0000 +++ b/libpurple/upnp.c Tue Feb 13 07:03:51 2007 +0000 @@ -352,7 +352,7 @@ start = start ? start + 3 : baseURL; path = strchr(start, '/'); length = path ? path - baseURL : strlen(baseURL); - controlURL = g_strdup_printf("%.*s%s", length, baseURL, tmp); + controlURL = g_strdup_printf("%.*s%s", (int)length, baseURL, tmp); } else { controlURL = g_strdup_printf("%s%s", baseURL, tmp); } diff -r 66dc2367b137 -r 2f0f563b3579 libpurple/util.c --- a/libpurple/util.c Tue Feb 13 05:00:49 2007 +0000 +++ b/libpurple/util.c Tue Feb 13 07:03:51 2007 +0000 @@ -23,6 +23,7 @@ #include "internal.h" #include "conversation.h" +#include "core.h" #include "debug.h" #include "notify.h" #include "prpl.h" @@ -2995,6 +2996,69 @@ /************************************************************************** * URI/URL Functions **************************************************************************/ + +void gaim_got_protocol_handler_uri(const char *uri) +{ + char proto[11]; + const char *tmp, *param_string; + char *cmd; + GHashTable *params = NULL; + int len; + + if (!(tmp = strchr(uri, ':')) || tmp == uri) { + gaim_debug_error("util", "Malformed protocol handler message - missing protocol.\n"); + return; + } + + len = MIN(sizeof(proto) - 1, (tmp - uri)); + + strncpy(proto, uri, len); + proto[len] = '\0'; + + tmp++; + gaim_debug_info("util", "Processing message '%s' for protocol '%s'.\n", tmp, proto); + + if ((param_string = strchr(tmp, '?'))) { + const char *keyend = NULL, *pairstart; + char *key, *value = NULL; + + cmd = g_strndup(tmp, (param_string - tmp)); + param_string++; + + params = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); + pairstart = tmp = param_string; + + while (*tmp || *pairstart) { + if (*tmp == '&' || !(*tmp)) { + /* If there is no explicit value */ + if (keyend == NULL) + keyend = tmp; + + if (keyend && keyend != pairstart) { + key = g_strndup(pairstart, (keyend - pairstart)); + /* If there is an explicit value */ + if (keyend != tmp && keyend != (tmp - 1)) + value = g_strndup(keyend + 1, (tmp - keyend - 1)); + g_hash_table_insert(params, key, value); + } + keyend = value = NULL; + pairstart = (*tmp) ? tmp + 1 : tmp; + } else if (*tmp == '=') + keyend = tmp; + + if (*tmp) + tmp++; + } + } else + cmd = g_strdup(tmp); + + gaim_signal_emit_return_1(gaim_get_core(), "uri-handler", proto, cmd, params); + + g_free(cmd); + if (params) + g_hash_table_destroy(params); +} + gboolean gaim_url_parse(const char *url, char **ret_host, int *ret_port, char **ret_path, char **ret_user, char **ret_passwd) diff -r 66dc2367b137 -r 2f0f563b3579 libpurple/util.h --- a/libpurple/util.h Tue Feb 13 05:00:49 2007 +0000 +++ b/libpurple/util.h Tue Feb 13 07:03:51 2007 +0000 @@ -817,6 +817,8 @@ /**************************************************************************/ /*@{*/ +void gaim_got_protocol_handler_uri(const char *uri); + /** * Parses a URL, returning its host, port, file path, username and password. * diff -r 66dc2367b137 -r 2f0f563b3579 pidgin/Makefile.am --- a/pidgin/Makefile.am Tue Feb 13 05:00:49 2007 +0000 +++ b/pidgin/Makefile.am Tue Feb 13 07:03:51 2007 +0000 @@ -3,6 +3,7 @@ getopt.h \ getopt1.c \ Makefile.mingw \ + pidgin.pc.in \ win32/IdleTracker/Makefile.mingw \ win32/IdleTracker/idletrack.c \ win32/IdleTracker/idletrack.h \ @@ -57,6 +58,9 @@ if ENABLE_GTK +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = pidgin.pc + SUBDIRS = pixmaps plugins sounds bin_PROGRAMS = pidgin diff -r 66dc2367b137 -r 2f0f563b3579 pidgin/Makefile.mingw --- a/pidgin/Makefile.mingw Tue Feb 13 05:00:49 2007 +0000 +++ b/pidgin/Makefile.mingw Tue Feb 13 07:03:51 2007 +0000 @@ -135,7 +135,7 @@ all: $(EXE_TARGET).exe $(GTKGAIM_TARGET).dll $(MAKE) -C $(GAIM_GTK_PLUGINS_TOP) -f $(GAIM_WIN32_MAKEFILE) -./win32/pidgin_exe_rc.rc: ./win32/pidgin_exe_rc.rc.in $(GAIM_TOP)/VERSION +win32/pidgin_exe_rc.rc: win32/pidgin_exe_rc.rc.in $(GAIM_TOP)/VERSION sed -e 's/@GAIM_VERSION@/$(GAIM_VERSION)/g' \ -e 's/@ORIGINAL_FILENAME@/$(EXE_NAME)/' \ $@.in > $@ @@ -150,7 +150,7 @@ $(MAKE) -C $(GAIM_GTK_SOUNDS_TOP) -f $(GAIM_WIN32_MAKEFILE) install $(MAKE) -C $(GAIM_GTK_IDLETRACK_TOP) -f $(GAIM_WIN32_MAKEFILE) install -./win32/pidgin_dll_rc.rc: ./win32/pidgin_dll_rc.rc.in $(GAIM_TOP)/VERSION +win32/pidgin_dll_rc.rc: win32/pidgin_dll_rc.rc.in $(GAIM_TOP)/VERSION sed -e 's/@GAIM_VERSION@/$(GAIM_VERSION)/g' \ $@.in > $@ diff -r 66dc2367b137 -r 2f0f563b3579 pidgin/gtkblist.c --- a/pidgin/gtkblist.c Tue Feb 13 05:00:49 2007 +0000 +++ b/pidgin/gtkblist.c Tue Feb 13 07:03:51 2007 +0000 @@ -302,7 +302,7 @@ chat->account); if (conv != NULL) - pidgin_conv_present_conversation(conv); + gaim_conversation_present(conv); serv_join_chat(chat->account->gc, chat->components); } @@ -3549,7 +3549,7 @@ convs = pidgin_conversations_find_unseen_list(GAIM_CONV_TYPE_IM, PIDGIN_UNSEEN_TEXT, TRUE, 1); if (convs) { - pidgin_conv_present_conversation((GaimConversation*)convs->data); + gaim_conversation_present((GaimConversation*)convs->data); g_list_free(convs); } break; diff -r 66dc2367b137 -r 2f0f563b3579 pidgin/gtkconv.c --- a/pidgin/gtkconv.c Tue Feb 13 05:00:49 2007 +0000 +++ b/pidgin/gtkconv.c Tue Feb 13 07:03:51 2007 +0000 @@ -4932,7 +4932,9 @@ /* TODO: These colors should not be hardcoded so log.c can use them */ - if (flags & GAIM_MESSAGE_SYSTEM) { + if (flags & GAIM_MESSAGE_RAW) { + gtk_imhtml_append_text(GTK_IMHTML(gtkconv->imhtml), message, gtk_font_options_all); + } else if (flags & GAIM_MESSAGE_SYSTEM) { g_snprintf(buf2, sizeof(buf2), "%s", sml_attrib ? sml_attrib : "", mdate, displaying); @@ -4952,8 +4954,6 @@ sml_attrib ? sml_attrib : "", displaying); gtk_imhtml_append_text(GTK_IMHTML(gtkconv->imhtml), buf2, gtk_font_options_all); - } else if (flags & GAIM_MESSAGE_RAW) { - gtk_imhtml_append_text(GTK_IMHTML(gtkconv->imhtml), displaying, gtk_font_options_all); } else { char *new_message = g_memdup(displaying, length); char *alias_escaped = (alias ? g_markup_escape_text(alias, strlen(alias)) : g_strdup("")); @@ -7255,7 +7255,7 @@ GtkWidget *page; GtkWidget *tab; - if (e->button == 2) { + if (e->button == 2 && e->type == GDK_BUTTON_PRESS) { PidginConversation *gtkconv; tab_clicked = pidgin_conv_get_tab_at_xy(win, e->x_root, e->y_root, NULL); diff -r 66dc2367b137 -r 2f0f563b3579 pidgin/gtkdialogs.c --- a/pidgin/gtkdialogs.c Tue Feb 13 05:00:49 2007 +0000 +++ b/pidgin/gtkdialogs.c Tue Feb 13 07:03:51 2007 +0000 @@ -724,7 +724,7 @@ if (conv == NULL) conv = gaim_conversation_new(GAIM_CONV_TYPE_IM, account, username); - pidgin_conv_present_conversation(conv); + gaim_conversation_present(conv); } static gboolean diff -r 66dc2367b137 -r 2f0f563b3579 pidgin/gtkdocklet.c --- a/pidgin/gtkdocklet.c Tue Feb 13 05:00:49 2007 +0000 +++ b/pidgin/gtkdocklet.c Tue Feb 13 07:03:51 2007 +0000 @@ -587,7 +587,7 @@ if (status == DOCKLET_STATUS_ONLINE_PENDING || status == DOCKLET_STATUS_AWAY_PENDING) { GList *l = get_pending_list(1); if (l != NULL) { - pidgin_conv_present_conversation((GaimConversation *)l->data); + gaim_conversation_present((GaimConversation *)l->data); g_list_free(l); } } else { diff -r 66dc2367b137 -r 2f0f563b3579 pidgin/pidgin.pc.in --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pidgin/pidgin.pc.in Tue Feb 13 07:03:51 2007 +0000 @@ -0,0 +1,13 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ +datadir=@datadir@ +sysconfdir=@sysconfdir@ + +Name: Pidgin +Description: Pidgin is a GTK2-based instant messenger application. +Version: @VERSION@ +Requires: glib-2.0 gtk+-2.0 purple +Cflags: -I${includedir}/pidgin + diff -r 66dc2367b137 -r 2f0f563b3579 pidgin/win32/gtkwin32dep.c --- a/pidgin/win32/gtkwin32dep.c Tue Feb 13 05:00:49 2007 +0000 +++ b/pidgin/win32/gtkwin32dep.c Tue Feb 13 07:03:51 2007 +0000 @@ -49,6 +49,7 @@ #include "gtkwin32dep.h" #include "win32dep.h" #include "gtkconv.h" +#include "util.h" #include "wspell.h" /* @@ -192,14 +193,20 @@ winpidgin_shell_execute(uri, "open", "http"); } -#define WM_FOCUS_REQUEST (WM_APP + 13) +#define PIDGIN_WM_FOCUS_REQUEST (WM_APP + 13) +#define PIDGIN_WM_PROTOCOL_HANDLE (WM_APP + 14) static LRESULT CALLBACK message_window_handler(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { - if (msg == WM_FOCUS_REQUEST) { + if (msg == PIDGIN_WM_FOCUS_REQUEST) { gaim_debug_info("winpidgin", "Got external Buddy List focus request."); gaim_blist_set_visible(TRUE); return TRUE; + } else if (msg == PIDGIN_WM_PROTOCOL_HANDLE) { + char *proto_msg = (char *) lparam; + gaim_debug_info("winpidgin", "Got protocol handler request: %s\n", proto_msg ? proto_msg : ""); + gaim_got_protocol_handler_uri(proto_msg); + return TRUE; } return DefWindowProc(hwnd, msg, wparam, lparam); diff -r 66dc2367b137 -r 2f0f563b3579 pidgin/win32/pidgin_dll_rc.rc.in --- a/pidgin/win32/pidgin_dll_rc.rc.in Tue Feb 13 05:00:49 2007 +0000 +++ b/pidgin/win32/pidgin_dll_rc.rc.in Tue Feb 13 07:03:51 2007 +0000 @@ -14,13 +14,13 @@ BEGIN BLOCK "040904B0" BEGIN - VALUE "CompanyName", "The Gaim developer community" - VALUE "FileDescription", "GTK+ Gaim Library" + VALUE "CompanyName", "The Pidgin developer community" + VALUE "FileDescription", "GTK+ Pidgin Library" VALUE "FileVersion", "@GAIM_VERSION@" - VALUE "InternalName", "gtkgaim" - VALUE "LegalCopyright", "Copyright (C) 1998-2006 The Gaim developer community (See the COPYRIGHT file in the source distribution)." - VALUE "OriginalFilename", "gtkgaim.dll" - VALUE "ProductName", "Gaim" + VALUE "InternalName", "libpidgin" + VALUE "LegalCopyright", "Copyright (C) 1998-2007 The Pidgin developer community (See the COPYRIGHT file in the source distribution)." + VALUE "OriginalFilename", "pidgin.dll" + VALUE "ProductName", "Pidgin" VALUE "ProductVersion", "@GAIM_VERSION@" END END diff -r 66dc2367b137 -r 2f0f563b3579 pidgin/win32/pidgin_exe_rc.rc.in --- a/pidgin/win32/pidgin_exe_rc.rc.in Tue Feb 13 05:00:49 2007 +0000 +++ b/pidgin/win32/pidgin_exe_rc.rc.in Tue Feb 13 07:03:51 2007 +0000 @@ -17,13 +17,13 @@ BEGIN BLOCK "040904B0" BEGIN - VALUE "CompanyName", "The Gaim developer community" - VALUE "FileDescription", "Gaim" + VALUE "CompanyName", "The Pidgin developer community" + VALUE "FileDescription", "Pidgin" VALUE "FileVersion", "@GAIM_VERSION@" - VALUE "InternalName", "gaim" - VALUE "LegalCopyright", "Copyright (C) 1998-2006 The Gaim developer community (See the COPYRIGHT file in the source distribution)." + VALUE "InternalName", "pidgin" + VALUE "LegalCopyright", "Copyright (C) 1998-2007 The Pidgin developer community (See the COPYRIGHT file in the source distribution)." VALUE "OriginalFilename", "@ORIGINAL_FILENAME@" - VALUE "ProductName", "Gaim" + VALUE "ProductName", "Pidgin" VALUE "ProductVersion", "@GAIM_VERSION@" END END diff -r 66dc2367b137 -r 2f0f563b3579 pidgin/win32/winpidgin.c --- a/pidgin/win32/winpidgin.c Tue Feb 13 05:00:49 2007 +0000 +++ b/pidgin/win32/winpidgin.c Tue Feb 13 07:03:51 2007 +0000 @@ -433,7 +433,8 @@ putenv(envstr); } -#define WM_FOCUS_REQUEST (WM_APP + 13) +#define PIDGIN_WM_FOCUS_REQUEST (WM_APP + 13) +#define PIDGIN_WM_PROTOCOL_HANDLE (WM_APP + 14) static BOOL winpidgin_set_running() { HANDLE h; @@ -443,7 +444,7 @@ HWND msg_win; if((msg_win = FindWindow(TEXT("WinpidginMsgWinCls"), NULL))) - if(SendMessage(msg_win, WM_FOCUS_REQUEST, (WPARAM) NULL, (LPARAM) NULL)) + if(SendMessage(msg_win, PIDGIN_WM_FOCUS_REQUEST, (WPARAM) NULL, (LPARAM) NULL)) return FALSE; /* If we get here, the focus request wasn't successful */ @@ -458,12 +459,67 @@ return TRUE; } +#define PROTO_HANDLER_SWITCH "--protocolhandler=" -#ifdef __GNUC__ -# ifndef _stdcall -# define _stdcall __attribute__((stdcall)) -# endif -#endif +static void handle_protocol(char *cmd) { + char *remote_msg, *tmp1, *tmp2; + int len; + SIZE_T len_written; + HWND msg_win; + DWORD pid; + HANDLE process; + + /* The start of the message */ + tmp1 = cmd + strlen(PROTO_HANDLER_SWITCH); + + /* The end of the message */ + if ((tmp2 = strchr(tmp1, ' '))) + len = (tmp2 - tmp1); + else + len = strlen(tmp1); + + if (len == 0) { + printf("No protocol message specified.\n"); + return; + } + + if (!(msg_win = FindWindow(TEXT("WinpidginMsgWinCls"), NULL))) { + printf("Unable to find an instance of Pidgin to handle protocol message.\n"); + return; + } + + GetWindowThreadProcessId(msg_win, &pid); + if (!(process = OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_WRITE, FALSE, pid))) { + DWORD dw = GetLastError(); + const char *err_msg = get_win32_error_message(dw); + printf("Unable to open Pidgin process. (%u) %s\n", (UINT) dw, err_msg); + return; + } + + printf("Trying to handle protocol message:\n'%*s'\n", len, tmp1); + + /* MEM_COMMIT initializes the memory to zero, + * so we don't need to worry that our section of tmp1 isn't nul-terminated */ + if ((remote_msg = (char*) VirtualAllocEx(process, NULL, len + 1, MEM_COMMIT, PAGE_READWRITE))) { + if (WriteProcessMemory(process, remote_msg, tmp1, len, &len_written)) { + if (!SendMessage(msg_win, PIDGIN_WM_PROTOCOL_HANDLE, len_written, (LPARAM) remote_msg)) + printf("Unable to send protocol message to Pidgin instance.\n"); + } else { + DWORD dw = GetLastError(); + const char *err_msg = get_win32_error_message(dw); + printf("Unable to write to remote memory. (%u) %s\n", (UINT) dw, err_msg); + } + + VirtualFreeEx(process, remote_msg, 0, MEM_RELEASE); + } else { + DWORD dw = GetLastError(); + const char *err_msg = get_win32_error_message(dw); + printf("Unable to allocate remote memory. (%u) %s\n", (UINT) dw, err_msg); + } + + CloseHandle(process); +} + int _stdcall WinMain (struct HINSTANCE__ *hInstance, struct HINSTANCE__ *hPrevInstance, @@ -471,6 +527,7 @@ char errbuf[512]; char pidgindir[MAX_PATH]; HMODULE hmod; + char *tmp; /* If debug or help or version flag used, create console for output */ if (strstr(lpszCmdLine, "-d") || strstr(lpszCmdLine, "-h") || strstr(lpszCmdLine, "-v")) { @@ -491,10 +548,16 @@ } } + /* If this is a protocol handler invocation, deal with it accordingly */ + if ((tmp = strstr(lpszCmdLine, PROTO_HANDLER_SWITCH)) != NULL) { + handle_protocol(tmp); + return 0; + } + /* Load exception handler if we have it */ if (GetModuleFileName(NULL, pidgindir, MAX_PATH) != 0) { - char *tmp = pidgindir; char *prev = NULL; + tmp = pidgindir; while ((tmp = strchr(tmp, '\\'))) { prev = tmp;