Mercurial > pidgin
changeset 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 | 96e5b32e75ad |
children | 52d5fad43950 |
files | configure.ac plugins/Makefile.am plugins/gaim-remote/.cvsignore plugins/gaim-remote/Makefile.am plugins/gaim-remote/remote-socket.c plugins/gaim-remote/remote-socket.h plugins/gaim-remote/remote.c plugins/gaim-remote/remote.h src/Makefile.am src/connection.c src/core.c src/core.h src/gaim-remote.c src/main.c |
diffstat | 14 files changed, 947 insertions(+), 640 deletions(-) [+] |
line wrap: on
line diff
--- a/configure.ac Fri Jun 13 23:49:26 2003 +0000 +++ b/configure.ac Sat Jun 14 06:06:40 2003 +0000 @@ -414,6 +414,7 @@ pixmaps/status/default/Makefile plugins/Makefile plugins/docklet/Makefile + plugins/gaim-remote/Makefile plugins/gestures/Makefile plugins/perl/Makefile plugins/ticker/Makefile
--- a/plugins/Makefile.am Fri Jun 13 23:49:26 2003 +0000 +++ b/plugins/Makefile.am Sat Jun 14 06:06:40 2003 +0000 @@ -1,4 +1,4 @@ -DIST_SUBDIRS = docklet gestures perl ticker +DIST_SUBDIRS = docklet gaim-remote gestures perl ticker if USE_PERL PERL_DIR = perl
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugins/gaim-remote/.cvsignore Sat Jun 14 06:06:40 2003 +0000 @@ -0,0 +1,7 @@ +Makefile +Makefile.in +.deps +.libs +*.dll +*.la +*.lo
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugins/gaim-remote/Makefile.am Sat Jun 14 06:06:40 2003 +0000 @@ -0,0 +1,20 @@ +plugindir = $(libdir)/gaim + +gaim_remote_la_LDFLAGS = -module -avoid-version + +if PLUGINS + +plugin_LTLIBRARIES = gaim-remote.la + +gaim_remote_la_SOURCES = \ + remote.c \ + remote-socket.c \ + remote-socket.h + +endif + +INCLUDES = \ + -I$(top_srcdir)/src \ + -DVERSION=\"$(VERSION)\" \ + -DDATADIR=\"$(datadir)\" \ + $(DEBUG_CFLAGS)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugins/gaim-remote/remote-socket.c Sat Jun 14 06:06:40 2003 +0000 @@ -0,0 +1,203 @@ +/* + * 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/types.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <unistd.h> +#include "gaim.h" +#include "remote-socket.h" + +void +gaim_remote_session_send_packet(int fd, GaimRemotePacket *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 +gaim_remote_packet_append_string(GaimRemotePacket *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 +gaim_remote_packet_append_char(GaimRemotePacket *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 +gaim_remote_packet_append_raw(GaimRemotePacket *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; +} + +GaimRemotePacket * +gaim_remote_packet_new(guchar type, guchar subtype) +{ + GaimRemotePacket *p = g_new0(GaimRemotePacket, 1); + p->type = type; + p->subtype = subtype; + p->length = 0; + p->data = NULL; + return p; +} + +void +gaim_remote_packet_free(GaimRemotePacket *p) +{ + if (p->data) + g_free(p->data); + g_free(p); +} + +GaimRemotePacket * +gaim_remote_session_read_packet(int fd) +{ + GaimRemotePacket *p = g_new0(GaimRemotePacket, 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; + + return p; +} + +/* copied directly from xmms_connect_to_session */ +int +gaim_remote_session_connect(int 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_remote_session_exists(int sess) +{ + GaimRemotePacket *pack = NULL; + int fd = gaim_remote_session_connect(sess); + + if (fd > 0) { + pack = gaim_remote_packet_new(CUI_TYPE_META, CUI_META_PING); + gaim_remote_session_send_packet(fd, pack); + gaim_remote_packet_free(pack); + close(fd); + + return TRUE; + } + + return FALSE; +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugins/gaim-remote/remote-socket.h Sat Jun 14 06:06:40 2003 +0000 @@ -0,0 +1,46 @@ +/* + * gaim-remote + * + * Copyright (C) 2003 Christian Hammond <chipx86@gnupdate.org> + * 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_ + +#include <glib.h> + +typedef struct +{ + unsigned char type; + unsigned char subtype; + unsigned long length; + char *data; + +} GaimRemotePacket; + +void gaim_remote_session_send_packet(int fd, GaimRemotePacket *packet); +int gaim_remote_session_connect(int session); +gboolean gaim_remote_session_exists(int sess); +GaimRemotePacket *gaim_remote_session_read_packet(int fd); + +GaimRemotePacket *gaim_remote_packet_new(guchar type, guchar subtype); +void gaim_remote_packet_free(GaimRemotePacket *p); +void gaim_remote_packet_append_string(GaimRemotePacket *p, char *str); +void gaim_remote_packet_append_char(GaimRemotePacket *p, char c); +void gaim_remote_packet_append_raw(GaimRemotePacket *p, char *str, int len); + +#endif /* _GAIM_SOCKET_H_ */
--- /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);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugins/gaim-remote/remote.h Sat Jun 14 06:06:40 2003 +0000 @@ -0,0 +1,27 @@ +/* + * 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. + */ +#ifndef _GAIM_REMOTE_H_ +#define _GAIM_REMOTE_H_ + +#include <gaim-remote/remote-socket.h> + +#endif /* _GAIM_REMOTE_H_ */
--- a/src/Makefile.am Fri Jun 13 23:49:26 2003 +0000 +++ b/src/Makefile.am Sat Jun 14 06:06:40 2003 +0000 @@ -16,7 +16,6 @@ connection.h \ conversation.c \ conversation.h \ - core.c \ core.h \ debug.c \ debug.h \ @@ -55,7 +54,6 @@ dnd-hints.c \ dnd-hints.h \ gaim.h \ - gaim-socket.h \ gaim-disclosure.c \ gaim-disclosure.h \ gaimrc.c \ @@ -97,7 +95,6 @@ md5.h \ privacy.h \ session.c \ - socket.c \ stock.c \ stock.h \ themes.c \ @@ -115,7 +112,10 @@ $(INTLLIBS) \ $(GTKSPELL_LIBS) -gaim_remote_SOURCES = gaim-remote.c socket.c +gaim_remote_SOURCES = \ + gaim-remote.c \ + $(top_srcdir)/plugins/gaim-remote/remote-socket.c + gaim_remote_DEPENDENCIES = @LIBOBJS@ gaim_remote_LDADD = @LIBOBJS@ $(GLIB_LIBS) $(INTLLIBS) @@ -126,4 +126,5 @@ -DLOCALEDIR=\"$(datadir)/locale\" \ -DLIBDIR=\"$(libdir)/gaim/\" \ -DDATADIR=\"$(datadir)\" \ - $(DEBUG_CFLAGS) + $(DEBUG_CFLAGS) \ + -I$(top_srcdir)/plugins
--- a/src/connection.c Fri Jun 13 23:49:26 2003 +0000 +++ b/src/connection.c Sat Jun 14 06:06:40 2003 +0000 @@ -123,9 +123,9 @@ serv_close(gc); - gaim_connection_set_state(gc, GAIM_DISCONNECTED); + connections = g_list_remove(connections, gc); - connections = g_list_remove(connections, gc); + gaim_connection_set_state(gc, GAIM_DISCONNECTED); gaim_event_broadcast(event_signoff, gc); system_log(log_signoff, gc, NULL,
--- a/src/core.c Fri Jun 13 23:49:26 2003 +0000 +++ b/src/core.c Sat Jun 14 06:06:40 2003 +0000 @@ -1,7 +1,6 @@ /* * gaim * - * 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 @@ -28,521 +27,4 @@ #include <stdlib.h> #include <sys/types.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 "gaim-socket.h" - -#ifdef _WIN32 -#include "win32dep.h" -#endif - -#ifndef _WIN32 -static gint UI_fd = -1; -#endif -int gaim_session = 0; -GSList *uis = NULL; - -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; -} - -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; -} - -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); -} - -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; - } -} - -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); -} - -#ifndef _WIN32 -static void meta_handler(struct UI *ui, guchar subtype, guchar *data) -{ - struct gaim_cui_packet *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 = 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: - 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_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*/ - -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; -} - -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 -}
--- a/src/core.h Fri Jun 13 23:49:26 2003 +0000 +++ b/src/core.h Sat Jun 14 06:06:40 2003 +0000 @@ -96,25 +96,6 @@ #define GAIM_ACCOUNT_CHECK_MAIL(account) \ ((account)->options & OPT_ACCT_MAIL_CHECK) -struct UI { - GIOChannel *channel; - guint inpa; -}; - -/* Globals in core.c */ -extern GSList *uis; -extern int gaim_session; - -/* Functions in core.c */ -extern gint UI_write(struct UI *, guchar *, int); -extern void UI_build_write(struct UI *, guchar, guchar, ...); -extern void UI_broadcast(guchar *data, int); -extern void UI_build_broadcast(guchar, guchar, ...); -/* Don't ever use these; when gaim-core is done these will be - * merged into the core's main() and won't be called directly */ -extern int core_main(); -extern void core_quit(); - /* Functions in gaimrc.c */ extern void load_prefs(); extern void load_pounces();
--- a/src/gaim-remote.c Fri Jun 13 23:49:26 2003 +0000 +++ b/src/gaim-remote.c Sat Jun 14 06:06:40 2003 +0000 @@ -26,7 +26,8 @@ #include <unistd.h> #include <string.h> #include <locale.h> -#include "gaim-socket.h" + +#include <gaim-remote/remote.h> void show_remote_usage(char *name) { @@ -130,32 +131,32 @@ int command_uri() { int fd = 0; - struct gaim_cui_packet *p = NULL; - fd = gaim_connect_to_session(0); + GaimRemotePacket *p = NULL; + fd = gaim_remote_session_connect(0); if (!fd) { fprintf(stderr, _("Gaim not running (on session 0)\n")); return 1; } - p = cui_packet_new(CUI_TYPE_REMOTE, CUI_REMOTE_URI); - cui_packet_append_string(p, opts.uri); - cui_send_packet (fd, p); + p = gaim_remote_packet_new(CUI_TYPE_REMOTE, CUI_REMOTE_URI); + gaim_remote_packet_append_string(p, opts.uri); + gaim_remote_session_send_packet(fd, p); close(fd); - cui_packet_free(p); + gaim_remote_packet_free(p); return 0; } int command_quit() { int fd = 0; - struct gaim_cui_packet *p = NULL; - fd = gaim_connect_to_session(0); + GaimRemotePacket *p = NULL; + fd = gaim_remote_session_connect(0); if (!fd) { fprintf(stderr, _("Gaim not running (on session 0)\n")); return 1; } - p = cui_packet_new(CUI_TYPE_META, CUI_META_QUIT); - cui_send_packet (fd, p); + p = gaim_remote_packet_new(CUI_TYPE_META, CUI_META_QUIT); + gaim_remote_session_send_packet(fd, p); close(fd); - cui_packet_free(p); + gaim_remote_packet_free(p); return 0; }
--- a/src/main.c Fri Jun 13 23:49:26 2003 +0000 +++ b/src/main.c Sat Jun 14 06:06:40 2003 +0000 @@ -50,7 +50,6 @@ #include "sound.h" #include "gtksound.h" #include "gaim.h" -#include "gaim-socket.h" #include "account.h" #include "prefs.h" #include "notify.h" @@ -426,7 +425,6 @@ gaim_connections_disconnect_all(); break; case SIGSEGV: - core_quit(); #ifndef DEBUG fprintf(stderr, "Gaim has segfaulted and attempted to dump a core file.\n" "This is a bug in the software and has happened through\n" @@ -463,84 +461,14 @@ if (gtk_main_level()) gtk_main_quit(); - core_quit(); exit(0); } } #endif -#ifndef _WIN32 -static gboolean socket_readable(GIOChannel *source, GIOCondition cond, gpointer ud) -{ - guchar type; - guchar subtype; - guint32 len; - guchar *data; - guint32 x; - GError *error; - - gaim_debug(GAIM_DEBUG_INFO, "core socket", "Core says: "); - g_io_channel_read_chars(source, &type, sizeof(type), &x, &error); - if(error) - g_error_free(error); - if (x == 0) { - gaim_debug(GAIM_DEBUG_ERROR, NULL, "CORE IS GONE!\n"); - g_io_channel_shutdown(source, TRUE, &error); - if(error) - g_free(error); - return FALSE; - } - gaim_debug(GAIM_DEBUG_INFO, NULL, "%d ", type); - g_io_channel_read_chars(source, &subtype, sizeof(subtype), &x, &error); - if(error) - g_error_free(error); - if (x == 0) { - gaim_debug(GAIM_DEBUG_ERROR, NULL, "CORE IS GONE!\n"); - g_io_channel_shutdown(source, TRUE, &error); - if(error) - g_error_free(error); - return FALSE; - } - - gaim_debug(GAIM_DEBUG_INFO, NULL, "%d ", subtype); - g_io_channel_read_chars(source, (guchar *)&len, sizeof(len), &x, &error); - if(error) - g_error_free(error); - if (x == 0) { - gaim_debug(GAIM_DEBUG_ERROR, NULL, "CORE IS GONE!\n"); - g_io_channel_shutdown(source, TRUE, &error); - if(error) - g_error_free(error); - return FALSE; - } - - gaim_debug(GAIM_DEBUG_INFO, NULL, "(%d bytes)\n", len); - - data = g_malloc(len); - g_io_channel_read_chars(source, data, len, &x, &error); - if(error) - g_error_free(error); - if (x != len) { - gaim_debug(GAIM_DEBUG_ERROR, "core socket", - "CORE IS GONE! (read %d/%d bytes)\n", x, len); - g_free(data); - g_io_channel_shutdown(source, TRUE, &error); - if(error) - g_error_free(error); - return FALSE; - } - - g_free(data); - return TRUE; -} -#endif /* _WIN32 */ - static int ui_main() { #ifndef _WIN32 - GIOChannel *channel; - int UI_fd; - char name[256]; GList *icons = NULL; GdkPixbuf *icon = NULL; char *icon_path; @@ -571,14 +499,6 @@ gaim_debug(GAIM_DEBUG_ERROR, "ui_main", "Failed to load the default window icon!\n"); } - - g_snprintf(name, sizeof(name), "%s" G_DIR_SEPARATOR_S "gaim_%s.%d", g_get_tmp_dir(), g_get_user_name(), gaim_session); - UI_fd = gaim_connect_to_session(0); - if (UI_fd < 0) - return 1; - - channel = g_io_channel_unix_new(UI_fd); - g_io_add_watch(channel, G_IO_IN | G_IO_HUP | G_IO_ERR, socket_readable, NULL); #endif return 0; @@ -925,7 +845,9 @@ wgaim_init(); #endif - core_main(); + gaim_set_blist(gaim_blist_new()); + gaim_blist_load(); + load_pounces(); ui_main(); @@ -958,7 +880,7 @@ } } - if (!opt_acct && !opt_nologin && gaim_session == 0) + if (!opt_acct && !opt_nologin) gaim_accounts_auto_login(GAIM_GTK_UI); if (opt_acct) { @@ -967,7 +889,6 @@ show_login(); gtk_main(); - core_quit(); gaim_sound_shutdown(); #ifdef _WIN32 wgaim_cleanup();