root/branches/vendor/third/gaim/plugins/gaim-remote/remote.c @ 22221

Revision 22221, 17.7 KB (checked in by ghudson, 4 years ago)

Import gaim 1.4.0.

Line 
1/**
2 * Remote control plugin for Gaim
3 *
4 * Copyright (C) 2003 Christian Hammond.
5 * Copyright (C) 1998-1999, Mark Spencer <markster@marko.net>
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as
9 * published by the Free Software Foundation; either version 2 of the
10 * License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
20 * 02111-1307, USA.
21 *
22 * @todo Make this a core plugin!
23 */
24#include "internal.h"
25#include "gtkgaim.h"
26
27#ifndef _WIN32
28# include <sys/un.h>
29#endif
30
31#include <signal.h>
32#include <getopt.h>
33
34#include "conversation.h"
35#include "core.h"
36#include "debug.h"
37#include "prpl.h"
38#include "notify.h"
39#include "version.h"
40
41/* XXX */
42#include "away.h"
43#include "gtkconv.h"
44#include "gtkplugin.h"
45#include "gaim.h"
46#include "prefs.h"
47
48#include <gaim-remote/remote.h>
49
50#define REMOTE_PLUGIN_ID "gtk-remote"
51
52struct UI {
53        GIOChannel *channel;
54        guint inpa;
55};
56
57#ifndef _WIN32
58static gint UI_fd = -1;
59static guint watcher = 0;
60#endif
61static int gaim_session = 0;
62static GSList *uis = NULL;
63
64/* AIM URI's ARE FUN :-D */
65static const char *
66gaim_remote_handle_uri(const char *uri)
67{
68        const char *username;
69        GString *str;
70        GList *conn;
71        GaimConnection *gc = NULL;
72        GaimAccount *account;
73
74        gaim_debug(GAIM_DEBUG_INFO, "gaim_remote_handle_uri", "Handling URI: %s\n", uri);
75
76        /* Well, we'd better check to make sure we have at least one
77           AIM account connected. */
78        for (conn = gaim_connections_get_all(); conn != NULL; conn = conn->next) {
79                gc = conn->data;
80                account = gaim_connection_get_account(gc);
81                username = gaim_account_get_username(account);
82
83                if (strcmp(gaim_account_get_protocol_id(account), "prpl-oscar") == 0 &&
84                        username != NULL && isalpha(*username)) {
85
86                        break;
87                }
88        }
89
90        if (gc == NULL)
91                return _("Not connected to AIM");
92
93        /* aim:goim?screenname=screenname&message=message */
94        if (!g_ascii_strncasecmp(uri, "aim:goim?", strlen("aim:goim?"))) {
95                char *who, *what;
96                GaimConversation *c;
97                uri = uri + strlen("aim:goim?");
98               
99                if (!(who = strstr(uri, "screenname="))) {
100                        return _("No screenname given.");
101                }
102                /* spaces are encoded as +'s */
103                who = who + strlen("screenname=");
104                str = g_string_new(NULL);
105                while (*who && (*who != '&')) {
106                        g_string_append_c(str, *who == '+' ? ' ' : *who);
107                        who++;
108                }
109                who = g_strdup(str->str);
110                g_string_free(str, TRUE);
111               
112                what = strstr(uri, "message=");
113                if (what) {
114                        what = what + strlen("message=");
115                        str = g_string_new(NULL);
116                        while (*what && (*what != '&' || !g_ascii_strncasecmp(what, "&amp;", 5))) {
117                                g_string_append_c(str, *what == '+' ? ' ' : *what);
118                                what++;
119                        }
120                        what = g_strdup(str->str);
121                        g_string_free(str, TRUE);
122                }
123
124                c = gaim_conversation_new(GAIM_CONV_IM, gc->account, who);
125                g_free(who);
126
127                if (what) {
128                        GaimGtkConversation *gtkconv = GAIM_GTK_CONVERSATION(c);
129
130                        gtk_text_buffer_insert_at_cursor(gtkconv->entry_buffer, what, -1);
131                        g_free(what);
132                }
133        } else if (!g_ascii_strncasecmp(uri, "aim:addbuddy?", strlen("aim:addbuddy?"))) {
134                char *who, *group;
135                uri = uri + strlen("aim:addbuddy?");
136                /* spaces are encoded as +'s */
137
138                if (!(who = strstr(uri, "screenname="))) {
139                        return _("No screenname given.");
140                }
141                who = who + strlen("screenname=");
142                str = g_string_new(NULL);
143                while (*who && (*who != '&')) {
144                        g_string_append_c(str, *who == '+' ? ' ' : *who);
145                        who++;
146                }
147                who = g_strdup(str->str);
148                g_string_free(str, TRUE);
149
150                group = strstr(uri, "group=");
151                if (group) {
152                        group = group + strlen("group=");
153                        str = g_string_new(NULL);
154                        while (*group && (*group != '&' || !g_ascii_strncasecmp(group, "&amp;", 5))) {
155                                g_string_append_c(str, *group == '+' ? ' ' : *group);
156                                group++;
157                        }
158                        group = g_strdup(str->str);
159                        g_string_free(str, TRUE);
160                }
161
162                gaim_debug(GAIM_DEBUG_MISC, "gaim_remote_handle_uri", "who: %s\n", who);
163                gaim_blist_request_add_buddy(gc->account, who, group, NULL);
164                g_free(who);
165                if (group)
166                        g_free(group);
167        } else if (!g_ascii_strncasecmp(uri, "aim:gochat?", strlen("aim:gochat?"))) {
168                char *room;
169                GHashTable *components;
170                int exch = 5;
171               
172                uri = uri + strlen("aim:gochat?");
173                /* spaces are encoded as +'s */
174               
175                if (!(room = strstr(uri, "roomname="))) {
176                        return _("No roomname given.");
177                }
178                room = room + strlen("roomname=");
179                str = g_string_new(NULL);
180                while (*room && (*room != '&')) {
181                        g_string_append_c(str, *room == '+' ? ' ' : *room);
182                        room++;
183                }
184                room = g_strdup(str->str);
185                g_string_free(str, TRUE);
186                components = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
187                                g_free);
188                g_hash_table_replace(components, g_strdup("room"), room);
189                g_hash_table_replace(components, g_strdup("exchange"),
190                                g_strdup_printf("%d", exch));
191
192                serv_join_chat(gc, components);
193                g_hash_table_destroy(components);
194        } else {
195                return _("Invalid AIM URI");
196        }
197       
198       
199        return NULL;
200}
201
202
203
204#if 0
205static guchar *
206UI_build(guint32 *len, guchar type, guchar subtype, va_list args)
207{
208        guchar *buffer;
209        guint32 pos;
210        int size;
211        void *data;
212
213        *len = sizeof(guchar) * 2 + 4;
214        buffer = g_malloc(*len);
215        pos = 0;
216
217        memcpy(buffer + pos, &type, sizeof(type)); pos += sizeof(type);
218        memcpy(buffer + pos, &subtype, sizeof(subtype)); pos += sizeof(subtype);
219
220        /* we come back and do size last */
221        pos += 4;
222
223        size = va_arg(args, int);
224        while (size != -1) {
225                *len += size;
226                buffer = g_realloc(buffer, *len);
227
228                data = va_arg(args, void *);
229                memcpy(buffer + pos, data, size);
230                pos += size;
231
232                size = va_arg(args, int);
233        }
234
235        pos -= sizeof(guchar) * 2 + 4;
236
237        /* now we do size */
238        memcpy(buffer + sizeof(guchar) * 2, &pos, 4);
239
240        return buffer;
241}
242
243static gint
244UI_write(struct UI *ui, guchar *data, gint len)
245{
246        GError *error = NULL;
247        gint sent;
248        /* we'll let the write silently fail because the read will pick it up as dead */
249        g_io_channel_write_chars(ui->channel, data, len, &sent, &error);
250        if (error)
251                g_error_free(error);
252        return sent;
253}
254
255static void
256UI_build_write(struct UI *ui, guchar type, guchar subtype, ...)
257{
258        va_list ap;
259        gchar *data;
260        guint32 len;
261
262        va_start(ap, subtype);
263        data = UI_build(&len, type, subtype, ap);
264        va_end(ap);
265
266        UI_write(ui, data, len);
267
268        g_free(data);
269}
270
271static void
272UI_broadcast(guchar *data, gint len)
273{
274        GSList *u = uis;
275        while (u) {
276                struct UI *ui = u->data;
277                UI_write(ui, data, len);
278                u = u->next;
279        }
280}
281
282static void
283UI_build_broadcast(guchar type, guchar subtype, ...)
284{
285        va_list ap;
286        gchar *data;
287        guint32 len;
288
289        if (!uis)
290                return;
291
292        va_start(ap, subtype);
293        data = UI_build(&len, type, subtype, ap);
294        va_end(ap);
295
296        UI_broadcast(data, len);
297
298        g_free(data);
299}
300#endif
301
302#ifndef _WIN32
303static void
304meta_handler(struct UI *ui, guchar subtype, gchar *data)
305{
306        GaimRemotePacket *p;
307        GError *error = NULL;
308        switch (subtype) {
309        case CUI_META_LIST:
310                break;
311        case CUI_META_QUIT:
312                while (uis) {
313                        ui = uis->data;
314                        uis = g_slist_remove(uis, ui);
315                        g_io_channel_shutdown(ui->channel, TRUE, &error);
316                        g_source_remove(ui->inpa);
317                        g_free(ui);
318                }
319                g_timeout_add(0, gaim_core_quit_cb, NULL);
320                break;
321        case CUI_META_DETACH:
322                uis = g_slist_remove(uis, ui);
323                g_io_channel_shutdown(ui->channel, TRUE, &error);
324                g_source_remove(ui->inpa);
325                g_free(ui);
326                break;
327        case CUI_META_PING:
328                p = gaim_remote_packet_new(CUI_TYPE_META, CUI_META_ACK);
329                gaim_remote_session_send_packet(g_io_channel_unix_get_fd(ui->channel),
330                                                                                p);
331                gaim_remote_packet_free(p);
332                break;
333        default:
334                gaim_debug(GAIM_DEBUG_WARNING, "cui",
335                                   "Unhandled meta subtype %d\n", subtype);
336                break;
337        }
338
339        if(error)
340                g_error_free(error);
341}
342
343static void
344plugin_handler(struct UI *ui, guchar subtype, gpointer data)
345{
346#ifdef GAIM_PLUGINS
347        guint id;
348        GaimPlugin *p;
349
350        switch (subtype) {
351                /*
352        case CUI_PLUGIN_LIST:
353                break;
354                */
355        case CUI_PLUGIN_LOAD:
356                gaim_plugin_load(gaim_plugin_probe(data));
357                break;
358        case CUI_PLUGIN_UNLOAD:
359                memcpy(&id, data, sizeof(id));
360                p = g_list_nth_data(gaim_plugins_get_loaded(), id);
361                if (p) {
362                        gaim_plugin_unload(p);
363                }
364                break;
365        default:
366                gaim_debug(GAIM_DEBUG_WARNING, "cui",
367                                   "Unhandled plugin subtype %d\n", subtype);
368                break;
369        }
370#endif
371}
372
373static void
374user_handler(struct UI *ui, guchar subtype, gchar *data)
375{
376        guint id;
377        GaimAccount *account;
378
379        switch (subtype) {
380                /*
381        case CUI_USER_LIST:
382                break;
383        case CUI_USER_ADD:
384                break;
385        case CUI_USER_REMOVE:
386                break;
387        case CUI_USER_MODIFY:
388                break;
389                */
390        case CUI_USER_SIGNON:
391                if (!data)
392                        return;
393                memcpy(&id, data, sizeof(id));
394                account = g_list_nth_data(gaim_accounts_get_all(), id);
395                if (account)
396                        gaim_account_connect(account);
397                /* don't need to do anything here because the UI will get updates from other handlers */
398                break;
399       case CUI_USER_AWAY:
400                {
401                    GSList* l;
402                    const char* default_away_name = gaim_prefs_get_string("/core/away/default_message");
403
404                    for(l = away_messages; l; l = l->next) {
405                        if(!strcmp(default_away_name, ((struct away_message *)l->data)->name)) {
406                            do_away_message(NULL, l->data);
407                            break;
408                        }
409                    }
410                }
411                break;
412       case CUI_USER_BACK:
413                do_im_back(NULL, NULL);
414                break;
415        default:
416                gaim_debug(GAIM_DEBUG_WARNING, "cui",
417                                   "Unhandled user subtype %d\n", subtype);
418                break;
419        }
420}
421
422static void
423message_handler(struct UI *ui, guchar subtype, gchar *data)
424{
425        switch (subtype) {
426        case CUI_MESSAGE_LIST:
427                break;
428        case CUI_MESSAGE_SEND:
429                if (!data)
430                        return;
431                {
432                        guint id;
433                        GaimConnection *gc;
434                        guint len;
435                        char *who, *msg;
436                        gint flags;
437                        int pos = 0;
438
439                        memcpy(&id, data + pos, sizeof(id));
440                        pos += sizeof(id);
441                        gc = g_list_nth_data(gaim_connections_get_all(), id);
442                        if (!gc)
443                                return;
444
445                        memcpy(&len, data + pos, sizeof(len));
446                        pos += sizeof(len);
447                        who = g_strndup(data + pos, len + 1);
448                        pos += len;
449
450                        memcpy(&len, data + pos, sizeof(len));
451                        pos += sizeof(len);
452                        msg = g_strndup(data + pos, len + 1);
453                        pos += len;
454
455                        memcpy(&flags, data + pos, sizeof(flags));
456                        serv_send_im(gc, who, msg, flags);
457
458                        g_free(who);
459                        g_free(msg);
460                }
461                break;
462        case CUI_MESSAGE_RECV:
463                break;
464        default:
465                gaim_debug(GAIM_DEBUG_WARNING, "cui",
466                                   "Unhandled message subtype %d\n", subtype);
467                break;
468        }
469}
470
471static gint
472gaim_recv(GIOChannel *source, gchar *buf, gint len)
473{
474        gint total = 0;
475        gsize cur;
476
477        GError *error = NULL;
478
479        while (total < len) {
480                if (g_io_channel_read_chars(source, buf + total, len - total, &cur, &error) != G_IO_STATUS_NORMAL) {
481                        if (error)
482                                g_error_free(error);
483                        return -1;
484                }
485                if (cur == 0)
486                        return total;
487                total += cur;
488        }
489
490        return total;
491}
492
493static void
494remote_handler(struct UI *ui, guchar subtype, gchar *data, int len)
495{
496        const char *resp;
497        char *send;
498        switch (subtype) {
499        case CUI_REMOTE_CONNECTIONS:
500                break;
501        case CUI_REMOTE_URI:
502                send = g_malloc(len + 1);
503                memcpy(send, data, len);
504                send[len] = 0;
505                resp = gaim_remote_handle_uri(send);
506                g_free(send);
507                /* report error */
508                break;
509        default:
510                gaim_debug(GAIM_DEBUG_WARNING, "cui",
511                                   "Unhandled remote subtype %d\n", subtype);
512                break;
513        }
514}
515
516static gboolean
517UI_readable(GIOChannel *source, GIOCondition cond, gpointer data)
518{
519        struct UI *ui = data;
520
521        gchar type;
522        gchar subtype;
523        gint len;
524
525        GError *error = NULL;
526
527        gchar *in;
528
529        /* no byte order worries! this'll change if we go to TCP */
530        if (gaim_recv(source, &type, sizeof(type)) != sizeof(type)) {
531                gaim_debug(GAIM_DEBUG_ERROR, "cui", "UI has abandoned us!\n");
532                uis = g_slist_remove(uis, ui);
533                g_io_channel_shutdown(ui->channel, TRUE, &error);
534                if(error) {
535                        g_error_free(error);
536                        error = NULL;
537                }
538                g_source_remove(ui->inpa);
539                g_free(ui);
540                return FALSE;
541        }
542
543        if (gaim_recv(source, &subtype, sizeof(subtype)) != sizeof(subtype)) {
544                gaim_debug(GAIM_DEBUG_ERROR, "cui", "UI has abandoned us!\n");
545                uis = g_slist_remove(uis, ui);
546                g_io_channel_shutdown(ui->channel, TRUE, &error);
547                if(error) {
548                        g_error_free(error);
549                        error = NULL;
550                }
551                g_source_remove(ui->inpa);
552                g_free(ui);
553                return FALSE;
554        }
555
556        if (gaim_recv(source, (gchar *)&len, sizeof(len)) != sizeof(len)) {
557                gaim_debug(GAIM_DEBUG_ERROR, "cui", "UI has abandoned us!\n");
558                uis = g_slist_remove(uis, ui);
559                g_io_channel_shutdown(ui->channel, TRUE, &error);
560                if(error) {
561                        g_error_free(error);
562                        error = NULL;
563                }
564                g_source_remove(ui->inpa);
565                g_free(ui);
566                return FALSE;
567        }
568
569        if (len) {
570                in = g_new0(gchar, len);
571                if (gaim_recv(source, in, len) != len) {
572                        gaim_debug(GAIM_DEBUG_ERROR, "cui", "UI has abandoned us!\n");
573                        uis = g_slist_remove(uis, ui);
574                        g_io_channel_shutdown(ui->channel, TRUE, &error);
575                        if(error) {
576                                g_error_free(error);
577                                error = NULL;
578                        }
579                        g_source_remove(ui->inpa);
580                        g_free(ui);
581                        return FALSE;
582                }
583        } else
584                in = NULL;
585
586        switch (type) {
587                case CUI_TYPE_META:
588                        meta_handler(ui, subtype, in);
589                        break;
590                case CUI_TYPE_PLUGIN:
591                        plugin_handler(ui, subtype, in);
592                        break;
593                case CUI_TYPE_USER:
594                        user_handler(ui, subtype, in);
595                        break;
596                        /*
597                case CUI_TYPE_CONN:
598                        conn_handler(ui, subtype, in);
599                        break;
600                case CUI_TYPE_BUDDY:
601                        buddy_handler(ui, subtype, in);
602                        break;
603                        */
604                case CUI_TYPE_MESSAGE:
605                        message_handler(ui, subtype, in);
606                        break;
607                        /*
608                case CUI_TYPE_CHAT:
609                        chat_handler(ui, subtype, in);
610                        break;
611                        */   
612                case CUI_TYPE_REMOTE:
613                        remote_handler(ui, subtype, in, len);
614                        break; 
615        default:
616                        gaim_debug(GAIM_DEBUG_WARNING, "cui",
617                                           "Unhandled type %d\n", type);
618                        break;
619        }
620
621        if (in)
622                g_free(in);
623        return TRUE;
624}
625
626static gboolean
627socket_readable(GIOChannel *source, GIOCondition cond, gpointer data)
628{
629        struct sockaddr_un saddr;
630        guint len = sizeof(saddr);
631        gint fd;
632
633        struct UI *ui;
634
635        if ((fd = accept(UI_fd, (struct sockaddr *)&saddr, &len)) == -1)
636                return FALSE;
637
638        ui = g_new0(struct UI, 1);
639        uis = g_slist_append(uis, ui);
640
641        ui->channel = g_io_channel_unix_new(fd);
642        ui->inpa = g_io_add_watch(ui->channel, G_IO_IN | G_IO_HUP | G_IO_ERR, UI_readable, ui);
643        g_io_channel_unref(ui->channel);
644
645        gaim_debug(GAIM_DEBUG_MISC, "cui", "Got one\n");
646        return TRUE;
647}
648
649static gint
650open_socket(char **error)
651{
652        struct sockaddr_un saddr;
653        gint fd;
654
655        while (gaim_remote_session_exists(gaim_session))
656                gaim_session++;
657
658        gaim_debug(GAIM_DEBUG_MISC, "cui", "Session: %d\n", gaim_session);
659
660        if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) != -1) {
661                mode_t m = umask(0177);
662                saddr.sun_family = AF_UNIX;
663
664                g_snprintf(saddr.sun_path, sizeof(saddr.sun_path), "%s" G_DIR_SEPARATOR_S "gaim_%s.%d",
665                                g_get_tmp_dir(), g_get_user_name(), gaim_session);
666                /* Unlink any existing socket for this session */
667                unlink (saddr.sun_path);
668
669                if (bind(fd, (struct sockaddr *)&saddr, sizeof(saddr)) != -1)
670                        listen(fd, 100);
671                else {
672                        char *tmp = g_locale_to_utf8(strerror(errno), -1, NULL, NULL, NULL);
673                        *error = g_strdup_printf(_("Failed to assign %s to a socket:\n%s"),
674                                           saddr.sun_path, tmp);
675                        g_log(NULL, G_LOG_LEVEL_CRITICAL,
676                              "Failed to assign %s to a socket (Error: %s)",
677                              saddr.sun_path, tmp);
678                        g_free(tmp);
679                        umask(m);
680                        return -1;
681                }
682                umask(m);
683        } else
684                g_log(NULL, G_LOG_LEVEL_CRITICAL, "Unable to open socket: %s", strerror(errno));
685        return fd;
686}
687#endif /*! _WIN32*/
688
689static gboolean
690plugin_load(GaimPlugin *plugin)
691{
692#ifndef _WIN32
693        GIOChannel *channel;
694        char *buf;
695
696        if ((UI_fd = open_socket(&buf)) < 0) {
697                gaim_notify_error(NULL, NULL, _("Unable to open socket"), buf);
698                g_free(buf);
699                return FALSE;
700        }
701
702        channel = g_io_channel_unix_new(UI_fd);
703        watcher = g_io_add_watch(channel, G_IO_IN, socket_readable, NULL);
704        g_io_channel_unref(channel);
705
706        return TRUE;
707#else
708        return FALSE;
709#endif
710}
711
712static gboolean
713plugin_unload(GaimPlugin *plugin)
714{
715        /* don't save prefs after plugins are gone... */
716#ifndef _WIN32
717        char buf[1024];
718
719        g_source_remove(watcher);
720        close(UI_fd);
721
722        g_snprintf(buf, sizeof(buf), "%s" G_DIR_SEPARATOR_S "gaim_%s.%d",
723                        g_get_tmp_dir(), g_get_user_name(), gaim_session);
724
725        unlink(buf);
726
727        gaim_debug(GAIM_DEBUG_MISC, "core", "Removed core\n");
728
729        return TRUE;
730#else
731        return FALSE;
732#endif
733}
734
735static GaimPluginInfo info =
736{
737        GAIM_PLUGIN_MAGIC,
738        GAIM_MAJOR_VERSION,
739        GAIM_MINOR_VERSION,
740        GAIM_PLUGIN_STANDARD,                             /**< type           */
741        GAIM_GTK_PLUGIN_TYPE,                             /**< ui_requirement */
742        0,                                                /**< flags          */
743        NULL,                                             /**< dependencies   */
744        GAIM_PRIORITY_DEFAULT,                            /**< priority       */
745
746        REMOTE_PLUGIN_ID,                                 /**< id             */
747        N_("Remote Control"),                             /**< name           */
748        VERSION,                                          /**< version        */
749                                                          /**  summary        */
750        N_("Provides remote control for gaim applications."),
751                                                          /**  description    */
752        N_("Gives Gaim the ability to be remote-controlled through third-party "
753           "applications or through the gaim-remote tool."),
754        "Sean Egan <sean.egan@binghamton.edu>",
755        GAIM_WEBSITE,                                     /**< homepage       */
756
757        plugin_load,                                      /**< load           */
758        plugin_unload,                                    /**< unload         */
759        NULL,                                             /**< destroy        */
760
761        NULL,                                             /**< ui_info        */
762        NULL,                                             /**< extra_info     */
763        NULL,
764        NULL
765};
766
767static void
768__init_plugin(GaimPlugin *plugin)
769{
770}
771
772/* This may be horribly wrong. Oh the mayhem! */
773GAIM_INIT_PLUGIN(remote, __init_plugin, info)
Note: See TracBrowser for help on using the browser.