source: trunk/gtk/ipc.c @ 3206

Last change on this file since 3206 was 3206, checked in by charles, 15 years ago

preferences code refresh in the gtk+ client

  • Property svn:keywords set to Date Rev Author Id
File size: 34.1 KB
Line 
1/******************************************************************************
2 * $Id: ipc.c 3206 2007-09-27 20:57:58Z charles $
3 *
4 * Copyright (c) 2006-2007 Transmission authors and contributors
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 * DEALINGS IN THE SOFTWARE.
23 *****************************************************************************/
24
25#include <sys/types.h>
26#include <sys/socket.h>
27#include <sys/un.h>
28#include <errno.h>
29#include <fcntl.h>
30#include <stdio.h>
31#include <stdlib.h>
32#include <string.h>
33#include <unistd.h>
34
35#include <gtk/gtk.h>
36#include <glib/gi18n.h>
37
38#include <libtransmission/transmission.h>
39#include <libtransmission/bencode.h>
40#include <libtransmission/ipcparse.h>
41
42#include "conf.h"
43#include "io.h"
44#include "ipc.h"
45#include "tr_core.h"
46#include "tr_prefs.h"
47#include "tr_torrent.h"
48#include "util.h"
49
50#ifndef AF_LOCAL
51#define AF_LOCAL AF_UNIX
52#endif
53
54#ifndef SUN_LEN
55#define SUN_LEN( sun )                                                       \
56  ( sizeof( *(sun) ) - sizeof( (sun)->sun_path ) + strlen( (sun)->sun_path ) )
57#endif
58
59/* XXX error handling throughout this file is pretty bogus */
60
61enum contype { CON_SERV, CON_CLIENT };
62
63struct constate_serv
64{
65    GtkWindow * wind;
66    gpointer    core;
67};
68
69struct constate_client
70{
71    GMainLoop  * loop;
72    enum ipc_msg msg;
73    GList      * files;
74    gboolean   * succeeded;
75    unsigned int msgid;
76};
77
78struct constate
79{
80    GSource          * source;
81    int                fd;
82    enum contype       type;
83    struct ipc_funcs * msgs;
84    struct ipc_info  * ipc;
85    union
86    {
87        struct constate_serv   serv;
88        struct constate_client client;
89    } u;
90};
91
92static void
93serv_bind(struct constate *con);
94static void
95rmsock(void);
96static gboolean
97client_connect(char *path, struct constate *con);
98static void
99srv_io_accept(GSource *source, int fd, struct sockaddr *sa, socklen_t len,
100              void *vdata);
101static size_t
102srv_io_received( GSource * source, void * data, size_t len, void * vdata );
103static size_t
104cli_io_received( GSource * source, void * data, size_t len, void * vdata );
105static void
106client_sendmsg( struct constate * con );
107static void
108destroycon(struct constate *con);
109static void
110all_io_closed(GSource *source, void *vdata);
111static void
112cli_io_sent( GSource * source, size_t id, void * vdata );
113static void
114smsg_add( enum ipc_msg id, benc_val_t * val, int64_t tag, void * arg );
115static void
116smsg_addone( enum ipc_msg id, benc_val_t * val, int64_t tag, void * arg );
117static void
118smsg_quit( enum ipc_msg id, benc_val_t * val, int64_t tag, void * arg );
119static void
120smsg_noop( enum ipc_msg id, benc_val_t * val, int64_t tag, void * arg );
121static void
122smsg_info( enum ipc_msg id, benc_val_t * val, int64_t tag, void * arg );
123static void
124smsg_infoall( enum ipc_msg id, benc_val_t * val, int64_t tag, void * arg );
125static int
126addinfo( TrTorrent * tor, enum ipc_msg msgid, int torid, int types,
127         benc_val_t * val );
128static void
129smsg_look( enum ipc_msg id, benc_val_t * val, int64_t tag, void * arg );
130static void
131smsg_tor( enum ipc_msg id, benc_val_t * val, int64_t tag, void * arg );
132static void
133smsg_torall( enum ipc_msg id, benc_val_t * val, int64_t tag, void * arg );
134static void
135smsg_pref( enum ipc_msg id, benc_val_t * val, int64_t tag, void * arg );
136static void
137smsg_int( enum ipc_msg id, benc_val_t * val, int64_t tag, void * arg );
138static void
139smsg_str( enum ipc_msg id, benc_val_t * val, int64_t tag, void * arg );
140static void
141smsg_sup( enum ipc_msg id, benc_val_t * val, int64_t tag, void * arg );
142static void
143all_default( enum ipc_msg id, benc_val_t * val, int64_t tag, void * arg );
144static gboolean
145simpleresp( struct constate * con, int64_t tag, enum ipc_msg id );
146static TrTorrent *
147findtorid( TrCore * core, int id, GtkTreeIter * iter );
148static TrTorrent *
149findtorhash( TrCore * core, const char * hash, int * id );
150
151/* this is only used on the server */
152static char *gl_sockpath = NULL;
153
154void
155ipc_socket_setup( GtkWindow * parent, TrCore * core )
156{
157  struct constate *con;
158
159  con = g_new0(struct constate, 1);
160  con->source = NULL;
161  con->fd = -1;
162  con->type = CON_SERV;
163
164  con->msgs = ipc_initmsgs();
165  if( NULL == con->msgs ||
166      0 > ipc_addmsg( con->msgs, IPC_MSG_ADDMANYFILES, smsg_add ) ||
167      0 > ipc_addmsg( con->msgs, IPC_MSG_ADDONEFILE,   smsg_addone ) ||
168      0 > ipc_addmsg( con->msgs, IPC_MSG_AUTOMAP,      smsg_int ) ||
169      0 > ipc_addmsg( con->msgs, IPC_MSG_AUTOSTART,    smsg_int ) ||
170      0 > ipc_addmsg( con->msgs, IPC_MSG_DIR,          smsg_str ) ||
171      0 > ipc_addmsg( con->msgs, IPC_MSG_DOWNLIMIT,    smsg_int ) ||
172      0 > ipc_addmsg( con->msgs, IPC_MSG_GETAUTOMAP,   smsg_pref ) ||
173      0 > ipc_addmsg( con->msgs, IPC_MSG_GETAUTOSTART, smsg_pref ) ||
174      0 > ipc_addmsg( con->msgs, IPC_MSG_GETDIR,       smsg_pref ) ||
175      0 > ipc_addmsg( con->msgs, IPC_MSG_GETDOWNLIMIT, smsg_pref ) ||
176      0 > ipc_addmsg( con->msgs, IPC_MSG_GETINFO,      smsg_info ) ||
177      0 > ipc_addmsg( con->msgs, IPC_MSG_GETINFOALL,   smsg_infoall ) ||
178      0 > ipc_addmsg( con->msgs, IPC_MSG_GETPEX,       smsg_pref ) ||
179      0 > ipc_addmsg( con->msgs, IPC_MSG_GETPORT,      smsg_pref ) ||
180      0 > ipc_addmsg( con->msgs, IPC_MSG_GETSTAT,      smsg_info ) ||
181      0 > ipc_addmsg( con->msgs, IPC_MSG_GETSTATALL,   smsg_infoall ) ||
182      0 > ipc_addmsg( con->msgs, IPC_MSG_GETUPLIMIT,   smsg_pref ) ||
183      0 > ipc_addmsg( con->msgs, IPC_MSG_LOOKUP,       smsg_look ) ||
184      0 > ipc_addmsg( con->msgs, IPC_MSG_NOOP,         smsg_noop ) ||
185      0 > ipc_addmsg( con->msgs, IPC_MSG_PEX,          smsg_int ) ||
186      0 > ipc_addmsg( con->msgs, IPC_MSG_PORT,         smsg_int ) ||
187      0 > ipc_addmsg( con->msgs, IPC_MSG_QUIT,         smsg_quit ) ||
188      0 > ipc_addmsg( con->msgs, IPC_MSG_REMOVE,       smsg_tor ) ||
189      0 > ipc_addmsg( con->msgs, IPC_MSG_REMOVEALL,    smsg_torall ) ||
190      0 > ipc_addmsg( con->msgs, IPC_MSG_START,        smsg_tor ) ||
191      0 > ipc_addmsg( con->msgs, IPC_MSG_STARTALL,     smsg_torall ) ||
192      0 > ipc_addmsg( con->msgs, IPC_MSG_STOP,         smsg_tor ) ||
193      0 > ipc_addmsg( con->msgs, IPC_MSG_STOPALL,      smsg_torall ) ||
194      0 > ipc_addmsg( con->msgs, IPC_MSG_SUP,          smsg_sup ) ||
195      0 > ipc_addmsg( con->msgs, IPC_MSG_UPLIMIT,      smsg_int ) )
196  {
197      errmsg( con->u.serv.wind, _("Failed to set up IPC:\n%s"),
198              strerror( errno ) );
199      g_free( con );
200      return;
201  }
202
203  ipc_setdefmsg( con->msgs, all_default );
204
205  con->u.serv.wind = parent;
206  con->u.serv.core = core;
207
208  g_object_add_weak_pointer( G_OBJECT( core ), &con->u.serv.core );
209
210  serv_bind(con);
211}
212
213static gboolean
214blocking_client( enum ipc_msg msgid, GList * files )
215{
216
217  struct constate *con;
218  char *path;
219  gboolean ret = FALSE;
220
221  con = g_new0(struct constate, 1);
222  con->source = NULL;
223  con->fd = -1;
224  con->type = CON_CLIENT;
225
226  con->msgs = ipc_initmsgs();
227  if( NULL == con->msgs )
228  {
229      fprintf( stderr, _("failed to set up IPC: %s\n"), strerror( errno ) );
230      g_free( con );
231      return FALSE;
232  }
233
234  con->ipc = ipc_newcon( con->msgs );
235  if( NULL == con->ipc )
236  {
237      ipc_freemsgs( con->msgs );
238      g_free( con );
239      return FALSE;
240  }
241
242  ipc_setdefmsg( con->msgs, all_default );
243
244  con->u.client.loop = g_main_loop_new(NULL, TRUE);
245  con->u.client.msg = msgid;
246  con->u.client.files = files;
247  con->u.client.succeeded = &ret;
248  con->u.client.msgid = 0;
249
250  path = cf_sockname();
251  if(!client_connect(path, con)) {
252    g_free(path);
253    destroycon(con);
254    return FALSE;
255  }
256
257  g_main_loop_run(con->u.client.loop);
258
259  return ret;
260}
261
262gboolean
263ipc_sendfiles_blocking( GList * files )
264{
265    return blocking_client( IPC_MSG_ADDMANYFILES, files );
266}
267
268gboolean
269ipc_sendquit_blocking( void )
270{
271    return blocking_client( IPC_MSG_QUIT, NULL );
272}
273
274/* open a local socket for clients connections */
275static void
276serv_bind(struct constate *con) {
277  struct sockaddr_un sa;
278
279  rmsock();
280  gl_sockpath = cf_sockname();
281
282  if(0 > (con->fd = socket(AF_LOCAL, SOCK_STREAM, 0)))
283    goto fail;
284
285  memset(&sa, 0,  sizeof(sa));
286  sa.sun_family = AF_LOCAL;
287  strncpy(sa.sun_path, gl_sockpath, sizeof(sa.sun_path) - 1);
288
289  /* unlink any existing socket file before trying to create ours */
290  unlink(gl_sockpath);
291  if(0 > bind(con->fd, (struct sockaddr *)&sa, SUN_LEN(&sa))) {
292    /* bind may fail if there was already a socket, so try twice */
293    unlink(gl_sockpath);
294    if(0 > bind(con->fd, (struct sockaddr *)&sa, SUN_LEN(&sa)))
295      goto fail;
296  }
297
298  if(0 > listen(con->fd, 5))
299    goto fail;
300
301  con->source = io_new_listening(con->fd, sizeof(struct sockaddr_un),
302                                 srv_io_accept, all_io_closed, con);
303
304  g_atexit(rmsock);
305
306  return;
307
308 fail:
309  errmsg(con->u.serv.wind, _("Failed to set up socket:\n%s"),
310         strerror(errno));
311  if(0 <= con->fd)
312    close(con->fd);
313  con->fd = -1;
314  rmsock();
315}
316
317static void
318rmsock(void) {
319  if(NULL != gl_sockpath) {
320    unlink(gl_sockpath);
321    g_free(gl_sockpath);
322  }
323}
324
325static gboolean
326client_connect(char *path, struct constate *con) {
327  struct sockaddr_un addr;
328  uint8_t          * buf;
329  size_t             size;
330
331  if(0 > (con->fd = socket(AF_UNIX, SOCK_STREAM, 0))) {
332    fprintf(stderr, _("failed to create socket: %s\n"), strerror(errno));
333    return FALSE;
334  }
335
336  memset(&addr, 0,  sizeof(addr));
337  addr.sun_family = AF_UNIX;
338  strncpy(addr.sun_path, path, sizeof(addr.sun_path) - 1);
339
340  if(0 > connect(con->fd, (struct sockaddr*)&addr, SUN_LEN(&addr))) {
341    fprintf(stderr, _("failed to connect to %s: %s\n"), path, strerror(errno));
342    return FALSE;
343  }
344
345  con->source = io_new(con->fd, cli_io_sent, cli_io_received,
346                       all_io_closed, con);
347  if( NULL == con->source )
348  {
349      close( con->fd );
350      return FALSE;
351  }
352
353  buf = ipc_mkvers( &size, "Transmission GTK+ " LONG_VERSION_STRING );
354  if( NULL == buf )
355  {
356      close( con->fd );
357      return FALSE;
358  }
359
360  io_send_keepdata( con->source, buf, size );
361
362  return TRUE;
363}
364
365static void
366srv_io_accept(GSource *source SHUTUP, int fd, struct sockaddr *sa SHUTUP,
367              socklen_t len SHUTUP, void *vdata) {
368  struct constate *con = vdata;
369  struct constate *newcon;
370  uint8_t        * buf;
371  size_t           size;
372
373  newcon = g_new(struct constate, 1);
374  memcpy(newcon, con, sizeof(*newcon));
375  newcon->fd = fd;
376
377  newcon->ipc = ipc_newcon( con->msgs );
378  if( NULL == newcon->ipc )
379  {
380      g_free( newcon );
381      close( fd );
382      return;
383  }
384
385  newcon->source = io_new(fd, NULL, srv_io_received, all_io_closed, newcon);
386  if( NULL == newcon->source )
387  {
388      ipc_freecon( newcon->ipc );
389      g_free( newcon );
390      close( fd );
391      return;
392  }
393
394  buf = ipc_mkvers( &size, "Transmission GTK+ " LONG_VERSION_STRING );
395  if( NULL == buf )
396  {
397      ipc_freecon( newcon->ipc );
398      g_free( newcon );
399      close( fd );
400      return;
401  }
402
403  io_send_keepdata( newcon->source, buf, size );
404}
405
406static size_t
407srv_io_received( GSource * source SHUTUP, void * data, size_t len,
408                 void * vdata )
409{
410    struct constate      * con = vdata;
411    struct constate_serv * srv = &con->u.serv;
412    ssize_t                res;
413
414    if( IPC_MIN_MSG_LEN > len )
415    {
416        return 0;
417    }
418
419    if( NULL == srv->core )
420    {
421        destroycon( con );
422    }
423
424    res = ipc_parse( con->ipc, data, len, con );
425
426    if( 0 > res )
427    {
428        switch( errno )
429        {
430            case EPERM:
431                errmsg( con->u.serv.wind, _("bad IPC protocol version") );
432                break;
433            case EINVAL:
434                errmsg( con->u.serv.wind, _("IPC protocol parse error") );
435                break;
436            default:
437                errmsg( con->u.serv.wind, _("IPC parsing failed: %s"),
438                        strerror( errno ) );
439        }
440        destroycon( con );
441        return 0;
442    }
443
444    return res;
445}
446
447static size_t
448cli_io_received( GSource * source SHUTUP, void * data, size_t len,
449                 void * vdata )
450{
451    struct constate        * con = vdata;
452    struct constate_client * cli = &con->u.client;
453    ssize_t                  res;
454
455    if( IPC_MIN_MSG_LEN > len )
456    {
457        return 0;
458    }
459
460    res = ipc_parse( con->ipc, data, len, con );
461
462    if( 0 > res )
463    {
464        switch( errno )
465        {
466            case EPERM:
467                fprintf( stderr, _("bad IPC protocol version\n") );
468                break;
469            case EINVAL:
470                fprintf( stderr, _("IPC protocol parse error\n") );
471                break;
472            default:
473                fprintf( stderr, _("IPC parsing failed: %s\n"),
474                         strerror( errno ) );
475                break;
476        }
477        destroycon( con );
478        return 0;
479    }
480
481    if( HASVERS( con->ipc ) && 0 == cli->msgid )
482    {
483        client_sendmsg( con );
484    }
485
486    return res;
487}
488
489static void
490client_sendmsg( struct constate * con )
491{
492    struct constate_client * cli = &con->u.client;
493    GList                  * ii;
494    uint8_t                * buf;
495    size_t                   size;
496    benc_val_t               packet, * val;
497    int                      saved;
498
499    switch( cli->msg )
500    {
501        case IPC_MSG_ADDMANYFILES:
502            val = ipc_initval( con->ipc, cli->msg, -1, &packet, TYPE_LIST );
503            if( NULL == val ||
504                tr_bencListReserve( val, g_list_length( cli->files ) ) )
505            {
506                perror( "malloc" );
507                destroycon( con );
508                return;
509            }
510            for( ii = cli->files; NULL != ii; ii = ii->next )
511            {
512                tr_bencInitStr( tr_bencListAdd( val ), ii->data, -1, 0 );
513            }
514            buf = ipc_mkval( &packet, &size );
515            saved = errno;
516            tr_bencFree( &packet );
517            g_list_free( cli->files );
518            cli->files = NULL;
519            break;
520        case IPC_MSG_QUIT:
521            buf = ipc_mkempty( con->ipc, &size, cli->msg, -1 );
522            saved = errno;
523            break;
524        default:
525            g_assert_not_reached();
526            return;
527    }
528
529    if( NULL == buf )
530    {
531        errno = saved;
532        perror( "malloc" );
533        destroycon( con );
534        return;
535    }
536
537    cli->msgid = io_send_keepdata( con->source, buf, size );
538}
539
540static void
541destroycon(struct constate *con) {
542  con->source = NULL;
543
544  if(0 <= con->fd)
545    close(con->fd);
546  con->fd = -1;
547  ipc_freecon( con->ipc );
548
549  switch(con->type) {
550    case CON_SERV:
551      break;
552    case CON_CLIENT:
553      ipc_freemsgs( con->msgs );
554      freestrlist(con->u.client.files);
555      g_main_loop_quit(con->u.client.loop);
556      break;
557  }
558}
559
560static void
561all_io_closed(GSource *source SHUTUP, void *vdata) {
562  struct constate *con = vdata;
563
564  destroycon(con);
565}
566
567static void
568cli_io_sent( GSource * source SHUTUP, size_t id, void *vdata )
569{
570  struct constate_client *cli = &((struct constate*)vdata)->u.client;
571
572  if(0 < id && cli->msgid == id) {
573    *(cli->succeeded) = TRUE;
574    destroycon(vdata);
575  }
576}
577
578static void
579smsg_add( enum ipc_msg id SHUTUP, benc_val_t * val, int64_t tag, void * arg )
580{
581    struct constate      * con = arg;
582    struct constate_serv * srv = &con->u.serv;
583    enum tr_torrent_action action;
584    benc_val_t           * path;
585    int                    ii;
586
587    if( NULL == val || TYPE_LIST != val->type )
588    {
589        simpleresp( con, tag, IPC_MSG_BAD );
590        return;
591    }
592
593    action = tr_prefs_get_action( PREF_KEY_ADDIPC );
594    for( ii = 0; ii < val->val.l.count; ii++ )
595    {
596        path = val->val.l.vals + ii;
597        if( TYPE_STR == path->type &&
598            /* XXX somehow escape invalid utf-8 */
599            g_utf8_validate( path->val.s.s, path->val.s.i, NULL ) )
600        {
601            tr_core_add( TR_CORE( srv->core ), path->val.s.s, action, FALSE );
602        }
603    }
604    tr_core_torrents_added( TR_CORE( srv->core ) );
605
606    /* XXX should send info response back with torrent ids */
607    simpleresp( con, tag, IPC_MSG_OK );
608}
609
610static void
611smsg_addone( enum ipc_msg id SHUTUP, benc_val_t * val, int64_t tag,
612             void * arg )
613{
614    struct constate      * con = arg;
615    struct constate_serv * srv = &con->u.serv;
616    enum tr_torrent_action action;
617    benc_val_t           * file, * data, * dir, * start;
618    gboolean               paused;
619
620    if( NULL == val || TYPE_DICT != val->type )
621    {
622        simpleresp( con, tag, IPC_MSG_BAD );
623        return;
624    }
625
626    file  = tr_bencDictFind( val, "file" );
627    data  = tr_bencDictFind( val, "data" );
628    dir   = tr_bencDictFind( val, "directory" );
629    start = tr_bencDictFind( val, "autostart" );
630
631    if( ( NULL != file  && TYPE_STR != file->type  ) ||
632        ( NULL != data  && TYPE_STR != data->type  ) ||
633        ( NULL != dir   && TYPE_STR != dir->type   ) ||
634        ( NULL != start && TYPE_INT != start->type ) )
635    {
636        simpleresp( con, tag, IPC_MSG_BAD );
637        return;
638    }
639
640    action = tr_prefs_get_action( PREF_KEY_ADDIPC );
641    paused = ( NULL == start || start->val.i ? FALSE : TRUE );
642    if( NULL != file )
643    {
644        if( NULL == dir )
645        {
646            tr_core_add( srv->core, file->val.s.s, action, paused );
647        }
648        else
649        {
650            tr_core_add_dir( srv->core, file->val.s.s, dir->val.s.s,
651                             action, paused );
652        }
653    }
654    else
655    {
656        if( NULL == dir )
657        {
658            tr_core_add_data( srv->core, (uint8_t *) data->val.s.s,
659                              data->val.s.i, paused );
660        }
661        else
662        {
663            tr_core_add_data_dir( srv->core, (uint8_t *) data->val.s.s,
664                                  data->val.s.i, dir->val.s.s, paused );
665        }
666    }
667    tr_core_torrents_added( TR_CORE( srv->core ) );
668
669    /* XXX should send info response back with torrent ids */
670    simpleresp( con, tag, IPC_MSG_OK );
671}
672
673static void
674smsg_quit( enum ipc_msg id SHUTUP, benc_val_t * val SHUTUP, int64_t tag SHUTUP,
675           void * arg SHUTUP )
676{
677    struct constate      * con = arg;
678    struct constate_serv * srv = &con->u.serv;
679
680    tr_core_quit( srv->core );
681}
682
683static void
684smsg_noop( enum ipc_msg id SHUTUP, benc_val_t * val SHUTUP, int64_t tag,
685           void * arg )
686{
687    simpleresp( arg, tag, IPC_MSG_OK );
688}
689
690static void
691smsg_info( enum ipc_msg id, benc_val_t * val, int64_t tag, void * arg )
692{
693    struct constate      * con = arg;
694    struct constate_serv * srv = &con->u.serv;
695    enum ipc_msg           respid;
696    benc_val_t           * ids, * types, * idval, packet, * pkval;
697    int                    typeflags, ii;
698    TrTorrent            * tor;
699    uint8_t              * buf;
700    size_t                 size;
701
702    if( NULL == val || TYPE_DICT != val->type )
703    {
704        simpleresp( con, tag, IPC_MSG_BAD );
705        return;
706    }
707
708    respid = ( IPC_MSG_GETINFO == id ? IPC_MSG_INFO : IPC_MSG_STAT );
709    ids    = tr_bencDictFind( val, "id" );
710    types  = tr_bencDictFind( val, "types" );
711    if( NULL == ids   || TYPE_LIST != ids->type ||
712        NULL == types || TYPE_LIST != types->type )
713    {
714        simpleresp( con, tag, IPC_MSG_BAD );
715        return;
716    }
717    typeflags = ipc_infotypes( respid, types );
718
719    pkval = ipc_initval( con->ipc, respid, tag, &packet, TYPE_LIST );
720    if( NULL == pkval )
721    {
722        simpleresp( con, tag, IPC_MSG_FAIL );
723        return;
724    }
725    for( ii = 0; ids->val.l.count > ii; ii++ )
726    {
727        idval = &ids->val.l.vals[ii];
728        if( TYPE_INT != idval->type || !TORRENT_ID_VALID( idval->val.i ) ||
729            NULL == ( tor = findtorid( srv->core, idval->val.i, NULL ) ) )
730        {
731            continue;
732        }
733        if( 0 > addinfo( tor, respid, idval->val.i, typeflags, pkval ) )
734        {
735            tr_bencFree( &packet );
736            simpleresp( con, tag, IPC_MSG_FAIL );
737            return;
738        }
739    }
740
741    buf = ipc_mkval( &packet, &size );
742    tr_bencFree( &packet );
743    if( NULL == buf )
744    {
745        simpleresp( con, tag, IPC_MSG_FAIL );
746    }
747    else
748    {
749        io_send_keepdata( con->source, buf, size );
750    }
751}
752
753static void
754smsg_infoall( enum ipc_msg id, benc_val_t * val, int64_t tag, void * arg )
755{
756    struct constate      * con = arg;
757    struct constate_serv * srv = &con->u.serv;
758    enum ipc_msg           respid;
759    benc_val_t             packet, * pkval;
760    int                    typeflags;
761    GtkTreeModel         * model;
762    GtkTreeIter            iter;
763    int                    rowid;
764    TrTorrent            * tor;
765    uint8_t              * buf;
766    size_t                 size;
767
768    if( NULL == val || TYPE_LIST != val->type )
769    {
770        simpleresp( con, tag, IPC_MSG_BAD );
771        return;
772    }
773
774    respid = ( IPC_MSG_GETINFOALL == id ? IPC_MSG_INFO : IPC_MSG_STAT );
775    typeflags = ipc_infotypes( respid, val );
776
777    pkval = ipc_initval( con->ipc, respid, tag, &packet, TYPE_LIST );
778    if( NULL == pkval )
779    {
780        simpleresp( con, tag, IPC_MSG_FAIL );
781        return;
782    }
783
784    model = tr_core_model( srv->core );
785    if( gtk_tree_model_get_iter_first( model, &iter ) )
786    {
787        do
788        {
789            gtk_tree_model_get( model, &iter, MC_ID, &rowid,
790                                MC_TORRENT, &tor, -1 );
791            g_object_unref( tor );
792            if( 0 > addinfo( tor, respid, rowid, typeflags, pkval ) )
793            {
794                tr_bencFree( &packet );
795                simpleresp( con, tag, IPC_MSG_FAIL );
796                return;
797            }
798        }
799        while( gtk_tree_model_iter_next( model, &iter ) );
800    }
801
802    buf = ipc_mkval( &packet, &size );
803    tr_bencFree( &packet );
804    if( NULL == buf )
805    {
806        simpleresp( con, tag, IPC_MSG_FAIL );
807    }
808    else
809    {
810        io_send_keepdata( con->source, buf, size );
811    }
812}
813
814static int
815addinfo( TrTorrent * tor, enum ipc_msg msgid, int torid, int types,
816         benc_val_t * val )
817{
818    if( IPC_MSG_INFO == msgid )
819    {
820        const tr_info * inf = tr_torrent_info( tor );
821        return ipc_addinfo( val, torid, inf, types );
822    }
823    else
824    {
825        const tr_stat * st = tr_torrent_stat( tor );
826        return ipc_addstat( val, torid, st, types );
827    }
828}
829
830static void
831smsg_look( enum ipc_msg id SHUTUP, benc_val_t * val, int64_t tag,
832             void * arg )
833{
834    struct constate      * con = arg;
835    struct constate_serv * srv = &con->u.serv;
836    benc_val_t             packet, * pkval, * hash;
837    int                    ii, torid;
838    TrTorrent            * tor;
839    const tr_info        * inf;
840    uint8_t              * buf;
841    size_t                 size;
842
843    if( NULL == val || TYPE_LIST != val->type )
844    {
845        simpleresp( con, tag, IPC_MSG_BAD );
846        return;
847    }
848
849    pkval = ipc_initval( con->ipc, IPC_MSG_INFO, tag, &packet, TYPE_LIST );
850    if( NULL == pkval )
851    {
852        simpleresp( con, tag, IPC_MSG_FAIL );
853        return;
854    }
855
856    for( ii = 0; val->val.l.count > ii; ii++ )
857    {
858        hash = &val->val.l.vals[ii];
859        if( NULL == hash || TYPE_STR != hash->type ||
860            SHA_DIGEST_LENGTH * 2 != hash->val.s.i ||
861            NULL == ( tor = findtorhash( srv->core, hash->val.s.s, &torid ) ) )
862        {
863            continue;
864        }
865        inf = tr_torrent_info( tor );
866        if( 0 > ipc_addinfo( pkval, torid, inf, IPC_INF_HASH ) )
867        {
868            tr_bencFree( &packet );
869            simpleresp( con, tag, IPC_MSG_FAIL );
870            return;
871        }
872    }
873
874    buf = ipc_mkval( &packet, &size );
875    tr_bencFree( &packet );
876    if( NULL == buf )
877    {
878        simpleresp( con, tag, IPC_MSG_FAIL );
879    }
880    else
881    {
882        io_send_keepdata( con->source, buf, size );
883    }
884}
885
886static void
887smsg_tor( enum ipc_msg id, benc_val_t * val, int64_t tag, void * arg )
888{
889    struct constate      * con = arg;
890    struct constate_serv * srv = &con->u.serv;
891    benc_val_t           * idval;
892    TrTorrent            * tor;
893    GtkTreeIter            iter;
894    int                    ii;
895
896    if( NULL == val || TYPE_LIST != val->type )
897    {
898        simpleresp( con, tag, IPC_MSG_BAD );
899        return;
900    }
901
902    for( ii = 0; val->val.l.count > ii; ii++ )
903    {
904        idval = &val->val.l.vals[ii];
905        if( TYPE_INT != idval->type || !TORRENT_ID_VALID( idval->val.i ) ||
906            NULL == ( tor = findtorid( srv->core, idval->val.i, &iter ) ) )
907        {
908            continue;
909        }
910        switch( id )
911        {
912            case IPC_MSG_REMOVE:
913                tr_core_delete_torrent( srv->core, &iter );
914                break;
915            case IPC_MSG_START:
916                tr_torrent_start( tor );
917                break;
918            case IPC_MSG_STOP:
919                tr_torrent_stop( tor );
920                break;
921            default:
922                g_assert_not_reached();
923                break;
924        }
925    }
926
927    tr_core_update( srv->core );
928    tr_core_save( srv->core );
929
930    /* XXX this is a lie */
931    simpleresp( con, tag, IPC_MSG_OK );
932}
933
934static void
935smsg_torall( enum ipc_msg id, benc_val_t * val SHUTUP, int64_t tag,
936             void * arg )
937{
938    struct constate      * con = arg;
939    struct constate_serv * srv = &con->u.serv;
940    TrTorrent            * tor;
941    GtkTreeModel         * model;
942    GtkTreeIter            iter;
943
944    model = tr_core_model( srv->core );
945    if( gtk_tree_model_get_iter_first( model, &iter ) )
946    {
947        do
948        {
949            gtk_tree_model_get( model, &iter, MC_TORRENT, &tor, -1 );
950            switch( id )
951            {
952                case IPC_MSG_REMOVEALL:
953                    tr_core_delete_torrent( srv->core, &iter );
954                    break;
955                case IPC_MSG_STARTALL:
956                    tr_torrent_start( tor );
957                    break;
958                case IPC_MSG_STOPALL:
959                    tr_torrent_stop( tor );
960                    break;
961                default:
962                    g_assert_not_reached();
963                    break;
964            }
965            g_object_unref( tor );
966        }
967        while( gtk_tree_model_iter_next( model, &iter ) );
968    }
969
970    tr_core_update( srv->core );
971    tr_core_save( srv->core );
972
973    /* XXX this is a lie */
974    simpleresp( con, tag, IPC_MSG_OK );
975}
976
977static void
978smsg_pref( enum ipc_msg id, benc_val_t * val SHUTUP, int64_t tag, void * arg )
979{
980    struct constate      * con = arg;
981    struct constate_serv * srv = &con->u.serv;
982    uint8_t              * buf;
983    size_t                 size;
984    tr_handle_status     * hstat;
985    const char           * pref;
986    int                    num;
987
988    switch( id )
989    {
990        case IPC_MSG_GETAUTOMAP:
991            hstat = tr_handleStatus( tr_core_handle( srv->core ) );
992            buf = ipc_mkint( con->ipc, &size, IPC_MSG_AUTOMAP, tag,
993                             !TR_NAT_TRAVERSAL_IS_DISABLED(
994                                 hstat->natTraversalStatus ) );
995            break;
996        case IPC_MSG_GETAUTOSTART:
997            buf = ipc_mkint( con->ipc, &size, IPC_MSG_AUTOSTART, tag, 1 );
998            break;
999        case IPC_MSG_GETDIR:
1000            /* XXX sending back "" when we're prompting is kind of bogus */
1001            pref = pref_flag_get( PREF_KEY_DIR_ASK ) ? "" : getdownloaddir();
1002            buf = ipc_mkstr( con->ipc, &size, IPC_MSG_DIR, tag, pref );
1003            break;
1004        case IPC_MSG_GETDOWNLIMIT:
1005            num = pref_flag_get( PREF_KEY_DL_LIMIT_ENABLED )
1006                ? pref_int_get( PREF_KEY_DL_LIMIT )
1007                : -1;
1008            buf = ipc_mkint( con->ipc, &size, IPC_MSG_DOWNLIMIT, tag, num );
1009            break;
1010        case IPC_MSG_GETPEX:
1011            buf = ipc_mkint( con->ipc, &size, IPC_MSG_PEX, tag,
1012                             pref_flag_get( PREF_KEY_PEX ) );
1013            break;
1014        case IPC_MSG_GETPORT:
1015            buf = ipc_mkint( con->ipc, &size, IPC_MSG_PORT, tag,
1016                             pref_flag_get( PREF_KEY_PORT ) );
1017            break;
1018        case IPC_MSG_GETUPLIMIT:
1019            num = pref_flag_get( PREF_KEY_UL_LIMIT_ENABLED )
1020                ? pref_int_get( PREF_KEY_UL_LIMIT )
1021                : -1;
1022            buf = ipc_mkint( con->ipc, &size, IPC_MSG_UPLIMIT, tag, num );
1023            break;
1024        default:
1025            g_assert_not_reached();
1026            return;
1027    }
1028
1029    if( NULL != buf )
1030    {
1031        io_send_keepdata( con->source, buf, size );
1032    }
1033}
1034
1035static void
1036smsg_int( enum ipc_msg id, benc_val_t * val, int64_t tag, void * arg )
1037{
1038    struct constate      * con = arg;
1039    struct constate_serv * srv = &con->u.serv;
1040
1041    if( NULL == val || TYPE_INT != val->type || INT_MAX < val->val.i )
1042    {
1043        simpleresp( con, tag, IPC_MSG_BAD );
1044        return;
1045    }
1046
1047    switch( id )
1048    {
1049        case IPC_MSG_AUTOMAP:
1050            tr_core_set_pref_bool( srv->core, PREF_KEY_NAT, val->val.i );
1051            break;
1052        case IPC_MSG_AUTOSTART:
1053            simpleresp( con, tag, IPC_MSG_BAD );
1054            return;
1055        case IPC_MSG_DOWNLIMIT:
1056            if( 0 > val->val.i )
1057            {
1058                tr_core_set_pref_bool( srv->core, PREF_KEY_DL_LIMIT_ENABLED, 0 );
1059            }
1060            else
1061            {
1062                tr_core_set_pref_int( srv->core, PREF_KEY_DL_LIMIT, val->val.i );
1063                tr_core_set_pref_bool( srv->core, PREF_KEY_DL_LIMIT_ENABLED, 1 );
1064            }
1065            break;
1066        case IPC_MSG_PEX:
1067            tr_core_set_pref_bool( srv->core, PREF_KEY_PEX, val->val.i );
1068            break;
1069        case IPC_MSG_PORT:
1070            tr_core_set_pref_int( srv->core, PREF_KEY_PORT, val->val.i );
1071            break;
1072        case IPC_MSG_UPLIMIT:
1073            if( 0 > val->val.i )
1074            {
1075                tr_core_set_pref_bool( srv->core, PREF_KEY_UL_LIMIT_ENABLED, 0 );
1076            }
1077            else
1078            {
1079                tr_core_set_pref_int( srv->core, PREF_KEY_UL_LIMIT, val->val.i );
1080                tr_core_set_pref_bool( srv->core, PREF_KEY_UL_LIMIT_ENABLED, 1 );
1081            }
1082            break;
1083        default:
1084            g_assert_not_reached();
1085            return;
1086    }
1087}
1088
1089static void
1090smsg_str( enum ipc_msg id, benc_val_t * val, int64_t tag, void * arg )
1091{
1092    struct constate      * con = arg;
1093    struct constate_serv * srv = &con->u.serv;
1094
1095    if( NULL == val || TYPE_STR != val->type )
1096    {
1097        simpleresp( con, tag, IPC_MSG_BAD );
1098        return;
1099    }
1100
1101    switch( id )
1102    {
1103        case IPC_MSG_DIR:
1104            tr_core_set_pref( srv->core, PREF_KEY_DIR_DEFAULT, val->val.s.s );
1105            break;
1106        default:
1107            g_assert_not_reached();
1108            return;
1109    }
1110}
1111
1112static void
1113smsg_sup( enum ipc_msg id SHUTUP, benc_val_t * val, int64_t tag, void * arg )
1114{
1115    struct constate      * con = arg;
1116    benc_val_t             packet, * pkval, * name;
1117    int                    ii;
1118    enum ipc_msg           found;
1119    uint8_t              * buf;
1120    size_t                 size;
1121
1122    if( NULL == val || TYPE_LIST != val->type )
1123    {
1124        simpleresp( con, tag, IPC_MSG_BAD );
1125        return;
1126    }
1127
1128    pkval = ipc_initval( con->ipc, IPC_MSG_SUP, tag, &packet, TYPE_LIST );
1129    if( NULL == pkval )
1130    {
1131        simpleresp( con, tag, IPC_MSG_FAIL );
1132        return;
1133    }
1134    if( tr_bencListReserve( pkval, val->val.l.count ) )
1135    {
1136        tr_bencFree( &packet );
1137        simpleresp( con, tag, IPC_MSG_FAIL );
1138        return;
1139    }
1140
1141    for( ii = 0; val->val.l.count > ii; ii++ )
1142    {
1143        name = &val->val.l.vals[ii];
1144        if( NULL == name || TYPE_STR != name->type )
1145        {
1146            continue;
1147        }
1148        found = ipc_msgid( con->ipc, name->val.s.s );
1149        if( IPC__MSG_COUNT == found || !ipc_ishandled( con->ipc, found ) )
1150        {
1151            continue;
1152        }
1153        tr_bencInitStr( tr_bencListAdd( pkval ),
1154                        name->val.s.s, name->val.s.i, 1 );
1155    }
1156
1157    buf = ipc_mkval( &packet, &size );
1158    tr_bencFree( &packet );
1159    if( NULL == buf )
1160    {
1161        simpleresp( con, tag, IPC_MSG_FAIL );
1162    }
1163    else
1164    {
1165        io_send_keepdata( con->source, buf, size );
1166    }
1167}
1168
1169static void
1170all_default( enum ipc_msg id, benc_val_t * val SHUTUP, int64_t tag, void * arg )
1171{
1172    switch( id )
1173    {
1174        case IPC_MSG_FAIL:
1175        case IPC_MSG_NOTSUP:
1176        case IPC_MSG_BAD:
1177        case IPC_MSG_OK:
1178            break;
1179        default:
1180            simpleresp( arg, tag, IPC_MSG_NOTSUP );
1181            break;
1182    }
1183}
1184
1185static gboolean
1186simpleresp( struct constate * con, int64_t tag, enum ipc_msg id )
1187{
1188    uint8_t         * buf;
1189    size_t            size;
1190
1191    buf = ipc_mkempty( con->ipc, &size, id, tag );
1192    if( NULL == buf )
1193    {
1194        return FALSE;
1195    }
1196
1197    io_send_keepdata( con->source, buf, size );
1198
1199    return TRUE;
1200}
1201
1202static TrTorrent *
1203findtorid( TrCore * core, int id, GtkTreeIter * iter )
1204{
1205    GtkTreeModel * model;
1206    GtkTreeIter    myiter;
1207    int            rowid;
1208    TrTorrent    * tor;
1209
1210    if( NULL == iter )
1211    {
1212        iter = &myiter;
1213    }
1214
1215    model = tr_core_model( core );
1216    if( gtk_tree_model_get_iter_first( model, iter ) )
1217    {
1218        do
1219        {
1220            gtk_tree_model_get( model, iter, MC_ID, &rowid, -1 );
1221            if( rowid == id )
1222            {
1223                gtk_tree_model_get( model, iter, MC_TORRENT, &tor, -1 );
1224                g_object_unref( tor );
1225                return tor;
1226            }
1227        }
1228        while( gtk_tree_model_iter_next( model, iter ) );
1229    }
1230
1231    return NULL;
1232}
1233
1234static TrTorrent *
1235findtorhash( TrCore * core, const char * hash, int * torid )
1236{
1237    GtkTreeModel * model;
1238    GtkTreeIter    iter;
1239    char         * rowhash;
1240    TrTorrent    * tor;
1241
1242    model = tr_core_model( core );
1243    if( gtk_tree_model_get_iter_first( model, &iter ) )
1244    {
1245        do
1246        {
1247            gtk_tree_model_get( model, &iter, MC_HASH, &rowhash, -1 );
1248            if( 0 == strcmp( hash, rowhash ) )
1249            {
1250                gtk_tree_model_get( model, &iter, MC_ID, torid,
1251                                    MC_TORRENT, &tor, -1 );
1252                g_object_unref( tor );
1253                return tor;
1254            }
1255        }
1256        while( gtk_tree_model_iter_next( model, &iter ) );
1257    }
1258
1259    return NULL;
1260}
Note: See TracBrowser for help on using the repository browser.