Mercurial > pidgin
view plugins/gaim-remote/remote.c @ 11098:df3b825c1b46
[gaim-migrate @ 13136]
" I was playing with some new versions of gettext and
friends at debconf, and found some horrific bugs in the
.po files. Here's the general summary of the problems
and their fixes:
* no.po should be called nb.po - renamed
* a Makevars is required for gettext 0.14.4 - added
* am.po was missing a plural form - added
* da.po had some mismatched C format types - marked as
fuzzy
* ka.po had "nplurals=INTEGER; plural=EXPRESSION;"
instead of the actual plural form - turns out nplural
is 1, so deleted the duplicate identical 2nd strings
* mk.po had the same problem, added the plural form but
nplural is 3 and the translations in the file only have
2 entries, so I marked them as fuzzy
* pl.po - fixed mismatched C format type
* ru.po - basically the same as mk.po. had a crap
plural form. added it and marked the wrongly-numbered
plurals as fuzzy
* sq.po - added plural form, but file looks correct
otherwise (nplurals=2)
* tr.po - tweaks to the header, and nplurals=1 so set
this and deleted pointless duplicate second forms
You need to move no.po to nb.po - it would be silly to
include that in the patch obviously.
Please apply to HEAD ASAP before it stops applying
(obviously if anyone changes the po files the patch
will break very easily) but also please leave this item
open until me or someone else has looked for and
corrected the same issues in oldstable.
Regards,
Rob"
I've been ignoring translations in HEAD as its really rather pointless for
now. I plan to continue doing so. but as I was less than clear talking to
robot101, i didn't think it was fair to penalize him.
committer: Tailor Script <tailor@pidgin.im>
author | Luke Schierer <lschiere@pidgin.im> |
---|---|
date | Wed, 13 Jul 2005 00:59:44 +0000 |
parents | 50224ac8184d |
children | bb0d7b719af2 |
line wrap: on
line source
/** * 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 "gtkgaim.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" #include "notify.h" #include "util.h" #include "version.h" /* XXX */ #include "gtkconv.h" #include "gtkplugin.h" #include "gaim.h" #include "prefs.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_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 (strcmp(gaim_account_get_protocol_id(account), "prpl-oscar") == 0 && 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, "&", 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, "&", 5))) { g_string_append_c(str, *group == '+' ? ' ' : *group); group++; } group = g_strdup(str->str); g_string_free(str, TRUE); } 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); } g_timeout_add(0, gaim_core_quit_cb, NULL); 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_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_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; #if 0 /* STATUS */ case CUI_USER_AWAY: { GSList* l; const char* default_away_name = gaim_prefs_get_string("/core/away/default_message"); for (l = away_messages; l; l = l->next) { if (!strcmp(default_away_name, ((struct away_message *)l->data)->name)) { do_away_message(NULL, l->data); break; } } } break; case CUI_USER_BACK: do_im_back(NULL, NULL); break; #endif /* STATUS */ case CUI_USER_LOGOUT: gaim_connections_disconnect_all(); break; default: 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_warning("cui", "Unhandled message subtype %d\n", subtype); break; } } static gint gaim_recv(GIOChannel *source, gchar *buf, gint len) { gint total = 0; gsize 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, gchar *data, int len) { const char *resp; char *send; GList *c = gaim_connections_get_all(); GaimConnection *gc = NULL; GaimAccount *account; switch (subtype) { case CUI_REMOTE_CONNECTIONS: break; case CUI_REMOTE_SEND: if (!data) return; { GaimConversation *conv; guint tlen, len, len2, quiet; char *who, *msg; char *tmp, *from, *proto; int pos = 0; gaim_debug_info("cui", "Got `gaim-remote send` packet\n",data); gaim_debug_info("cui", "g-r>%s;\n",data); tmp = g_strndup(data + pos, 4); tlen = atoi(tmp); pos += 4; who = g_strndup(data+pos, tlen); pos += tlen; tmp = g_strndup(data + pos, 4); tlen = atoi(tmp); len=tlen; /* length for 'from' compare */ pos += 4; from = g_strndup(data+pos, tlen); pos += tlen; tmp = g_strndup(data + pos, 4); tlen = atoi(tmp); len2=tlen; /* length for 'proto' compare */ pos += 4; proto = g_strndup(data+pos, tlen); pos += tlen; tmp = g_strndup(data + pos, 4); tlen = atoi(tmp); pos += 4; msg = g_strndup(data+pos, tlen); pos += tlen; tmp = g_strndup(data + pos, 1); quiet = atoi(tmp); /* quiet flag - not in use yet */ /* find acct */ while (c) { gc = c->data; account=gaim_connection_get_account(gc); if ((!gaim_utf8_strcasecmp(from, gaim_account_get_username(account))) && (!g_ascii_strncasecmp(proto, gaim_account_get_protocol_id(account), len2)) ) break; c = c->next; } if (!gc) return; /* end acct find */ /* gaim_debug_info("cui", "g-r>To: %s; From: %s; Protocol: %s; Message: %s; Quiet: %d\n",who,from,proto,msg,quiet); */ conv = gaim_conversation_new(GAIM_CONV_IM, gaim_connection_get_account(gc), who); gaim_conv_im_send(GAIM_CONV_IM(conv), msg); /* likely to be used for quiet: serv_send_im(gc, who, msg, -1, 0); */ g_free(who); g_free(msg); g_free(from); g_free(tmp); } 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_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; gint 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_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_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_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_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_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_misc("cui", "Got one\n"); return TRUE; } static gint open_socket(char **error) { struct sockaddr_un saddr; gint fd; while (gaim_remote_session_exists(gaim_session)) gaim_session++; 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); /* Remove any stale socket on this session */ unlink(saddr.sun_path); if (bind(fd, (struct sockaddr *)&saddr, sizeof(saddr)) != -1) listen(fd, 100); else { char *tmp = g_locale_to_utf8(strerror(errno), -1, NULL, NULL, NULL); *error = g_strdup_printf(_("Failed to assign %s to a socket:\n%s"), saddr.sun_path, tmp); g_log(NULL, G_LOG_LEVEL_CRITICAL, "Failed to assign %s to a socket (Error: %s)", saddr.sun_path, strerror(errno)); umask(m); g_free(tmp); 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; char *buf = NULL; gaim_debug_register_category("cui"); gaim_debug_register_category("gaim_remote_handle_uri"); if ((UI_fd = open_socket(&buf)) < 0) { gaim_notify_error(NULL, NULL, _("Unable to open socket"), buf); g_free(buf); 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); g_unlink(buf); gaim_debug_misc("core", "Removed core\n"); gaim_debug_unregister_category("cui"); gaim_debug_unregister_category("gaim_remote_handle_uri"); return TRUE; #else return FALSE; #endif } static GaimPluginInfo info = { GAIM_PLUGIN_MAGIC, GAIM_MAJOR_VERSION, GAIM_MINOR_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."), "Sean Egan <sean.egan@binghamton.edu>", GAIM_WEBSITE, /**< homepage */ plugin_load, /**< load */ plugin_unload, /**< unload */ NULL, /**< destroy */ NULL, /**< ui_info */ NULL, /**< extra_info */ NULL, NULL }; static void _init_plugin(GaimPlugin *plugin) { } /* This may be horribly wrong. Oh the mayhem! */ GAIM_INIT_PLUGIN(remote, _init_plugin, info)