transmission/daemon/server.c

1034 lines
28 KiB
C

/******************************************************************************
* $Id$
*
* Copyright (c) 2007 Joshua Elsasser
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*****************************************************************************/
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/un.h>
#include <assert.h>
#include <errno.h>
#include <event.h>
#include <fcntl.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <libtransmission/bsdtree.h>
#include <libtransmission/bencode.h>
#include <libtransmission/ipcparse.h>
#include "errors.h"
#include "misc.h"
#include "server.h"
#include "torrents.h"
/* time out clients after this many seconds */
#define CLIENT_TIMEOUT ( 60 )
struct client
{
int fd;
struct bufferevent * ev;
struct ipc_info * ipc;
RB_ENTRY( client ) link;
};
RB_HEAD( allclients, client );
static void newclient( int, short, void * );
static void noop ( struct bufferevent *, void * );
static void byebye ( struct bufferevent *, short, void * );
static void doread ( struct bufferevent *, void * );
static int queuemsg ( struct client *, uint8_t *, size_t );
static int msgresp ( struct client *, int64_t, enum ipc_msg );
static void defmsg ( enum ipc_msg, benc_val_t *, int64_t, void * );
static void noopmsg ( enum ipc_msg, benc_val_t *, int64_t, void * );
static void addmsg1 ( enum ipc_msg, benc_val_t *, int64_t, void * );
static void addmsg2 ( enum ipc_msg, benc_val_t *, int64_t, void * );
static void quitmsg ( enum ipc_msg, benc_val_t *, int64_t, void * );
static void intmsg ( enum ipc_msg, benc_val_t *, int64_t, void * );
static void strmsg ( enum ipc_msg, benc_val_t *, int64_t, void * );
static void infomsg ( enum ipc_msg, benc_val_t *, int64_t, void * );
static int addinfo ( benc_val_t *, int, int );
static int addstat ( benc_val_t *, int, int );
static void tormsg ( enum ipc_msg, benc_val_t *, int64_t, void * );
static void lookmsg ( enum ipc_msg, benc_val_t *, int64_t, void * );
static void prefmsg ( enum ipc_msg, benc_val_t *, int64_t, void * );
static void supmsg ( enum ipc_msg, benc_val_t *, int64_t, void * );
static int clientcmp( struct client *, struct client * );
RB_GENERATE_STATIC( allclients, client, link, clientcmp )
INTCMP_FUNC( clientcmp, client, ev )
static struct event_base * gl_base = NULL;
static struct ipc_funcs * gl_tree = NULL;
static int gl_debug = 0;
static int gl_exiting = 0;
static struct allclients gl_clients = RB_INITIALIZER( &gl_clients );
int
server_init( struct event_base * base )
{
assert( NULL == gl_base && NULL == gl_tree );
gl_base = base;
gl_tree = ipc_initmsgs();
if( NULL == gl_tree )
{
return -1;
}
if( 0 > ipc_addmsg( gl_tree, IPC_MSG_ADDMANYFILES, addmsg1 ) ||
0 > ipc_addmsg( gl_tree, IPC_MSG_ADDONEFILE, addmsg2 ) ||
0 > ipc_addmsg( gl_tree, IPC_MSG_AUTOMAP, intmsg ) ||
0 > ipc_addmsg( gl_tree, IPC_MSG_AUTOSTART, intmsg ) ||
0 > ipc_addmsg( gl_tree, IPC_MSG_CRYPTO, strmsg ) ||
0 > ipc_addmsg( gl_tree, IPC_MSG_DOWNLIMIT, intmsg ) ||
0 > ipc_addmsg( gl_tree, IPC_MSG_DIR, strmsg ) ||
0 > ipc_addmsg( gl_tree, IPC_MSG_GETAUTOMAP, prefmsg ) ||
0 > ipc_addmsg( gl_tree, IPC_MSG_GETAUTOSTART, prefmsg ) ||
0 > ipc_addmsg( gl_tree, IPC_MSG_GETCRYPTO, prefmsg ) ||
0 > ipc_addmsg( gl_tree, IPC_MSG_GETDOWNLIMIT, prefmsg ) ||
0 > ipc_addmsg( gl_tree, IPC_MSG_GETDIR, prefmsg ) ||
0 > ipc_addmsg( gl_tree, IPC_MSG_GETINFO, infomsg ) ||
0 > ipc_addmsg( gl_tree, IPC_MSG_GETINFOALL, infomsg ) ||
0 > ipc_addmsg( gl_tree, IPC_MSG_GETPEX, prefmsg ) ||
0 > ipc_addmsg( gl_tree, IPC_MSG_GETPORT, prefmsg ) ||
0 > ipc_addmsg( gl_tree, IPC_MSG_GETSTAT, infomsg ) ||
0 > ipc_addmsg( gl_tree, IPC_MSG_GETSTATALL, infomsg ) ||
0 > ipc_addmsg( gl_tree, IPC_MSG_GETUPLIMIT, prefmsg ) ||
0 > ipc_addmsg( gl_tree, IPC_MSG_GETSUP, supmsg ) ||
0 > ipc_addmsg( gl_tree, IPC_MSG_LOOKUP, lookmsg ) ||
0 > ipc_addmsg( gl_tree, IPC_MSG_NOOP, noopmsg ) ||
0 > ipc_addmsg( gl_tree, IPC_MSG_PEX, intmsg ) ||
0 > ipc_addmsg( gl_tree, IPC_MSG_PORT, intmsg ) ||
0 > ipc_addmsg( gl_tree, IPC_MSG_QUIT, quitmsg ) ||
0 > ipc_addmsg( gl_tree, IPC_MSG_REMOVE, tormsg ) ||
0 > ipc_addmsg( gl_tree, IPC_MSG_REMOVEALL, tormsg ) ||
0 > ipc_addmsg( gl_tree, IPC_MSG_START, tormsg ) ||
0 > ipc_addmsg( gl_tree, IPC_MSG_STARTALL, tormsg ) ||
0 > ipc_addmsg( gl_tree, IPC_MSG_STOP, tormsg ) ||
0 > ipc_addmsg( gl_tree, IPC_MSG_STOPALL, tormsg ) ||
0 > ipc_addmsg( gl_tree, IPC_MSG_UPLIMIT, intmsg ) )
{
return -1;
}
ipc_setdefmsg( gl_tree, defmsg );
return 0;
}
void
server_debug( int enable )
{
gl_debug = enable;
}
int
server_listen( int fd )
{
struct event * ev;
int flags;
assert( NULL != gl_base );
flags = fcntl( fd, F_GETFL );
if( 0 > flags )
{
errnomsg( "failed to get flags on socket" );
return -1;
}
if( 0 > fcntl( fd, F_SETFL, flags | O_NONBLOCK ) )
{
errnomsg( "failed to set flags on socket" );
return -1;
}
if( 0 > listen( fd, 5 ) )
{
errnomsg( "failed to listen on socket" );
return -1;
}
ev = malloc( sizeof *ev );
if( NULL == ev )
{
mallocmsg( sizeof *ev );
return -1;
}
event_set( ev, fd, EV_READ | EV_PERSIST, newclient, ev );
event_base_set( gl_base, ev );
event_add( ev, NULL );
return 0;
}
void
server_quit( void )
{
struct client * ii, * next;
if(gl_exiting)
return;
torrent_exit( 0 );
gl_exiting = 1;
for( ii = RB_MIN( allclients, &gl_clients ); NULL != ii; ii = next )
{
next = RB_NEXT( allclients, &gl_clients, ii );
byebye( ii->ev, EVBUFFER_EOF, NULL );
}
}
void
newclient( int fd, short event UNUSED, void * arg )
{
struct sockaddr_un sa;
struct client * client, * old;
socklen_t socklen;
struct bufferevent * clev;
int clfd;
size_t buflen;
uint8_t * buf;
if( gl_exiting )
{
event_del( arg );
return;
}
for( ;; )
{
client = calloc( 1, sizeof *client );
if( NULL == client )
{
mallocmsg( sizeof *client );
return;
}
socklen = sizeof sa;
clfd = accept( fd, ( struct sockaddr * )&sa, &socklen );
if( 0 > clfd )
{
if( EWOULDBLOCK != errno && EAGAIN != errno &&
ECONNABORTED != errno )
{
errnomsg( "failed to accept ipc connection" );
}
free( client );
break;
}
client->ipc = ipc_newcon( gl_tree );
if( NULL == client->ipc )
{
mallocmsg( sizeof *client->ipc );
close( clfd );
free( client );
return;
}
clev = bufferevent_new( clfd, doread, noop, byebye, client );
if( NULL == clev )
{
errnomsg( "failed to create bufferevent" );
close( clfd );
ipc_freecon( client->ipc );
free( client );
return;
}
bufferevent_base_set( gl_base, clev );
bufferevent_settimeout( clev, CLIENT_TIMEOUT, CLIENT_TIMEOUT );
client->fd = clfd;
client->ev = clev;
old = RB_INSERT( allclients, &gl_clients, client );
assert( NULL == old );
if( gl_debug )
{
printf( "*** new client %i\n", clfd );
}
bufferevent_enable( clev, EV_READ );
buf = ipc_mkvers( &buflen, "Transmission daemon " LONG_VERSION_STRING );
if( 0 > queuemsg( client, buf, buflen ) )
{
free( buf );
return;
}
free( buf );
}
}
void
noop( struct bufferevent * ev UNUSED, void * arg UNUSED )
{
/* libevent prior to 1.2 couldn't handle a NULL write callback */
}
void
byebye( struct bufferevent * ev, short what, void * arg UNUSED )
{
struct client * client, key;
if( !( EVBUFFER_EOF & what ) )
{
if( EVBUFFER_TIMEOUT & what )
{
errmsg( "client connection timed out" );
}
else if( EVBUFFER_READ & what )
{
errmsg( "read error on client connection" );
}
else if( EVBUFFER_WRITE & what )
{
errmsg( "write error on client connection" );
}
else if( EVBUFFER_ERROR & what )
{
errmsg( "error on client connection" );
}
else
{
errmsg( "unknown error on client connection: 0x%x", what );
}
}
memset( &key, 0, sizeof key );
key.ev = ev;
client = RB_FIND( allclients, &gl_clients, &key );
assert( NULL != client );
RB_REMOVE( allclients, &gl_clients, client );
bufferevent_free( ev );
close( client->fd );
ipc_freecon( client->ipc );
if( gl_debug )
{
printf( "*** client %i went bye-bye\n", client->fd );
}
free( client );
}
void
doread( struct bufferevent * ev, void * arg )
{
struct client * client = arg;
ssize_t res;
uint8_t * buf;
size_t len;
assert( !gl_exiting );
buf = EVBUFFER_DATA( EVBUFFER_INPUT( ev ) );
len = EVBUFFER_LENGTH( EVBUFFER_INPUT( ev ) );
if( gl_debug )
{
printf( "<<< %zu bytes from client %i: ", len, client->fd );
fwrite( buf, 1, len, stdout );
putc( '\n', stdout );
}
if( IPC_MIN_MSG_LEN > len )
{
return;
}
res = ipc_parse( client->ipc, buf, len, client );
if( gl_exiting )
{
return;
}
if( 0 > res )
{
switch( errno )
{
case EPERM:
errmsg( "unsupported protocol version" );
break;
case EINVAL:
errmsg( "protocol parse error" );
break;
default:
errnomsg( "parsing failed" );
break;
}
byebye( ev, EVBUFFER_ERROR, NULL );
}
else if( 0 < res )
{
evbuffer_drain( EVBUFFER_INPUT( ev ), res );
}
}
int
queuemsg( struct client * client, uint8_t * buf, size_t buflen )
{
if( NULL == buf )
{
if( EPERM != errno )
{
errnomsg( "failed to build message" );
byebye( client->ev, EVBUFFER_EOF, NULL );
}
return -1;
}
if( gl_debug )
{
printf( ">>> %zu bytes to client %i: ", buflen, client->fd );
fwrite( buf, 1, buflen, stdout );
putc( '\n', stdout );
}
if( 0 > bufferevent_write( client->ev, buf, buflen ) )
{
errnomsg( "failed to buffer %zd bytes of data for write", buflen );
return -1;
}
return 0;
}
int
msgresp( struct client * client, int64_t tag, enum ipc_msg id )
{
uint8_t * buf;
size_t buflen;
int ret;
if( 0 >= tag )
{
return 0;
}
buf = ipc_mkempty( client->ipc, &buflen, id, tag );
ret = queuemsg( client, buf, buflen );
free( buf );
return ret;
}
void
defmsg( enum ipc_msg id UNUSED, benc_val_t * val UNUSED, int64_t tag,
void * arg )
{
struct client * client = arg;
msgresp( client, tag, IPC_MSG_NOTSUP );
}
void
noopmsg( enum ipc_msg id UNUSED, benc_val_t * val UNUSED, int64_t tag,
void * arg )
{
struct client * client = arg;
msgresp( client, tag, IPC_MSG_OK );
}
void
addmsg1( enum ipc_msg id UNUSED, benc_val_t * val, int64_t tag, void * arg )
{
struct client * client = arg;
benc_val_t pk, * added, * file;
int ii, tor;
size_t buflen;
uint8_t * buf;
if( NULL == val || TYPE_LIST != val->type )
{
msgresp( client, tag, IPC_MSG_BAD );
return;
}
added = ipc_initval( client->ipc, IPC_MSG_INFO, tag, &pk, TYPE_LIST );
if( NULL == added )
{
errnomsg( "failed to build message" );
byebye( client->ev, EVBUFFER_EOF, NULL );
return;
}
for( ii = 0; ii < val->val.l.count; ii++ )
{
file = &val->val.l.vals[ii];
if( TYPE_STR != file->type )
{
continue;
}
/* XXX need to somehow inform client of skipped or failed files */
tor = torrent_add_file( file->val.s.s, NULL, -1 );
if( TORRENT_ID_VALID( tor ) )
{
const tr_info * inf = torrent_info( tor );
if( 0 > ipc_addinfo( added, tor, inf, 0 ) )
{
errnomsg( "failed to build message" );
tr_bencFree( &pk );
byebye( client->ev, EVBUFFER_EOF, NULL );
return;
}
}
}
buf = ipc_mkval( &pk, &buflen );
tr_bencFree( &pk );
queuemsg( client, buf, buflen );
free( buf );
}
void
addmsg2( enum ipc_msg id UNUSED, benc_val_t * dict, int64_t tag, void * arg )
{
struct client * client = arg;
benc_val_t * val, pk;
int tor, start;
size_t buflen;
uint8_t * buf;
const char * dir;
if( NULL == dict || TYPE_DICT != dict->type )
{
msgresp( client, tag, IPC_MSG_BAD );
return;
}
val = tr_bencDictFind( dict, "directory" );
dir = ( NULL == val || TYPE_STR != val->type ? NULL : val->val.s.s );
val = tr_bencDictFind( dict, "autostart" );
start = ( NULL == val || TYPE_INT != val->type ? -1 :
( val->val.i ? 1 : 0 ) );
val = tr_bencDictFind( dict, "data" );
if( NULL != val && TYPE_STR == val->type )
{
/* XXX detect duplicates and return a message indicating so */
tor = torrent_add_data( ( uint8_t * )val->val.s.s, val->val.s.i,
dir, start );
}
else
{
val = tr_bencDictFind( dict, "file" );
if( NULL == val || TYPE_STR != val->type )
{
msgresp( client, tag, IPC_MSG_BAD );
return;
}
/* XXX detect duplicates and return a message indicating so */
tor = torrent_add_file( val->val.s.s, dir, start );
}
if( TORRENT_ID_VALID( tor ) )
{
const tr_info * inf;
val = ipc_initval( client->ipc, IPC_MSG_INFO, tag, &pk, TYPE_LIST );
if( NULL == val )
{
errnomsg( "failed to build message" );
byebye( client->ev, EVBUFFER_EOF, NULL );
return;
}
inf = torrent_info( tor );
if( 0 > ipc_addinfo( val, tor, inf, 0 ) )
{
errnomsg( "failed to build message" );
tr_bencFree( &pk );
byebye( client->ev, EVBUFFER_EOF, NULL );
return;
}
buf = ipc_mkval( &pk, &buflen );
tr_bencFree( &pk );
queuemsg( client, buf, buflen );
free( buf );
}
else
{
msgresp( client, tag, IPC_MSG_FAIL );
}
}
void
quitmsg( enum ipc_msg id UNUSED, benc_val_t * val UNUSED, int64_t tag UNUSED,
void * arg UNUSED )
{
server_quit();
}
void
intmsg( enum ipc_msg id, benc_val_t * val, int64_t tag, void * arg )
{
struct client * client = arg;
int num;
if( NULL == val || TYPE_INT != val->type )
{
msgresp( client, tag, IPC_MSG_BAD );
return;
}
num = MAX( INT_MIN, MIN( INT_MAX, val->val.i ) );
switch( id )
{
case IPC_MSG_AUTOMAP:
torrent_enable_port_mapping( num ? 1 : 0 );
break;
case IPC_MSG_AUTOSTART:
torrent_set_autostart( num ? 1 : 0 );
break;
case IPC_MSG_DOWNLIMIT:
torrent_set_downlimit( num );
break;
case IPC_MSG_PEX:
torrent_set_pex( num ? 1 : 0 );
break;
case IPC_MSG_PORT:
torrent_set_port( num );
break;
case IPC_MSG_UPLIMIT:
torrent_set_uplimit( num );
break;
default:
assert( 0 );
return;
}
msgresp( client, tag, IPC_MSG_OK );
}
void
strmsg( enum ipc_msg id, benc_val_t * val, int64_t tag, void * arg )
{
struct client * client = arg;
if( NULL == val || TYPE_STR != val->type )
{
msgresp( client, tag, IPC_MSG_BAD );
return;
}
switch( id )
{
case IPC_MSG_CRYPTO:
if(!strcasecmp(val->val.s.s, "plaintext"))
torrent_set_encryption(TR_PLAINTEXT_PREFERRED);
else if(!strcasecmp(val->val.s.s, "preferred"))
torrent_set_encryption(TR_ENCRYPTION_PREFERRED);
else if(!strcasecmp(val->val.s.s, "required"))
torrent_set_encryption(TR_ENCRYPTION_REQUIRED);
else
{
msgresp(client, tag, IPC_MSG_BAD);
return;
}
break;
case IPC_MSG_DIR:
torrent_set_directory( val->val.s.s );
break;
default:
assert( 0 );
return;
}
msgresp( client, tag, IPC_MSG_OK );
}
void
infomsg( enum ipc_msg id, benc_val_t * val, int64_t tag, void * arg )
{
struct client * client = arg;
uint8_t * buf;
size_t buflen;
benc_val_t pk, * pkinf, * typelist, * idlist, * idval;
int all, types, ii, tor;
void * iter;
enum ipc_msg respid;
int ( * addfunc )( benc_val_t *, int, int );
all = 0;
switch( id )
{
case IPC_MSG_GETINFOALL:
all = 1;
/* FALLTHROUGH; */
case IPC_MSG_GETINFO:
respid = IPC_MSG_INFO;
addfunc = addinfo;
break;
case IPC_MSG_GETSTATALL:
all = 1;
/* FALLTHROUGH */
case IPC_MSG_GETSTAT:
respid = IPC_MSG_STAT;
addfunc = addstat;
break;
default:
assert( 0 );
return;
}
/* initialize packet */
pkinf = ipc_initval( client->ipc, respid, tag, &pk, TYPE_LIST );
if( NULL == pkinf )
{
errnomsg( "failed to build message" );
byebye( client->ev, EVBUFFER_EOF, NULL );
return;
}
/* add info/status for all torrents */
if( all )
{
if( NULL == val || TYPE_LIST != val->type )
{
msgresp( client, tag, IPC_MSG_BAD );
tr_bencFree( &pk );
return;
}
types = ipc_infotypes( respid, val );
iter = NULL;
while( NULL != ( iter = torrent_iter( iter, &tor ) ) )
{
if( 0 > addfunc( pkinf, tor, types ) )
{
errnomsg( "failed to build message" );
tr_bencFree( &pk );
byebye( client->ev, EVBUFFER_EOF, NULL );
return;
}
}
}
/* add info/status for the requested IDs */
else
{
if( NULL == val || TYPE_DICT != val->type )
{
msgresp( client, tag, IPC_MSG_BAD );
tr_bencFree( &pk );
return;
}
typelist = tr_bencDictFind( val, "type" );
idlist = tr_bencDictFind( val, "id" );
if( NULL == typelist || TYPE_LIST != typelist->type ||
NULL == idlist || TYPE_LIST != idlist->type )
{
msgresp( client, tag, IPC_MSG_BAD );
tr_bencFree( &pk );
return;
}
types = ipc_infotypes( respid, typelist );
for( ii = 0; idlist->val.l.count > ii; ii++ )
{
idval = &idlist->val.l.vals[ii];
if( TYPE_INT != idval->type || !TORRENT_ID_VALID( idval->val.i ) )
{
continue;
}
tor = idval->val.i;
if( 0 > addfunc( pkinf, idval->val.i, types ) )
{
errnomsg( "failed to build message" );
tr_bencFree( &pk );
byebye( client->ev, EVBUFFER_EOF, NULL );
return;
}
}
}
/* generate packet data and send it */
buf = ipc_mkval( &pk, &buflen );
tr_bencFree( &pk );
queuemsg( client, buf, buflen );
free( buf );
}
int
addinfo( benc_val_t * list, int id, int types )
{
const tr_info * inf = torrent_info( id );
return inf ? ipc_addinfo( list, id, inf, types ) : 0;
}
int
addstat( benc_val_t * list, int id, int types )
{
const tr_stat * st = torrent_stat( id );
return st ? ipc_addstat( list, id, st, types ) : 0;
}
void
tormsg( enum ipc_msg id, benc_val_t * val, int64_t tag, void * arg )
{
struct client * client = arg;
benc_val_t * idval;
int ii, all;
void * iter;
void ( * func )( int );
all = 0;
switch( id )
{
case IPC_MSG_REMOVEALL:
all = 1;
/* FALLTHROUGH */
case IPC_MSG_REMOVE:
func = torrent_remove;
break;
case IPC_MSG_STARTALL:
all = 1;
/* FALLTHROUGH */
case IPC_MSG_START:
func = torrent_start;
break;
case IPC_MSG_STOPALL:
all = 1;
/* FALLTHROUGH */
case IPC_MSG_STOP:
func = torrent_stop;
break;
default:
assert( 0 );
return;
}
/* remove/start/stop all torrents */
if( all )
{
iter = NULL;
while( NULL != ( iter = torrent_iter( iter, &ii ) ) )
{
func( ii );
if( torrent_remove == func )
{
iter = NULL;
}
}
}
/* remove/start/stop requested list of torrents */
else
{
if( NULL == val || TYPE_LIST != val->type )
{
msgresp( client, tag, IPC_MSG_BAD );
return;
}
for( ii = 0; val->val.l.count > ii; ii++ )
{
idval = &val->val.l.vals[ii];
if( TYPE_INT != idval->type || !TORRENT_ID_VALID( idval->val.i ) )
{
continue;
}
func( idval->val.i );
}
}
msgresp( client, tag, IPC_MSG_OK );
}
void
lookmsg( enum ipc_msg id UNUSED, benc_val_t * val, int64_t tag, void * arg )
{
struct client * client = arg;
uint8_t * buf;
size_t buflen;
int ii;
benc_val_t * hash, pk, * pkinf;
int64_t found;
if( NULL == val || TYPE_LIST != val->type )
{
msgresp( client, tag, IPC_MSG_BAD );
return;
}
pkinf = ipc_initval( client->ipc, IPC_MSG_INFO, tag, &pk, TYPE_LIST );
if( NULL == pkinf )
{
errnomsg( "failed to build message" );
byebye( client->ev, EVBUFFER_EOF, NULL );
return;
}
for( ii = 0; val->val.l.count > ii; ii++ )
{
const tr_info * inf;
hash = &val->val.l.vals[ii];
if( NULL == hash || TYPE_STR != hash->type ||
SHA_DIGEST_LENGTH * 2 != hash->val.s.i )
{
tr_bencFree( &pk );
msgresp( client, tag, IPC_MSG_BAD );
return;
}
found = torrent_lookup( ( uint8_t * )hash->val.s.s );
if( !TORRENT_ID_VALID( found ) )
{
continue;
}
inf = torrent_info( found );
assert( NULL != inf );
if( 0 > ipc_addinfo( pkinf, found, inf, IPC_INF_HASH ) )
{
errnomsg( "failed to build message" );
tr_bencFree( &pk );
byebye( client->ev, EVBUFFER_EOF, NULL );
return;
}
}
buf = ipc_mkval( &pk, &buflen );
tr_bencFree( &pk );
queuemsg( client, buf, buflen );
free( buf );
}
void
prefmsg( enum ipc_msg id, benc_val_t * val UNUSED, int64_t tag, void * arg )
{
struct client * client = arg;
uint8_t * buf;
size_t buflen;
const char * strval;
switch( id )
{
case IPC_MSG_GETAUTOMAP:
buf = ipc_mkint( client->ipc, &buflen, IPC_MSG_AUTOMAP, tag,
torrent_get_port_mapping() );
break;
case IPC_MSG_GETAUTOSTART:
buf = ipc_mkint( client->ipc, &buflen, IPC_MSG_AUTOSTART, tag,
torrent_get_autostart() );
break;
case IPC_MSG_GETCRYPTO:
switch(torrent_get_encryption())
{
case TR_PLAINTEXT_PREFERRED:
strval = "plaintext";
break;
case TR_ENCRYPTION_PREFERRED:
strval = "preferred";
break;
case TR_ENCRYPTION_REQUIRED:
strval = "required";
break;
default:
assert(0);
return;
}
buf = ipc_mkstr(client->ipc, &buflen, IPC_MSG_CRYPTO, tag, strval);
break;
case IPC_MSG_GETDIR:
buf = ipc_mkstr( client->ipc, &buflen, IPC_MSG_DIR, tag,
torrent_get_directory() );
break;
case IPC_MSG_GETDOWNLIMIT:
buf = ipc_mkint( client->ipc, &buflen, IPC_MSG_DOWNLIMIT, tag,
torrent_get_downlimit() );
break;
case IPC_MSG_GETPEX:
buf = ipc_mkint( client->ipc, &buflen, IPC_MSG_PEX, tag,
torrent_get_pex() );
break;
case IPC_MSG_GETPORT:
buf = ipc_mkint( client->ipc, &buflen, IPC_MSG_PORT, tag,
torrent_get_port() );
break;
case IPC_MSG_GETUPLIMIT:
buf = ipc_mkint( client->ipc, &buflen, IPC_MSG_UPLIMIT, tag,
torrent_get_uplimit() );
break;
default:
assert( 0 );
return;
}
queuemsg( client, buf, buflen );
free( buf );
}
void
supmsg( enum ipc_msg id UNUSED, benc_val_t * val, int64_t tag, void * arg )
{
struct client * client = arg;
uint8_t * buf;
size_t buflen;
int ii;
benc_val_t pk, *pkval, * name;
enum ipc_msg found;
if( NULL == val || TYPE_LIST != val->type )
{
msgresp( client, tag, IPC_MSG_BAD );
return;
}
pkval = ipc_initval( client->ipc, IPC_MSG_SUP, tag, &pk, TYPE_LIST );
if( NULL == pkval )
{
errnomsg( "failed to build message" );
byebye( client->ev, EVBUFFER_EOF, NULL );
return;
}
/* XXX look at other initval to make sure we free pk */
if( tr_bencListReserve( pkval, val->val.l.count ) )
{
errnomsg( "failed to build message" );
tr_bencFree( &pk );
byebye( client->ev, EVBUFFER_EOF, NULL );
return;
}
for( ii = 0; val->val.l.count > ii; ii++ )
{
name = &val->val.l.vals[ii];
if( NULL == name || TYPE_STR != name->type )
{
tr_bencFree( &pk );
msgresp( client, tag, IPC_MSG_BAD );
return;
}
found = ipc_msgid( client->ipc, name->val.s.s );
if( IPC__MSG_COUNT == found || !ipc_ishandled( client->ipc, found ) )
{
continue;
}
tr_bencInitStr( tr_bencListAdd( pkval ),
name->val.s.s, name->val.s.i, 1 );
}
buf = ipc_mkval( &pk, &buflen );
tr_bencFree( &pk );
queuemsg( client, buf, buflen );
free( buf );
}