gaim/gaim

umask fix for v0_73-branch
v0_74-branch
2003-11-25, Ethan Blanton
58472b7b6607
Parents cc88ae001c4d
Children 31d32fd067cd
umask fix for v0_73-branch
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/gaim-remote/remote.c Tue Nov 25 20:26:31 2003 -0500
@@ -0,0 +1,739 @@
+/**
+ * Remote control plugin for Gaim
+ *
+ * Copyright (C) 2003 Christian Hammond.
+ * 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., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ * @todo Make this a core plugin!
+ */
+#include "internal.h"
+#include "gtkinternal.h"
+
+#ifndef _WIN32
+# include <sys/un.h>
+#endif
+
+#include <signal.h>
+#include <getopt.h>
+
+#include "conversation.h"
+#include "core.h"
+#include "debug.h"
+#include "prpl.h"
+
+/* XXX */
+#include "gtkconv.h"
+#include "gtkplugin.h"
+#include "gaim.h"
+#include "ui.h"
+
+#include <gaim-remote/remote.h>
+
+#define REMOTE_PLUGIN_ID "gtk-remote"
+
+struct UI {
+ GIOChannel *channel;
+ guint inpa;
+};
+
+#ifndef _WIN32
+static gint UI_fd = -1;
+static guint watcher = 0;
+#endif
+static int gaim_session = 0;
+static GSList *uis = NULL;
+
+/* AIM URI's ARE FUN :-D */
+static const char *
+gaim_remote_handle_uri(const char *uri)
+{
+ const char *username;
+ GString *str;
+ GList *conn;
+ GaimConnection *gc = NULL;
+ GaimAccount *account;
+
+ gaim_debug(GAIM_DEBUG_INFO, "gaim_remote_handle_uri", "Handling URI: %s\n", uri);
+
+ /* Well, we'd better check to make sure we have at least one
+ AIM account connected. */
+ for (conn = gaim_connections_get_all(); conn != NULL; conn = conn->next) {
+ gc = conn->data;
+ account = gaim_connection_get_account(gc);
+ username = gaim_account_get_username(account);
+
+ if (gaim_account_get_protocol(account) == GAIM_PROTO_OSCAR &&
+ username != NULL && isalpha(*username)) {
+
+ break;
+ }
+ }
+
+ if (gc == NULL)
+ return _("Not connected to AIM");
+
+ /* aim:goim?screenname=screenname&message=message */
+ if (!g_ascii_strncasecmp(uri, "aim:goim?", strlen("aim:goim?"))) {
+ char *who, *what;
+ GaimConversation *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_ascii_strncasecmp(what, "&amp;", 5))) {
+ g_string_append_c(str, *what == '+' ? ' ' : *what);
+ what++;
+ }
+ what = g_strdup(str->str);
+ g_string_free(str, TRUE);
+ }
+
+ c = gaim_conversation_new(GAIM_CONV_IM, gc->account, who);
+ g_free(who);
+
+ if (what) {
+ GaimGtkConversation *gtkconv = GAIM_GTK_CONVERSATION(c);
+
+ gtk_text_buffer_insert_at_cursor(gtkconv->entry_buffer, what, -1);
+ g_free(what);
+ }
+ } else if (!g_ascii_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_ascii_strncasecmp(group, "&amp;", 5))) {
+ g_string_append_c(str, *group == '+' ? ' ' : *group);
+ group++;
+ }
+ group = g_strdup(str->str);
+ g_string_free(str, TRUE);
+ }
+
+ gaim_debug(GAIM_DEBUG_MISC, "gaim_remote_handle_uri", "who: %s\n", who);
+ gaim_blist_request_add_buddy(gc->account, who, group, NULL);
+ g_free(who);
+ if (group)
+ g_free(group);
+ } else if (!g_ascii_strncasecmp(uri, "aim:gochat?", strlen("aim:gochat?"))) {
+ char *room;
+ GHashTable *components;
+ 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);
+ components = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
+ g_free);
+ g_hash_table_replace(components, g_strdup("room"), room);
+ g_hash_table_replace(components, g_strdup("exchange"),
+ g_strdup_printf("%d", exch));
+
+ serv_join_chat(gc, components);
+ g_hash_table_destroy(components);
+ } else {
+ return _("Invalid AIM URI");
+ }
+
+
+ return NULL;
+}
+
+
+
+#if 0
+static guchar *
+UI_build(guint32 *len, guchar type, guchar subtype, va_list args)
+{
+ guchar *buffer;
+ guint32 pos;
+ int size;
+ void *data;
+
+ *len = sizeof(guchar) * 2 + 4;
+ buffer = g_malloc(*len);
+ pos = 0;
+
+ memcpy(buffer + pos, &type, sizeof(type)); pos += sizeof(type);
+ memcpy(buffer + pos, &subtype, sizeof(subtype)); pos += sizeof(subtype);
+
+ /* we come back and do size last */
+ pos += 4;
+
+ size = va_arg(args, int);
+ while (size != -1) {
+ *len += size;
+ buffer = g_realloc(buffer, *len);
+
+ data = va_arg(args, void *);
+ memcpy(buffer + pos, data, size);
+ pos += size;
+
+ size = va_arg(args, int);
+ }
+
+ pos -= sizeof(guchar) * 2 + 4;
+
+ /* now we do size */
+ memcpy(buffer + sizeof(guchar) * 2, &pos, 4);
+
+ return buffer;
+}
+
+static gint
+UI_write(struct UI *ui, guchar *data, gint len)
+{
+ GError *error = NULL;
+ gint sent;
+ /* we'll let the write silently fail because the read will pick it up as dead */
+ g_io_channel_write_chars(ui->channel, data, len, &sent, &error);
+ if (error)
+ g_error_free(error);
+ return sent;
+}
+
+static void
+UI_build_write(struct UI *ui, guchar type, guchar subtype, ...)
+{
+ va_list ap;
+ gchar *data;
+ guint32 len;
+
+ va_start(ap, subtype);
+ data = UI_build(&len, type, subtype, ap);
+ va_end(ap);
+
+ UI_write(ui, data, len);
+
+ g_free(data);
+}
+
+static void
+UI_broadcast(guchar *data, gint len)
+{
+ GSList *u = uis;
+ while (u) {
+ struct UI *ui = u->data;
+ UI_write(ui, data, len);
+ u = u->next;
+ }
+}
+
+static void
+UI_build_broadcast(guchar type, guchar subtype, ...)
+{
+ va_list ap;
+ gchar *data;
+ guint32 len;
+
+ if (!uis)
+ return;
+
+ va_start(ap, subtype);
+ data = UI_build(&len, type, subtype, ap);
+ va_end(ap);
+
+ UI_broadcast(data, len);
+
+ g_free(data);
+}
+#endif
+
+#ifndef _WIN32
+static void
+meta_handler(struct UI *ui, guchar subtype, gchar *data)
+{
+ GaimRemotePacket *p;
+ GError *error = NULL;
+ switch (subtype) {
+ case CUI_META_LIST:
+ break;
+ case CUI_META_QUIT:
+ while (uis) {
+ ui = uis->data;
+ uis = g_slist_remove(uis, ui);
+ g_io_channel_shutdown(ui->channel, TRUE, &error);
+ g_source_remove(ui->inpa);
+ g_free(ui);
+ }
+ gaim_core_quit();
+ break;
+ case CUI_META_DETACH:
+ uis = g_slist_remove(uis, ui);
+ g_io_channel_shutdown(ui->channel, TRUE, &error);
+ g_source_remove(ui->inpa);
+ g_free(ui);
+ break;
+ case CUI_META_PING:
+ p = gaim_remote_packet_new(CUI_TYPE_META, CUI_META_ACK);
+ gaim_remote_session_send_packet(g_io_channel_unix_get_fd(ui->channel),
+ p);
+ gaim_remote_packet_free(p);
+ break;
+ default:
+ gaim_debug(GAIM_DEBUG_WARNING, "cui",
+ "Unhandled meta subtype %d\n", subtype);
+ break;
+ }
+
+ if(error)
+ g_error_free(error);
+}
+
+static void
+plugin_handler(struct UI *ui, guchar subtype, gpointer data)
+{
+#ifdef GAIM_PLUGINS
+ guint id;
+ GaimPlugin *p;
+
+ switch (subtype) {
+ /*
+ case CUI_PLUGIN_LIST:
+ break;
+ */
+ case CUI_PLUGIN_LOAD:
+ gaim_plugin_load(gaim_plugin_probe(data));
+ break;
+ case CUI_PLUGIN_UNLOAD:
+ memcpy(&id, data, sizeof(id));
+ p = g_list_nth_data(gaim_plugins_get_loaded(), id);
+ if (p) {
+ gaim_plugin_unload(p);
+ }
+ break;
+ default:
+ gaim_debug(GAIM_DEBUG_WARNING, "cui",
+ "Unhandled plugin subtype %d\n", subtype);
+ break;
+ }
+#endif
+}
+
+static void
+user_handler(struct UI *ui, guchar subtype, gchar *data)
+{
+ guint id;
+ GaimAccount *account;
+
+ switch (subtype) {
+ /*
+ case CUI_USER_LIST:
+ break;
+ case CUI_USER_ADD:
+ break;
+ case CUI_USER_REMOVE:
+ break;
+ case CUI_USER_MODIFY:
+ break;
+ */
+ case CUI_USER_SIGNON:
+ if (!data)
+ return;
+ memcpy(&id, data, sizeof(id));
+ account = g_list_nth_data(gaim_accounts_get_all(), id);
+ if (account)
+ gaim_account_connect(account);
+ /* don't need to do anything here because the UI will get updates from other handlers */
+ break;
+ default:
+ gaim_debug(GAIM_DEBUG_WARNING, "cui",
+ "Unhandled user subtype %d\n", subtype);
+ break;
+ }
+}
+
+static void
+message_handler(struct UI *ui, guchar subtype, gchar *data)
+{
+ switch (subtype) {
+ case CUI_MESSAGE_LIST:
+ break;
+ case CUI_MESSAGE_SEND:
+ if (!data)
+ return;
+ {
+ guint id;
+ GaimConnection *gc;
+ guint len;
+ char *who, *msg;
+ gint flags;
+ int pos = 0;
+
+ memcpy(&id, data + pos, sizeof(id));
+ pos += sizeof(id);
+ gc = g_list_nth_data(gaim_connections_get_all(), id);
+ if (!gc)
+ return;
+
+ memcpy(&len, data + pos, sizeof(len));
+ pos += sizeof(len);
+ who = g_strndup(data + pos, len + 1);
+ pos += len;
+
+ memcpy(&len, data + pos, sizeof(len));
+ pos += sizeof(len);
+ msg = g_strndup(data + pos, len + 1);
+ pos += len;
+
+ memcpy(&flags, data + pos, sizeof(flags));
+ serv_send_im(gc, who, msg, flags);
+
+ g_free(who);
+ g_free(msg);
+ }
+ break;
+ case CUI_MESSAGE_RECV:
+ break;
+ default:
+ gaim_debug(GAIM_DEBUG_WARNING, "cui",
+ "Unhandled message subtype %d\n", subtype);
+ break;
+ }
+}
+
+static gint
+gaim_recv(GIOChannel *source, gchar *buf, gint len)
+{
+ gint total = 0;
+ guint cur;
+
+ GError *error = NULL;
+
+ while (total < len) {
+ if (g_io_channel_read_chars(source, buf + total, len - total, (gsize *) &cur, &error) != G_IO_STATUS_NORMAL) {
+ if (error)
+ g_error_free(error);
+ return -1;
+ }
+ if (cur == 0)
+ return total;
+ total += cur;
+ }
+
+ return total;
+}
+
+static void
+remote_handler(struct UI *ui, guchar subtype, gchar *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 = gaim_remote_handle_uri(send);
+ g_free(send);
+ /* report error */
+ break;
+ default:
+ gaim_debug(GAIM_DEBUG_WARNING, "cui",
+ "Unhandled remote subtype %d\n", subtype);
+ break;
+ }
+}
+
+static gboolean
+UI_readable(GIOChannel *source, GIOCondition cond, gpointer data)
+{
+ struct UI *ui = data;
+
+ gchar type;
+ gchar subtype;
+ guint32 len;
+
+ GError *error = NULL;
+
+ gchar *in;
+
+ /* no byte order worries! this'll change if we go to TCP */
+ if (gaim_recv(source, &type, sizeof(type)) != sizeof(type)) {
+ gaim_debug(GAIM_DEBUG_ERROR, "cui", "UI has abandoned us!\n");
+ uis = g_slist_remove(uis, ui);
+ g_io_channel_shutdown(ui->channel, TRUE, &error);
+ if(error) {
+ g_error_free(error);
+ error = NULL;
+ }
+ g_source_remove(ui->inpa);
+ g_free(ui);
+ return FALSE;
+ }
+
+ if (gaim_recv(source, &subtype, sizeof(subtype)) != sizeof(subtype)) {
+ gaim_debug(GAIM_DEBUG_ERROR, "cui", "UI has abandoned us!\n");
+ uis = g_slist_remove(uis, ui);
+ g_io_channel_shutdown(ui->channel, TRUE, &error);
+ if(error) {
+ g_error_free(error);
+ error = NULL;
+ }
+ g_source_remove(ui->inpa);
+ g_free(ui);
+ return FALSE;
+ }
+
+ if (gaim_recv(source, (gchar *)&len, sizeof(len)) != sizeof(len)) {
+ gaim_debug(GAIM_DEBUG_ERROR, "cui", "UI has abandoned us!\n");
+ uis = g_slist_remove(uis, ui);
+ g_io_channel_shutdown(ui->channel, TRUE, &error);
+ if(error) {
+ g_error_free(error);
+ error = NULL;
+ }
+ g_source_remove(ui->inpa);
+ g_free(ui);
+ return FALSE;
+ }
+
+ if (len) {
+ in = g_new0(gchar, len);
+ if (gaim_recv(source, in, len) != len) {
+ gaim_debug(GAIM_DEBUG_ERROR, "cui", "UI has abandoned us!\n");
+ uis = g_slist_remove(uis, ui);
+ g_io_channel_shutdown(ui->channel, TRUE, &error);
+ if(error) {
+ g_error_free(error);
+ error = NULL;
+ }
+ g_source_remove(ui->inpa);
+ g_free(ui);
+ return FALSE;
+ }
+ } else
+ in = NULL;
+
+ switch (type) {
+ case CUI_TYPE_META:
+ meta_handler(ui, subtype, in);
+ break;
+ case CUI_TYPE_PLUGIN:
+ plugin_handler(ui, subtype, in);
+ break;
+ case CUI_TYPE_USER:
+ user_handler(ui, subtype, in);
+ break;
+ /*
+ case CUI_TYPE_CONN:
+ conn_handler(ui, subtype, in);
+ break;
+ case CUI_TYPE_BUDDY:
+ buddy_handler(ui, subtype, in);
+ break;
+ */
+ case CUI_TYPE_MESSAGE:
+ message_handler(ui, subtype, in);
+ break;
+ /*
+ case CUI_TYPE_CHAT:
+ chat_handler(ui, subtype, in);
+ break;
+ */
+ case CUI_TYPE_REMOTE:
+ remote_handler(ui, subtype, in, len);
+ break;
+ default:
+ gaim_debug(GAIM_DEBUG_WARNING, "cui",
+ "Unhandled type %d\n", type);
+ break;
+ }
+
+ if (in)
+ g_free(in);
+ return TRUE;
+}
+
+static gboolean
+socket_readable(GIOChannel *source, GIOCondition cond, gpointer data)
+{
+ struct sockaddr_un saddr;
+ guint len = sizeof(saddr);
+ gint fd;
+
+ struct UI *ui;
+
+ if ((fd = accept(UI_fd, (struct sockaddr *)&saddr, &len)) == -1)
+ return FALSE;
+
+ ui = g_new0(struct UI, 1);
+ uis = g_slist_append(uis, ui);
+
+ ui->channel = g_io_channel_unix_new(fd);
+ ui->inpa = g_io_add_watch(ui->channel, G_IO_IN | G_IO_HUP | G_IO_ERR, UI_readable, ui);
+ g_io_channel_unref(ui->channel);
+
+ gaim_debug(GAIM_DEBUG_MISC, "cui", "Got one\n");
+ return TRUE;
+}
+
+static gint
+open_socket()
+{
+ struct sockaddr_un saddr;
+ gint fd;
+
+ while (gaim_remote_session_exists(gaim_session))
+ gaim_session++;
+
+ gaim_debug(GAIM_DEBUG_MISC, "cui", "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, sizeof(saddr.sun_path), "%s" G_DIR_SEPARATOR_S "gaim_%s.%d",
+ 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));
+ umask(m);
+ return -1;
+ }
+ umask(m);
+ } else
+ g_log(NULL, G_LOG_LEVEL_CRITICAL, "Unable to open socket: %s", strerror(errno));
+ return fd;
+}
+#endif /*! _WIN32*/
+
+static gboolean
+plugin_load(GaimPlugin *plugin)
+{
+#ifndef _WIN32
+ GIOChannel *channel;
+
+ if ((UI_fd = open_socket()) < 0)
+ return FALSE;
+
+ channel = g_io_channel_unix_new(UI_fd);
+ watcher = g_io_add_watch(channel, G_IO_IN, socket_readable, NULL);
+ g_io_channel_unref(channel);
+
+ return TRUE;
+#else
+ return FALSE;
+#endif
+}
+
+static gboolean
+plugin_unload(GaimPlugin *plugin)
+{
+ /* don't save prefs after plugins are gone... */
+#ifndef _WIN32
+ char buf[1024];
+
+ g_source_remove(watcher);
+ close(UI_fd);
+
+ g_snprintf(buf, sizeof(buf), "%s" G_DIR_SEPARATOR_S "gaim_%s.%d",
+ g_get_tmp_dir(), g_get_user_name(), gaim_session);
+
+ unlink(buf);
+
+ gaim_debug(GAIM_DEBUG_MISC, "core", "Removed core\n");
+
+ return TRUE;
+#else
+ return FALSE;
+#endif
+}
+
+static GaimPluginInfo info =
+{
+ 2, /**< api_version */
+ GAIM_PLUGIN_STANDARD, /**< type */
+ GAIM_GTK_PLUGIN_TYPE, /**< ui_requirement */
+ 0, /**< flags */
+ NULL, /**< dependencies */
+ GAIM_PRIORITY_DEFAULT, /**< priority */
+
+ REMOTE_PLUGIN_ID, /**< id */
+ N_("Remote Control"), /**< name */
+ VERSION, /**< version */
+ /** summary */
+ N_("Provides remote control for gaim applications."),
+ /** description */
+ N_("Gives Gaim the ability to be remote-controlled through third-party "
+ "applications or through the gaim-remote tool."),
+ "Christian Hammond <chipx86@gnupdate.org>", /**< author */
+ GAIM_WEBSITE, /**< homepage */
+
+ plugin_load, /**< load */
+ plugin_unload, /**< unload */
+ NULL, /**< destroy */
+
+ NULL, /**< ui_info */
+ NULL /**< extra_info */
+};
+
+static void
+__init_plugin(GaimPlugin *plugin)
+{
+}
+
+/* This may be horribly wrong. Oh the mayhem! */
+GAIM_INIT_PLUGIN(remote, __init_plugin, info)