source: trunk/gtk/ipc.c @ 2227

Last change on this file since 2227 was 2227, checked in by joshe, 15 years ago

Add client/server name to IPC version handshake.

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