changeset 3478:3da42b64304e

[gaim-migrate @ 3532] This isn't complete--but it's cool. gaim-remote is now built and installed. Eventually it will do lots of cool stuff--right now it just handles aim:// URI's. Try it out... when connected to OSCAR run: gaim-remote uri "aim://goim?screenname=seanegn&message=Good+job+Sean" Also, I made it so that if you're already running a Gaim session, and start a new one, it won't auto-login and disconnect all your accounts from the first instance. Useful if you accidentally hit the wrong button or something. committer: Tailor Script <tailor@pidgin.im>
author Sean Egan <seanegan@gmail.com>
date Fri, 30 Aug 2002 16:09:22 +0000
parents db5dd21aa345
children 84df15473af6
files ChangeLog src/Makefile.am src/aim.c src/core.c src/core.h src/gaim-socket.h src/gaim.h src/socket.c src/util.c
diffstat 9 files changed, 413 insertions(+), 10 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Fri Aug 30 03:39:00 2002 +0000
+++ b/ChangeLog	Fri Aug 30 16:09:22 2002 +0000
@@ -58,6 +58,9 @@
 	* Nice Oscar changes--mostly internal (Thanks Mark Doliner)
 	* event_del_conversation for plugins (Thanks Bill Tompkins)
 	* Code cleanups and fixes (Thanks Federico Mena Quintero)
+	* aim:// URI's supported with gaim-remote command
+	* Don't auto-login if an existing Gaim session is already
+	  running.
 
 version 0.59 (06/24/2002):
 	* Hungarian translation added (Thanks, Sutto Zoltan)
--- a/src/Makefile.am	Fri Aug 30 03:39:00 2002 +0000
+++ b/src/Makefile.am	Fri Aug 30 16:09:22 2002 +0000
@@ -1,6 +1,6 @@
 SUBDIRS = protocols
 
-bin_PROGRAMS = gaim
+bin_PROGRAMS = gaim gaim-remote
 gaim_SOURCES =	about.c \
 		aim.c \
 		away.c \
@@ -25,12 +25,15 @@
 		proxy.c \
 		prpl.c \
 		server.c \
+		socket.c \
 		sound.c \
 		util.c
 gaim_DEPENDENCIES = @LIBOBJS@ $(STATIC_LINK_LIBS)
 gaim_LDFLAGS = -export-dynamic
 gaim_LDADD = @LDADD@ @LIBOBJS@
 
+gaim_remote_SOURCES = gaim-remote.c socket.c
+
 CFLAGS += $(PERL_CFLAGS)
 CFLAGS += -DLOCALEDIR=\"$(datadir)/locale\" -DLIBDIR=\"$(libdir)/gaim/\" $(DEBUG_CFLAGS) -DDATADIR=\"$(datadir)\"
 
--- a/src/aim.c	Fri Aug 30 03:39:00 2002 +0000
+++ b/src/aim.c	Fri Aug 30 16:09:22 2002 +0000
@@ -504,7 +504,7 @@
 		debug_printf("Failed to load icon from %s/pixmaps/gaim.png\n",DATADIR);
 	}
 
-	g_snprintf(name, sizeof(name), "%s/gaim_%s.%d", g_get_tmp_dir(), g_get_user_name(), getpid());
+	g_snprintf(name, sizeof(name), "%s/gaim_%s.%d", g_get_tmp_dir(), g_get_user_name(), gaim_session);
 
 	UI_fd = open_socket(name);
 	if (UI_fd < 0)
@@ -770,7 +770,7 @@
 		}
 	}
 
-	if (!opt_acct && !opt_nologin)
+	if (!opt_acct && !opt_nologin && gaim_session == 0)
 		auto_login();
 
 	if (opt_acct) {
--- a/src/core.c	Fri Aug 30 03:39:00 2002 +0000
+++ b/src/core.c	Fri Aug 30 16:09:22 2002 +0000
@@ -38,8 +38,10 @@
 #include <string.h>
 
 #include "gaim.h"
+#include "gaim-socket.h"
 
 static gint UI_fd = -1;
+int gaim_session = 0;
 GSList *uis = NULL;
 
 static guchar *UI_build(guint32 *len, guchar type, guchar subtype, va_list args)
@@ -132,6 +134,7 @@
 
 static void meta_handler(struct UI *ui, guchar subtype, guchar *data)
 {
+	struct gaim_cui_packet *p;
 	switch (subtype) {
 	case CUI_META_LIST:
 		break;
@@ -151,6 +154,11 @@
 		g_source_remove(ui->inpa);
 		g_free(ui);
 		break;
+	case CUI_META_PING:
+		p = cui_packet_new(CUI_TYPE_META, CUI_META_ACK);
+		cui_send_packet(g_io_channel_unix_get_fd(ui->channel), p);
+		cui_packet_free(p);
+		break;
 	default:
 		debug_printf("unhandled meta subtype %d\n", subtype);
 		break;
@@ -289,6 +297,27 @@
 	return total;
 }
 
+static void remote_handler(struct UI *ui, guchar subtype, guchar *data, int len)
+{
+	const char *resp;
+	char *send;
+	switch (subtype) {
+	case CUI_REMOTE_CONNECTIONS:
+		break;
+	case CUI_REMOTE_URI:
+		send = g_malloc(len + 1);
+		memcpy(send, data, len);
+		send[len] = 0;
+		resp = handle_uri(send);
+		g_free(send);
+		/* report error */
+		break;
+	default:
+		debug_printf("Unhandled remote subtype %d\n", subtype);
+		break;
+	}
+}
+
 static gboolean UI_readable(GIOChannel *source, GIOCondition cond, gpointer data)
 {
 	struct UI *ui = data;
@@ -365,8 +394,11 @@
 		case CUI_TYPE_CHAT:
 			chat_handler(ui, subtype, in);
 			break;
-			*/
-		default:
+			*/   
+	        case CUI_TYPE_REMOTE:
+			remote_handler(ui, subtype, in, len);
+			break; 
+	        default:
 			debug_printf("unhandled type %d\n", type);
 			break;
 	}
@@ -402,18 +434,23 @@
 {
 	struct sockaddr_un saddr;
 	gint fd;
-
+	
+	while (gaim_session_exists(gaim_session))
+		gaim_session++;
+	
+	debug_printf("session: %d\n", gaim_session);
+	
 	if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) != -1) {
 		mode_t m = umask(0177);
 		saddr.sun_family = AF_UNIX;
 		g_snprintf(saddr.sun_path, 108, "%s/gaim_%s.%d",
-				g_get_tmp_dir(), g_get_user_name(), getpid());
+			   g_get_tmp_dir(), g_get_user_name(), gaim_session);
 		if (bind(fd, (struct sockaddr *)&saddr, sizeof(saddr)) != -1)
 			listen(fd, 100);
 		else {
 			g_log(NULL, G_LOG_LEVEL_CRITICAL,
-				"Failed to assign %s to a socket (Error: %s)",	
-				saddr.sun_path, strerror(errno));
+			      "Failed to assign %s to a socket (Error: %s)",	
+			      saddr.sun_path, strerror(errno));
 			return -1;
 		}
 		umask(m);
@@ -450,7 +487,7 @@
 {
 	char buf[1024];
 	close(UI_fd);
-	sprintf(buf, "%s/gaim_%s.%d", g_get_tmp_dir(), g_get_user_name(), getpid());
+	sprintf(buf, "%s/gaim_%s.%d", g_get_tmp_dir(), g_get_user_name(), gaim_session);
 	unlink(buf);
 	debug_printf("Removed core\n");
 }
--- a/src/core.h	Fri Aug 30 03:39:00 2002 +0000
+++ b/src/core.h	Fri Aug 30 16:09:22 2002 +0000
@@ -148,6 +148,7 @@
 
 /* Globals in core.c */
 extern GSList *uis;
+extern gaim_session;
 
 /* Globals in plugins.c */
 extern GList *plugins;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/gaim-socket.h	Fri Aug 30 16:09:22 2002 +0000
@@ -0,0 +1,44 @@
+/*
+ * gaim-remote
+ *
+ * Copyright (C) 2002, Sean Egan <bj91704@binghamton.edu>
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef _GAIM_SOCKET_H_
+#define _GAIM_SOCKET_H_
+
+struct gaim_cui_packet {
+	guchar type;
+	guchar subtype;
+	guint32 length;
+	char *data;
+};
+
+
+void cui_send_packet (int fd, struct gaim_cui_packet *p);
+gint gaim_connect_to_session(gint session);
+gboolean gaim_session_exists(int sess);
+
+struct gaim_cui_packet *cui_packet_new(guchar type, guchar subtype);
+void cui_packet_free(struct gaim_cui_packet *p);
+void cui_packet_append_string(struct gaim_cui_packet *p, char *str);
+void cui_packet_append_char(struct gaim_cui_packet *p, char c);
+void cui_packet_append_raw(struct gaim_cui_packet *p, char *str, int len);
+
+
+#endif /* _GAIM_SOCKET_H_ */
--- a/src/gaim.h	Fri Aug 30 03:39:00 2002 +0000
+++ b/src/gaim.h	Fri Aug 30 16:09:22 2002 +0000
@@ -43,12 +43,19 @@
 #define CUI_TYPE_BUDDY		5	/* BUDDY_LIST, i.e., both groups and buddies */
 #define CUI_TYPE_MESSAGE	6
 #define CUI_TYPE_CHAT		7
+#define CUI_TYPE_REMOTE         8       /* This is used to send commands to other UI's, 
+					 * like "Open new conversation" or "send IM".
+					 * Even though there's much redundancy with the
+					 * other CUI_TYPES, we're better keeping this stuff
+					 * seperate because it's intended use is so different */
 
 #define CUI_META_LIST		1	/* 1 is always list; this is ignored by the core.
 					   If we move to TCP this can be a keepalive */
 #define CUI_META_QUIT		2
 #define CUI_META_DETACH		3	/* you don't need to send this, you can just close
 					   the socket. the core will understand. */
+#define CUI_META_PING           4
+#define CUI_META_ACK            5
 
 #define CUI_PLUGIN_LIST		1
 #define CUI_PLUGIN_LOAD		2
@@ -84,6 +91,19 @@
 #define CUI_CHAT_SEND		5
 #define CUI_CHAT_RECV		6
 
+#define CUI_REMOTE_CONNECTIONS  2       /* Get a list of gaim_connections */
+#define CUI_REMOTE_URI          3       /* Have the core handle aim:// URI's */
+#define CUI_REMOTE_BLIST        4       /* Return a copy of the buddy list */
+#define CUI_REMOTE_STATE        5       /* Given a buddy, return his presence. */
+#define CUI_REMOTE_NEW_CONVO    6       /* Must give a user, can give an optional message */
+#define CUI_REMOTE_SEND         7       /* Sends a message, a 'quiet' flag determines whether
+					 * a convo window is displayed or not. */
+#define CUI_REMOTE_ADD_BUDDY    8       /* Adds buddy to list */
+#define CUI_REMOTE_REMOVE_BUDDY 9       /* Removes buddy from list */
+#define CUI_REMOTE_JOIN_CHAT    10       /* Joins a chat. */
+                              /* What else?? */
+
+
 #define IM_FLAG_AWAY     0x01
 #define IM_FLAG_CHECKBOX 0x02
 #define IM_FLAG_GAIMUSER 0x04
@@ -432,6 +452,7 @@
 extern time_t get_time(int, int, int, int, int, int);
 extern FILE *gaim_mkstemp(gchar **);
 extern char *convert_string(char *, const char *, const char *);
+extern const char *handle_uri(char *);
 
 #ifdef HAVE_LANGINFO_CODESET
 #define utf8_to_str(in) convert_string(in, nl_langinfo(CODESET), "UTF-8");
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/socket.c	Fri Aug 30 16:09:22 2002 +0000
@@ -0,0 +1,168 @@
+/*
+ * gaim-remote
+ *
+ * Copyright (C) 2002, Sean Egan <bj91704@binghamton.edu>
+ * 
+ * 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
+ *
+ */
+
+/* Somewhat inspired by XMMS:
+ *  Copyright (C) 1998-2002  Peter Alm, Mikael Alm, Olle Hallnas,
+ *                           Thomas Nilsson and 4Front Technologies
+ *  Copyright (C) 1999-2002  Haavard Kvaalen 
+ */
+ 
+/* This provides code for connecting to a Gaim socket and communicating with
+ * it.  It will eventually be made a library once the core and ui are split. */
+
+#include <sys/socket.h>
+#include <sys/un.h>
+#include "gaim.h"
+#include "gaim-socket.h"
+
+void cui_send_packet (int fd, struct gaim_cui_packet *p) {
+	int len = sizeof(p->type) + sizeof(p->subtype) + sizeof(p->length) + p->length;
+	char *pack = g_malloc(len);
+	char *a = pack;
+	memcpy (a, &(p->type), sizeof(p->type));
+	a = a + sizeof(p->type);
+	memcpy (a, &(p->subtype), sizeof(p->subtype));
+	a = a + sizeof(p->subtype);
+	memcpy (a, &(p->length), sizeof(p->length));
+	a = a + sizeof(p->length);
+	memcpy (a, p->data, p->length);
+	write(fd, pack, len);
+	g_free(pack);
+}
+
+
+void cui_packet_append_string(struct gaim_cui_packet *p, char *str) {
+	int len = p->length + strlen(str);
+	char *k = g_malloc(len);
+	memcpy(k, p->data, p->length);
+	memcpy(k + p->length, str, strlen(str));
+	if (p->data)
+		g_free(p->data);
+	p->data = k;
+	p->length = len;
+}
+
+void cui_packet_append_char(struct gaim_cui_packet *p, char c) {
+	int len = p->length + sizeof(char);
+	char *k = g_malloc(len);
+	memcpy(k, p->data, p->length);
+	k[p->length] = c;
+	if (p->data)
+		g_free(p->data);
+	p->data = k;
+	p->length = len;
+}
+
+void cui_packet_append_raw(struct gaim_cui_packet *p, char *str, int len) {
+	int lent = p->length + len;
+	char *k = g_malloc(lent);
+	memcpy(k, p->data, p->length);
+	memcpy(k + p->length, str, len);
+	if (p->data)
+		g_free(p->data);
+	p->data = k;
+	p->length = lent;
+}
+
+struct gaim_cui_packet *cui_packet_new(guchar type, guchar subtype) {
+	struct gaim_cui_packet *p = g_new0(struct gaim_cui_packet, 1);
+	p->type = type;
+	p->subtype = subtype;
+	p->length = 0;
+	p->data = NULL;
+	return p;
+}
+
+void cui_packet_free(struct gaim_cui_packet *p) {
+	if (p->data)
+		g_free(p->data);
+	g_free(p);
+}
+
+struct gaim_cui_packet *cui_read_packet(int fd) {
+	struct gaim_cui_packet *p = g_new0(struct gaim_cui_packet, 1);
+	char *data = NULL;
+
+	if (!(read(fd, p->type, sizeof(p->type)))) {
+		g_free(p);
+		return NULL;
+	}
+	
+	
+	if (!(read(fd, p->subtype, sizeof(p->subtype)))) {
+		g_free(p);
+		return NULL;
+	}
+	
+	
+	if (!(read(fd, p->length, sizeof(p->length)))) {
+		g_free(p);
+		return NULL;
+	}
+	
+	if (p->length) {
+		data = g_malloc(p->length);
+		if (!(read(fd, data, p->length))) {
+			g_free(p);
+			return NULL;
+		}
+	}
+	p->data = data;
+}
+
+/* copied directly from xmms_connect_to_session */
+gint gaim_connect_to_session(gint session)
+{
+	gint fd;
+	uid_t stored_uid, euid;
+	struct sockaddr_un saddr;
+
+	if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) != -1)
+	{
+		saddr.sun_family = AF_UNIX;
+		stored_uid = getuid();
+		euid = geteuid();
+		setuid(euid);
+		sprintf(saddr.sun_path, "%s/gaim_%s.%d", g_get_tmp_dir(), g_get_user_name(), session);
+		setreuid(stored_uid, euid);
+		if (connect(fd, (struct sockaddr *) &saddr, sizeof (saddr)) != -1)
+			return fd;
+	}
+	close(fd);
+	return -1;
+}
+
+gboolean gaim_session_exists(int sess)
+{
+	struct gaim_cui_packet *pack = NULL;
+
+	int fd = gaim_connect_to_session(sess);
+	if (fd > 0) {
+		pack = cui_packet_new(CUI_TYPE_META, CUI_META_PING);
+		cui_send_packet(fd, pack);
+		cui_packet_free(pack);
+		close(fd);
+	} else {
+		return FALSE;
+	}
+	return TRUE;
+}
+
--- a/src/util.c	Fri Aug 30 03:39:00 2002 +0000
+++ b/src/util.c	Fri Aug 30 16:09:22 2002 +0000
@@ -32,7 +32,9 @@
 #include <string.h>
 #include <sys/wait.h>
 #include <ctype.h>
+#ifdef HAVE_ICONV
 #include <iconv.h>
+#endif
 #include <math.h>
 #include "gaim.h"
 #include "prpl.h"
@@ -1264,3 +1266,127 @@
 
 	return fp;
 }
+
+/* AIM URI's ARE FUN :-D */
+const char *handle_uri(char *uri) {
+	GString *str;
+	GSList *conn = connections;
+	struct gaim_connection *gc;
+
+	debug_printf("Handling URI: %s\n", uri);
+	
+	/* Well, we'd better check to make sure we have at least one
+	   AIM account connected. */
+	while (gc = conn->data) {
+		if (gc->protocol == PROTO_TOC) {
+			break;
+		}
+		conn = conn->next;
+	}
+
+	if (gc == NULL)
+		return "Not connected to AIM";
+
+ 	/* aim://goim?screenname=screenname&message=message */
+	if (!g_strncasecmp(uri, "aim://goim?", strlen("aim://goim?"))) {
+		char *who, *what;
+		struct conversation *c;
+		uri = uri + strlen("aim://goim?");
+		
+		if (!(who = strstr(uri, "screenname="))) {
+			return "No screenname given.";
+		}
+		/* spaces are encoded as +'s */
+		who = who + strlen("screenname=");
+		str = g_string_new(NULL);
+		while (*who && (*who != '&')) {
+			g_string_append_c(str, *who == '+' ? ' ' : *who);
+			who++;
+		}
+		who = g_strdup(str->str);
+		g_string_free(str, TRUE);
+		
+		what = strstr(uri, "message=");
+		if (what) {
+			what = what + strlen("message=");
+			str = g_string_new(NULL);
+			while (*what && (*what != '&' || !g_strncasecmp(what, "&amp;", 5))) {
+				g_string_append_c(str, *what == '+' ? ' ' : *what);
+				what++;
+			}
+			what = g_strdup(str->str);
+			g_string_free(str, TRUE);
+		}
+		
+		c = new_conversation(who);
+		g_free(who);
+		if (what) {
+			int finish;
+			gtk_editable_insert_text(GTK_EDITABLE(c->entry),
+					 what, strlen(what), &finish);
+			g_free(what);
+		}
+	} else if (!g_strncasecmp(uri, "aim://addbuddy?", strlen("aim://addbuddy?"))) {
+		char *who, *group;
+		uri = uri + strlen("aim://addbuddy?");
+		/* spaces are encoded as +'s */
+		
+		if (!(who = strstr(uri, "screenname="))) {
+			return "No screenname given.";
+		}
+		who = who + strlen("screenname=");
+		str = g_string_new(NULL);
+		while (*who && (*who != '&')) {
+			g_string_append_c(str, *who == '+' ? ' ' : *who);
+			who++;
+		}
+		who = g_strdup(str->str);
+		g_string_free(str, TRUE);
+
+		group = strstr(uri, "group=");
+		if (group) {
+			group = group + strlen("group=");
+			str = g_string_new(NULL);
+			while (*group && (*group != '&' || !g_strncasecmp(group, "&amp;", 5))) {
+				g_string_append_c(str, *group == '+' ? ' ' : *group);
+				group++;
+			}
+			group = g_strdup(str->str);
+			g_string_free(str, TRUE);
+		}
+		debug_printf("who: %s\n", who);
+		show_add_buddy(gc, who, group, NULL);
+		g_free(who);
+		if (group)
+			g_free(group);
+	} else if (!g_strncasecmp(uri, "aim://gochat?", strlen("aim://gochat?"))) {
+		char *room;
+		GList *chat=NULL;
+		int exch = 5;
+		
+		uri = uri + strlen("aim://gochat?");
+		/* spaces are encoded as +'s */
+		
+		if (!(room = strstr(uri, "roomname="))) {
+			return "No roomname given.";
+		}
+		room = room + strlen("roomname=");
+		str = g_string_new(NULL);
+		while (*room && (*room != '&')) {
+			g_string_append_c(str, *room == '+' ? ' ' : *room);
+			room++;
+		}
+		room = g_strdup(str->str);
+		g_string_free(str, TRUE);
+		chat = g_list_append(NULL, room);
+		chat = g_list_append(chat, &exch);
+		serv_join_chat(gc, chat);
+		g_free(room);
+		g_list_free(chat);
+	} else {
+		return "Invalid AIM URI";
+	}
+	
+	
+	return NULL;
+}