Mercurial > pidgin
diff plugins/gaim-remote/remote.c @ 5859:022786c7ab53
[gaim-migrate @ 6290]
CUI is gone, long live gaim-remote! The old CUI functionality, which was
for remote-controlling gaim, is now a Core Plugin, so any future UI
(including the current, normal gaim gtk UI) can be remote-controlled.
Applications will soon be able to link against the library and header files
and provide their own remote-control of gaim, but why bother? :) If you
use gaim-remote, make sure to load the new plugin. It won't auto-load.
committer: Tailor Script <tailor@pidgin.im>
author | Christian Hammond <chipx86@chipx86.com> |
---|---|
date | Sat, 14 Jun 2003 06:06:40 +0000 |
parents | |
children | 059d95c67cda |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugins/gaim-remote/remote.c Sat Jun 14 06:06:40 2003 +0000 @@ -0,0 +1,617 @@ +/* + * 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. + */ +#include "config.h" + +#include "gaim.h" + +#ifdef _WIN32 +#include <winsock.h> +#include <io.h> +#else +#include <sys/socket.h> +#include <sys/un.h> +#include <unistd.h> +#endif + +#include <sys/stat.h> +#include <errno.h> +#include <signal.h> +#include <getopt.h> +#include <stdarg.h> +#include <string.h> + +#include "gaim.h" +#include "remote-socket.h" + +#ifdef _WIN32 +#include "win32dep.h" +#endif + + +#define REMOTE_PLUGIN_ID "core-remote" + +struct UI { + GIOChannel *channel; + guint inpa; +}; + +#ifndef _WIN32 +static gint UI_fd = -1; +#endif +int gaim_session = 0; +GSList *uis = 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, guchar *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); + } + do_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, guchar *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, guchar *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) + serv_login(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, guchar *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, -1, 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, guchar *buf, gint len) +{ + gint total = 0; + gint cur; + + GError *error = NULL; + + while (total < len) { + if (g_io_channel_read_chars(source, buf + total, len - total, &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, 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: + 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; + + guchar type; + guchar subtype; + guint32 len; + + GError *error = NULL; + + guchar *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, (guchar *)&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(guchar, 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; + gint 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)); + return -1; + } + umask(m); + } else + g_log(NULL, G_LOG_LEVEL_CRITICAL, "Unable to open socket: %s", strerror(errno)); + return fd; +} +#endif /*! _WIN32*/ + +static int +core_main() +{ + /* + GMainLoop *loop; + */ +#ifndef _WIN32 + GIOChannel *channel; +#endif + + gaim_set_blist(gaim_blist_new()); + gaim_blist_load(); + +#ifndef _WIN32 + + UI_fd = open_socket(); + if (UI_fd < 0) + return 1; + + channel = g_io_channel_unix_new(UI_fd); + g_io_add_watch(channel, G_IO_IN, socket_readable, NULL); + g_io_channel_unref(channel); +#endif + + /* + loop = g_main_new(TRUE); + g_main_run(loop); + */ + + return 0; +} + +static void +core_quit() +{ + /* don't save prefs after plugins are gone... */ +#ifndef _WIN32 + char buf[1024]; + close(UI_fd); + snprintf(buf, 1024, "%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"); +#endif +} + +static gboolean +plugin_load(GaimPlugin *plugin) +{ + core_main(); +} + +static gboolean +plugin_unload(GaimPlugin *plugin) +{ + core_quit(); +} + +static GaimPluginInfo info = +{ + 2, /**< api_version */ + GAIM_PLUGIN_STANDARD, /**< type */ + NULL, /**< 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 */ + WEBSITE, /**< homepage */ + + plugin_load, /**< load */ + plugin_unload, /**< unload */ + NULL, /**< destroy */ + + NULL, /**< ui_info */ + NULL /**< extra_info */ +}; + +static void +__init_plugin(GaimPlugin *plugin) +{ +} + +GAIM_INIT_PLUGIN(remote, __init_plugin, info);