changeset 15613:b0471b2a1de9

Core support for external protocol URIs. The actual handling of the URIs will be in the prpls and other plugins. This commit only includes the win32 method of actually passing in a URI - the dbus implementation still needs to be written.
author Daniel Atallah <daniel.atallah@gmail.com>
date Sun, 11 Feb 2007 00:46:43 +0000
parents 5dd46cb1a80a
children b6f9f5331a82
files ChangeLog.API libpurple/core.c libpurple/util.c libpurple/util.h pidgin/win32/gtkwin32dep.c pidgin/win32/winpidgin.c
diffstat 6 files changed, 155 insertions(+), 10 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog.API	Sun Feb 11 00:30:48 2007 +0000
+++ b/ChangeLog.API	Sun Feb 11 00:46:43 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
--- a/libpurple/core.c	Sun Feb 11 00:30:48 2007 +0000
+++ b/libpurple/core.c	Sun Feb 11 00:46:43 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
--- a/libpurple/util.c	Sun Feb 11 00:30:48 2007 +0000
+++ b/libpurple/util.c	Sun Feb 11 00:46:43 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)
--- a/libpurple/util.h	Sun Feb 11 00:30:48 2007 +0000
+++ b/libpurple/util.h	Sun Feb 11 00:46:43 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.
  *
--- a/pidgin/win32/gtkwin32dep.c	Sun Feb 11 00:30:48 2007 +0000
+++ b/pidgin/win32/gtkwin32dep.c	Sun Feb 11 00:46:43 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);
--- a/pidgin/win32/winpidgin.c	Sun Feb 11 00:30:48 2007 +0000
+++ b/pidgin/win32/winpidgin.c	Sun Feb 11 00:46:43 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;