1077 lines
32 KiB
Objective-C
1077 lines
32 KiB
Objective-C
/******************************************************************************
|
|
* $Id$
|
|
*
|
|
* Copyright (c) 2007 Transmission authors and contributors
|
|
*
|
|
* 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/un.h>
|
|
#include <assert.h>
|
|
#include <errno.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
|
|
#include "bencode.h"
|
|
#include "ipcparse.h"
|
|
#include "transmission.h"
|
|
|
|
#import "IPCController.h"
|
|
#import "Torrent.h"
|
|
#import "PrefsController.h"
|
|
|
|
static void
|
|
getaddr( struct sockaddr_un * );
|
|
static NSArray *
|
|
bencarray( benc_val_t * val, int type );
|
|
static void
|
|
msg_lookup ( enum ipc_msg msgid, benc_val_t * val, int64_t tag, void * arg );
|
|
static void
|
|
msg_info ( enum ipc_msg msgid, benc_val_t * val, int64_t tag, void * arg );
|
|
void
|
|
msg_infoall ( enum ipc_msg msgid, benc_val_t * val, int64_t tag, void * arg );
|
|
void
|
|
msg_action ( enum ipc_msg msgid, benc_val_t * val, int64_t tag, void * arg );
|
|
void
|
|
msg_actionall( enum ipc_msg msgid, benc_val_t * val, int64_t tag, void * arg );
|
|
void
|
|
msg_addold ( enum ipc_msg msgid, benc_val_t * val, int64_t tag, void * arg );
|
|
void
|
|
msg_addnew ( enum ipc_msg msgid, benc_val_t * val, int64_t tag, void * arg );
|
|
void
|
|
msg_getbool ( enum ipc_msg msgid, benc_val_t * val, int64_t tag, void * arg );
|
|
void
|
|
msg_getint ( enum ipc_msg msgid, benc_val_t * val, int64_t tag, void * arg );
|
|
void
|
|
msg_getstr ( enum ipc_msg msgid, benc_val_t * val, int64_t tag, void * arg );
|
|
void
|
|
msg_setbool ( enum ipc_msg msgid, benc_val_t * val, int64_t tag, void * arg );
|
|
void
|
|
msg_setint ( enum ipc_msg msgid, benc_val_t * val, int64_t tag, void * arg );
|
|
void
|
|
msg_setstr ( enum ipc_msg msgid, benc_val_t * val, int64_t tag, void * arg );
|
|
void
|
|
msg_empty ( enum ipc_msg msgid, benc_val_t * val, int64_t tag, void * arg );
|
|
void
|
|
msg_sup ( enum ipc_msg msgid, benc_val_t * val, int64_t tag, void * arg );
|
|
static void
|
|
msg_default ( enum ipc_msg msgid, benc_val_t * val, int64_t tag, void * arg );
|
|
|
|
@interface IPCClient : NSObject
|
|
{
|
|
NSFileHandle * _handle;
|
|
struct ipc_info * _ipc;
|
|
IPCController * _controller;
|
|
NSMutableData * _buf;
|
|
}
|
|
|
|
- (id) initClient: (IPCController *) controller
|
|
funcs: (struct ipc_funcs *) funcs
|
|
handle: (NSFileHandle *) handle;
|
|
- (IPCController *) controller;
|
|
- (struct ipc_info *) ipc;
|
|
- (void) gotdata: (NSNotification *) notification;
|
|
- (BOOL) sendresp: (uint8_t *) buf
|
|
size: (size_t) size;
|
|
- (BOOL) sendrespEmpty: (enum ipc_msg) msgid
|
|
tag: (int64_t) tag;
|
|
- (BOOL) sendrespInt: (enum ipc_msg) msgid
|
|
tag: (int64_t) tag
|
|
val: (int64_t) val;
|
|
- (BOOL) sendrespStr: (enum ipc_msg) msgid
|
|
tag: (int64_t) tag
|
|
val: (NSString *) val;
|
|
- (void) sendrespInfo: (enum ipc_msg) respid
|
|
tag: (int64_t) tag
|
|
torrents: (NSArray *) tors
|
|
types: (int) types;
|
|
|
|
@end
|
|
|
|
@interface IPCController (Private)
|
|
|
|
- (void) newclient: (NSNotification *) notification;
|
|
- (void) killclient: (IPCClient *) client;
|
|
NSUserDefaults * fDefaults;
|
|
PrefsController * fPrefsController;
|
|
|
|
|
|
@end
|
|
|
|
@implementation IPCController
|
|
|
|
- (id) init
|
|
{
|
|
struct sockaddr_un sun;
|
|
|
|
self = [super init];
|
|
if( nil == self )
|
|
return nil;
|
|
|
|
getaddr( &sun );
|
|
unlink( sun.sun_path );
|
|
_sock = [[NSSocketPort alloc]
|
|
initWithProtocolFamily: PF_UNIX
|
|
socketType: SOCK_STREAM
|
|
protocol: 0
|
|
address: [NSData dataWithBytes: &sun
|
|
length: sizeof(sun)]];
|
|
_listen = [[NSFileHandle alloc]
|
|
initWithFileDescriptor: [_sock socket]
|
|
closeOnDealloc: YES];
|
|
_funcs = ipc_initmsgs();
|
|
_clients = [[NSMutableArray alloc] init];
|
|
|
|
/* XXX is this error checking bogus? */
|
|
if( nil == _sock ||
|
|
nil == _listen ||
|
|
NULL == _funcs ||
|
|
nil == _clients ||
|
|
0 > ipc_addmsg( _funcs, IPC_MSG_ADDMANYFILES, msg_addold ) ||
|
|
0 > ipc_addmsg( _funcs, IPC_MSG_ADDONEFILE, msg_addnew ) ||
|
|
0 > ipc_addmsg( _funcs, IPC_MSG_AUTOMAP, msg_setbool ) ||
|
|
0 > ipc_addmsg( _funcs, IPC_MSG_AUTOSTART, msg_setbool ) ||
|
|
0 > ipc_addmsg( _funcs, IPC_MSG_CRYPTO, msg_setstr ) ||
|
|
0 > ipc_addmsg( _funcs, IPC_MSG_DIR, msg_setstr ) ||
|
|
0 > ipc_addmsg( _funcs, IPC_MSG_DOWNLIMIT, msg_setint ) ||
|
|
0 > ipc_addmsg( _funcs, IPC_MSG_GETAUTOMAP, msg_getbool ) ||
|
|
0 > ipc_addmsg( _funcs, IPC_MSG_GETAUTOSTART, msg_getbool ) ||
|
|
0 > ipc_addmsg( _funcs, IPC_MSG_GETCRYPTO, msg_getstr ) ||
|
|
0 > ipc_addmsg( _funcs, IPC_MSG_GETDIR, msg_getstr ) ||
|
|
0 > ipc_addmsg( _funcs, IPC_MSG_GETDOWNLIMIT, msg_getint ) ||
|
|
0 > ipc_addmsg( _funcs, IPC_MSG_GETINFO, msg_info ) ||
|
|
0 > ipc_addmsg( _funcs, IPC_MSG_GETINFOALL, msg_infoall ) ||
|
|
0 > ipc_addmsg( _funcs, IPC_MSG_GETPEX, msg_getbool ) ||
|
|
0 > ipc_addmsg( _funcs, IPC_MSG_GETPORT, msg_getint ) ||
|
|
0 > ipc_addmsg( _funcs, IPC_MSG_GETSTAT, msg_info ) ||
|
|
0 > ipc_addmsg( _funcs, IPC_MSG_GETSTATALL, msg_infoall ) ||
|
|
0 > ipc_addmsg( _funcs, IPC_MSG_GETUPLIMIT, msg_getint ) ||
|
|
0 > ipc_addmsg( _funcs, IPC_MSG_LOOKUP, msg_lookup ) ||
|
|
0 > ipc_addmsg( _funcs, IPC_MSG_NOOP, msg_empty ) ||
|
|
0 > ipc_addmsg( _funcs, IPC_MSG_PEX, msg_setbool ) ||
|
|
0 > ipc_addmsg( _funcs, IPC_MSG_PORT, msg_setint ) ||
|
|
0 > ipc_addmsg( _funcs, IPC_MSG_QUIT, msg_empty ) ||
|
|
0 > ipc_addmsg( _funcs, IPC_MSG_REMOVE, msg_action ) ||
|
|
0 > ipc_addmsg( _funcs, IPC_MSG_REMOVEALL, msg_actionall ) ||
|
|
0 > ipc_addmsg( _funcs, IPC_MSG_START, msg_action ) ||
|
|
0 > ipc_addmsg( _funcs, IPC_MSG_STARTALL, msg_actionall ) ||
|
|
0 > ipc_addmsg( _funcs, IPC_MSG_STOP, msg_action ) ||
|
|
0 > ipc_addmsg( _funcs, IPC_MSG_STOPALL, msg_actionall ) ||
|
|
0 > ipc_addmsg( _funcs, IPC_MSG_SUP, msg_sup ) ||
|
|
0 > ipc_addmsg( _funcs, IPC_MSG_UPLIMIT, msg_setint ) )
|
|
{
|
|
[self release];
|
|
return nil;
|
|
}
|
|
ipc_setdefmsg( _funcs, msg_default );
|
|
|
|
[[NSNotificationCenter defaultCenter]
|
|
addObserver: self
|
|
selector: @selector(newclient:)
|
|
name: NSFileHandleConnectionAcceptedNotification
|
|
object: _listen];
|
|
[_listen acceptConnectionInBackgroundAndNotify];
|
|
|
|
return self;
|
|
}
|
|
|
|
- (void) dealloc
|
|
{
|
|
struct sockaddr_un sun;
|
|
|
|
[[NSNotificationCenter defaultCenter] removeObserver: self];
|
|
[_listen release];
|
|
[_sock release];
|
|
[_clients release];
|
|
ipc_freemsgs( _funcs );
|
|
getaddr( &sun );
|
|
unlink( sun.sun_path );
|
|
[super dealloc];
|
|
}
|
|
|
|
- (id) delegate
|
|
{
|
|
return _delegate;
|
|
}
|
|
|
|
- (void) setDelegate: (id) newdelegate
|
|
{
|
|
_delegate = newdelegate;
|
|
}
|
|
|
|
- (void) setPrefsController: (id) thePrefsController
|
|
{
|
|
fPrefsController = thePrefsController;
|
|
}
|
|
|
|
@end
|
|
|
|
@implementation IPCController (Private)
|
|
|
|
- (void) newclient: (NSNotification *) notification
|
|
{
|
|
NSDictionary * info;
|
|
NSFileHandle * handle;
|
|
NSNumber * error;
|
|
IPCClient * client;
|
|
|
|
|
|
info = [notification userInfo];
|
|
handle = [info objectForKey: NSFileHandleNotificationFileHandleItem];
|
|
error = [info objectForKey: @"NSFileHandleError"];
|
|
|
|
if( nil != error )
|
|
{
|
|
NSLog( @"Failed to accept IPC socket connection: %@", error );
|
|
return;
|
|
}
|
|
|
|
[_listen acceptConnectionInBackgroundAndNotify];
|
|
|
|
if( nil == handle )
|
|
return;
|
|
|
|
client = [[IPCClient alloc]
|
|
initClient: self
|
|
funcs: _funcs
|
|
handle: handle];
|
|
if( nil == client )
|
|
return;
|
|
|
|
[_clients addObject:client];
|
|
[client release];
|
|
}
|
|
|
|
- (void) killclient: (IPCClient *) client
|
|
{
|
|
[_clients removeObject: client];
|
|
}
|
|
|
|
@end
|
|
|
|
@implementation IPCClient
|
|
|
|
- (id) initClient: (IPCController *) controller
|
|
funcs: (struct ipc_funcs *) funcs
|
|
handle: (NSFileHandle *) handle
|
|
{
|
|
uint8_t * buf;
|
|
size_t size;
|
|
|
|
self = [super init];
|
|
if( nil == self )
|
|
return nil;
|
|
|
|
_handle = [handle retain];
|
|
_ipc = ipc_newcon( funcs );
|
|
_controller = controller;
|
|
_buf = [[NSMutableData alloc] init];
|
|
|
|
buf = ipc_mkvers( &size, "Transmission Mac OS X " LONG_VERSION_STRING );
|
|
if( NULL == _ipc || nil == _buf || NULL == buf ||
|
|
![self sendresp: buf size: size] )
|
|
{
|
|
[self release];
|
|
return nil;
|
|
}
|
|
|
|
[[NSNotificationCenter defaultCenter]
|
|
addObserver: self
|
|
selector: @selector(gotdata:)
|
|
name: NSFileHandleReadCompletionNotification
|
|
object: _handle];
|
|
[_handle readInBackgroundAndNotify];
|
|
|
|
return self;
|
|
}
|
|
|
|
- (void) dealloc
|
|
{
|
|
[[NSNotificationCenter defaultCenter] removeObserver: self];
|
|
[_handle release];
|
|
[_buf release];
|
|
ipc_freecon( _ipc );
|
|
[super dealloc];
|
|
}
|
|
|
|
- (IPCController *) controller
|
|
{
|
|
return _controller;
|
|
}
|
|
|
|
- (struct ipc_info *) ipc
|
|
{
|
|
return _ipc;
|
|
}
|
|
|
|
- (void) gotdata: (NSNotification *) notification
|
|
{
|
|
NSDictionary * info;
|
|
NSData * data;
|
|
NSNumber * error;
|
|
ssize_t res;
|
|
|
|
info = [notification userInfo];
|
|
data = [info objectForKey: NSFileHandleNotificationDataItem];
|
|
error = [info objectForKey: @"NSFileHandleError"];
|
|
|
|
if( nil != error )
|
|
{
|
|
NSLog( @"Failed to read from IPC socket connection: %@", error );
|
|
[_controller killclient: self];
|
|
return;
|
|
}
|
|
|
|
if( nil == data || 0 == [data length] )
|
|
{
|
|
[_controller killclient: self];
|
|
return;
|
|
}
|
|
|
|
[_handle readInBackgroundAndNotify];
|
|
[_buf appendData: data];
|
|
|
|
if( IPC_MIN_MSG_LEN > [_buf length] )
|
|
return;
|
|
|
|
res = ipc_parse( _ipc, [_buf mutableBytes], [_buf length], self );
|
|
|
|
if( 0 > res )
|
|
{
|
|
switch( errno )
|
|
{
|
|
case EPERM:
|
|
NSLog( @"IPC client has unsupported protocol version" );
|
|
break;
|
|
case EINVAL:
|
|
NSLog( @"IPC protocol parse error" );
|
|
break;
|
|
default:
|
|
NSLog( @"IPC parsing failed" );
|
|
break;
|
|
}
|
|
[_controller killclient: self];
|
|
return;
|
|
}
|
|
else if( 0 < res )
|
|
{
|
|
assert( res <= [_buf length]);
|
|
if( res < [_buf length])
|
|
{
|
|
memmove( [_buf mutableBytes], [_buf bytes] + res,
|
|
[_buf length] - res );
|
|
}
|
|
[_buf setLength: ([_buf length] - res)];
|
|
}
|
|
}
|
|
|
|
- (BOOL) sendresp: (uint8_t *) buf
|
|
size: (size_t) size
|
|
{
|
|
@try
|
|
{
|
|
[_handle writeData: [NSData dataWithBytesNoCopy: buf
|
|
length: size
|
|
freeWhenDone: YES]];
|
|
return YES;
|
|
}
|
|
@catch( NSException * ex )
|
|
{
|
|
NSLog( @"Failed to write to IPC socket connection" );
|
|
return NO;
|
|
}
|
|
}
|
|
|
|
- (BOOL) sendrespEmpty: (enum ipc_msg) msgid
|
|
tag: (int64_t) tag
|
|
{
|
|
uint8_t * buf;
|
|
size_t size;
|
|
|
|
buf = ipc_mkempty( _ipc, &size, msgid, tag );
|
|
if( NULL == buf )
|
|
return NO;
|
|
|
|
return [self sendresp: buf
|
|
size: size];
|
|
}
|
|
|
|
- (BOOL) sendrespInt: (enum ipc_msg) msgid
|
|
tag: (int64_t) tag
|
|
val: (int64_t) val
|
|
{
|
|
uint8_t * buf;
|
|
size_t size;
|
|
|
|
buf = ipc_mkint( _ipc, &size, msgid, tag, val );
|
|
if( NULL == buf )
|
|
return NO;
|
|
|
|
return [self sendresp: buf
|
|
size: size];
|
|
}
|
|
|
|
- (BOOL) sendrespStr: (enum ipc_msg) msgid
|
|
tag: (int64_t) tag
|
|
val: (NSString *) val
|
|
{
|
|
uint8_t * buf;
|
|
size_t size;
|
|
NSData * data;
|
|
NSMutableData * sucky;
|
|
|
|
if( [val canBeConvertedToEncoding: NSUTF8StringEncoding] )
|
|
buf = ipc_mkstr( _ipc, &size, msgid, tag,
|
|
[val cStringUsingEncoding: NSUTF8StringEncoding] );
|
|
else
|
|
{
|
|
data = [val dataUsingEncoding: NSUTF8StringEncoding
|
|
allowLossyConversion: YES];
|
|
/* XXX this sucks, I should add a length argument to ipc_mkstr() */
|
|
sucky = [NSMutableData dataWithData: data];
|
|
[sucky appendBytes: "" length: 1];
|
|
buf = ipc_mkstr( _ipc, &size, msgid, tag, [sucky bytes] );
|
|
}
|
|
if( NULL == buf )
|
|
return NO;
|
|
|
|
return [self sendresp: buf
|
|
size: size];
|
|
}
|
|
|
|
- (void) sendrespInfo: (enum ipc_msg) respid
|
|
tag: (int64_t) tag
|
|
torrents: (NSArray *) tors
|
|
types: (int) types
|
|
{
|
|
benc_val_t packet, * pkinf;
|
|
NSEnumerator * enumerator;
|
|
Torrent * tor;
|
|
uint8_t * buf;
|
|
size_t size;
|
|
int res;
|
|
|
|
pkinf = ipc_initval( _ipc, respid, tag, &packet, TYPE_LIST );
|
|
if( NULL == pkinf )
|
|
goto fail;
|
|
if( tr_bencListReserve( pkinf, [tors count] ) )
|
|
{
|
|
tr_bencFree( &packet );
|
|
goto fail;
|
|
}
|
|
|
|
enumerator = [tors objectEnumerator];
|
|
while( nil != ( tor = [enumerator nextObject] ) )
|
|
{
|
|
if( IPC_MSG_INFO == respid )
|
|
res = ipc_addinfo( pkinf, [tor torrentID], [tor torrentInfo], types );
|
|
else
|
|
res = ipc_addstat( pkinf, [tor torrentID], [tor torrentStat], types );
|
|
if( 0 > res )
|
|
{
|
|
tr_bencFree( &packet );
|
|
goto fail;
|
|
}
|
|
}
|
|
|
|
buf = ipc_mkval( &packet, &size );
|
|
tr_bencFree( &packet );
|
|
if( NULL == buf )
|
|
goto fail;
|
|
[self sendresp: buf size: size ];
|
|
return;
|
|
|
|
fail:
|
|
NSLog( @"Failed to create IPC reply packet" );
|
|
[_controller killclient: self];
|
|
}
|
|
|
|
|
|
@end
|
|
|
|
void getaddr( struct sockaddr_un * sun )
|
|
{
|
|
bzero( sun, sizeof *sun );
|
|
sun->sun_family = AF_LOCAL;
|
|
strlcpy( sun->sun_path, tr_getPrefsDirectory(), sizeof sun->sun_path );
|
|
strlcat( sun->sun_path, "/socket", sizeof sun->sun_path );
|
|
}
|
|
|
|
NSArray * bencarray( benc_val_t * val, int type )
|
|
{
|
|
int ii;
|
|
NSMutableArray * ret;
|
|
benc_val_t * item;
|
|
|
|
assert( TYPE_STR == type || TYPE_INT == type );
|
|
|
|
if( NULL == val || TYPE_LIST != val->type )
|
|
return nil;
|
|
|
|
ret = [NSMutableArray arrayWithCapacity: val->val.l.count];
|
|
for( ii = 0; ii < val->val.l.count; ii++ )
|
|
{
|
|
item = &val->val.l.vals[ii];
|
|
if( type != item->type )
|
|
return nil;
|
|
if( TYPE_STR == type )
|
|
{
|
|
[ret addObject: [NSString stringWithCString: item->val.s.s
|
|
encoding: NSUTF8StringEncoding]];
|
|
}
|
|
else
|
|
{
|
|
[ret addObject: [NSNumber numberWithLongLong: (long long) item->val.i]];
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
void msg_lookup( enum ipc_msg msgid, benc_val_t * val, int64_t tag, void * arg )
|
|
{
|
|
IPCClient * client = arg;
|
|
NSArray * hashes, * tors;
|
|
benc_val_t packet, * pkinf;
|
|
NSEnumerator * enumerator;
|
|
Torrent * tor;
|
|
uint8_t * buf;
|
|
size_t size;
|
|
|
|
hashes = bencarray( val, TYPE_STR );
|
|
if( NULL == hashes )
|
|
{
|
|
[client sendrespEmpty: IPC_MSG_BAD tag: tag];
|
|
return;
|
|
}
|
|
|
|
tors = [[[client controller] delegate] ipcGetTorrentsByHash: hashes];
|
|
[client sendrespInfo: IPC_MSG_INFO
|
|
tag: tag
|
|
torrents: tors
|
|
types: IPC_INF_HASH];
|
|
}
|
|
|
|
void msg_info( enum ipc_msg msgid, benc_val_t * val, int64_t tag, void * arg )
|
|
{
|
|
IPCClient * client = arg;
|
|
enum ipc_msg respid;
|
|
benc_val_t * typesval;
|
|
int types;
|
|
NSArray * ids, * tors;
|
|
|
|
if( NULL == val || TYPE_DICT != val->type )
|
|
goto bad;
|
|
|
|
typesval = tr_bencDictFind( val, "type" );
|
|
if( NULL == typesval || TYPE_LIST != typesval->type ||
|
|
nil == ( ids = bencarray( tr_bencDictFind( val, "id" ), TYPE_INT ) ) )
|
|
goto bad;
|
|
|
|
respid = ( IPC_MSG_GETINFO == msgid ? IPC_MSG_INFO : IPC_MSG_STAT );
|
|
tors = [[[client controller] delegate] ipcGetTorrentsByID: ids];
|
|
types = ipc_infotypes( respid, typesval );
|
|
[client sendrespInfo: respid tag: tag torrents: tors types: types];
|
|
return;
|
|
|
|
bad:
|
|
NSLog( @"Got bad IPC packet" );
|
|
[client sendrespEmpty: IPC_MSG_BAD tag: tag];
|
|
}
|
|
|
|
void msg_infoall( enum ipc_msg msgid, benc_val_t * val, int64_t tag, void * arg )
|
|
{
|
|
IPCClient * client = arg;
|
|
enum ipc_msg respid;
|
|
int types;
|
|
NSArray * tors;
|
|
|
|
if( NULL == val || TYPE_LIST != val->type )
|
|
{
|
|
NSLog( @"Got bad IPC packet" );
|
|
[client sendrespEmpty: IPC_MSG_BAD tag: tag];
|
|
return;
|
|
}
|
|
|
|
respid = ( IPC_MSG_GETINFOALL == msgid ? IPC_MSG_INFO : IPC_MSG_STAT );
|
|
tors = [[[client controller] delegate] ipcGetTorrentsByID: nil];
|
|
types = ipc_infotypes( respid, val );
|
|
[client sendrespInfo: respid tag: tag torrents: tors types: types];
|
|
}
|
|
|
|
void msg_action( enum ipc_msg msgid, benc_val_t * val, int64_t tag, void * arg )
|
|
{
|
|
IPCClient * client = arg;
|
|
BOOL res;
|
|
NSArray * ids, * tors;
|
|
id delegate;
|
|
|
|
ids = bencarray( val, TYPE_INT );
|
|
if( nil == ids )
|
|
{
|
|
NSLog( @"Got bad IPC packet" );
|
|
[client sendrespEmpty: IPC_MSG_BAD tag: tag];
|
|
return;
|
|
}
|
|
|
|
delegate = [[client controller] delegate];
|
|
tors = [delegate ipcGetTorrentsByID: ids];
|
|
switch( msgid )
|
|
{
|
|
case IPC_MSG_REMOVE:
|
|
res = [delegate ipcRemoveTorrents: tors];
|
|
break;
|
|
case IPC_MSG_START:
|
|
res = [delegate ipcStartTorrents: tors];
|
|
break;
|
|
case IPC_MSG_STOP:
|
|
res = [delegate ipcStopTorrents: tors];
|
|
break;
|
|
default:
|
|
assert( 0 );
|
|
return;
|
|
}
|
|
|
|
if( res )
|
|
[client sendrespEmpty: IPC_MSG_OK tag: tag];
|
|
else
|
|
[client sendrespEmpty: IPC_MSG_FAIL tag: tag];
|
|
}
|
|
|
|
void msg_actionall( enum ipc_msg msgid, benc_val_t * val, int64_t tag, void * arg )
|
|
{
|
|
IPCClient * client = arg;
|
|
BOOL res;
|
|
NSArray * tors;
|
|
id delegate;
|
|
|
|
delegate = [[client controller] delegate];
|
|
tors = [delegate ipcGetTorrentsByID: nil];
|
|
switch( msgid )
|
|
{
|
|
case IPC_MSG_REMOVEALL:
|
|
res = [delegate ipcRemoveTorrents: tors];
|
|
break;
|
|
case IPC_MSG_STARTALL:
|
|
res = [delegate ipcStartTorrents: tors];
|
|
break;
|
|
case IPC_MSG_STOPALL:
|
|
res = [delegate ipcStopTorrents: tors];
|
|
break;
|
|
default:
|
|
assert( 0 );
|
|
return;
|
|
}
|
|
|
|
if( res )
|
|
[client sendrespEmpty: IPC_MSG_OK tag: tag];
|
|
else
|
|
[client sendrespEmpty: IPC_MSG_FAIL tag: tag];
|
|
}
|
|
|
|
void msg_addold( enum ipc_msg msgid, benc_val_t * val, int64_t tag, void * arg )
|
|
{
|
|
IPCClient * client = arg;
|
|
NSArray * paths;
|
|
BOOL res;
|
|
|
|
paths = bencarray( val, TYPE_STR );
|
|
if( nil == paths )
|
|
{
|
|
NSLog( @"Got bad IPC packet" );
|
|
[client sendrespEmpty: IPC_MSG_BAD tag: tag];
|
|
return;
|
|
}
|
|
|
|
/* XXX should send back info message with torrent IDs */
|
|
if( [[[client controller] delegate] ipcAddTorrents: paths] )
|
|
[client sendrespEmpty: IPC_MSG_OK tag: tag];
|
|
else
|
|
[client sendrespEmpty: IPC_MSG_FAIL tag: tag];
|
|
}
|
|
|
|
void msg_addnew( enum ipc_msg msgid, benc_val_t * val, int64_t tag, void * arg )
|
|
{
|
|
IPCClient * client = arg;
|
|
benc_val_t * fileval, * dataval, * dirval, * autoval;
|
|
NSString * file, * dir;
|
|
NSData * data;
|
|
BOOL autobool, res;
|
|
|
|
if( NULL == val || TYPE_DICT != val->type )
|
|
goto bad;
|
|
|
|
fileval = tr_bencDictFind( val, "file" );
|
|
dataval = tr_bencDictFind( val, "data" );
|
|
dirval = tr_bencDictFind( val, "directory" );
|
|
autoval = tr_bencDictFind( val, "autostart" );
|
|
if( ( ( NULL == fileval || TYPE_STR != fileval->type ) &&
|
|
( NULL == dataval || TYPE_STR != dataval->type ) ) ||
|
|
( ( NULL != fileval && TYPE_STR == fileval->type ) &&
|
|
( NULL != dataval && TYPE_STR == dataval->type ) ) ||
|
|
( NULL != dirval && TYPE_STR != dirval->type ) ||
|
|
( NULL != autoval && TYPE_INT != autoval->type ) )
|
|
goto bad;
|
|
|
|
dir = ( NULL == dirval ? nil :
|
|
[NSString stringWithCString: dirval->val.s.s
|
|
encoding: NSUTF8StringEncoding] );
|
|
autobool = ( NULL == autoval || 0 == autoval->val.i ? NO : YES );
|
|
|
|
if( NULL != fileval )
|
|
{
|
|
file = [NSString stringWithCString: fileval->val.s.s
|
|
encoding: NSUTF8StringEncoding];
|
|
if( NULL == autoval )
|
|
res = [[[client controller] delegate]
|
|
ipcAddTorrentFile: file
|
|
directory: dir];
|
|
else
|
|
res = [[[client controller] delegate]
|
|
ipcAddTorrentFileAutostart: file
|
|
directory: dir
|
|
autostart: autobool];
|
|
}
|
|
else
|
|
{
|
|
data = [NSData dataWithBytes: dataval->val.s.s
|
|
length: dataval->val.s.i];
|
|
if( NULL == autoval )
|
|
res = [[[client controller] delegate]
|
|
ipcAddTorrentData: data
|
|
directory: dir];
|
|
else
|
|
res = [[[client controller] delegate]
|
|
ipcAddTorrentDataAutostart: data
|
|
directory: dir
|
|
autostart: autobool];
|
|
}
|
|
|
|
/* XXX should send back info message with torrent ID */
|
|
if( res )
|
|
[client sendrespEmpty: IPC_MSG_OK tag: tag];
|
|
else
|
|
[client sendrespEmpty: IPC_MSG_FAIL tag: tag];
|
|
return;
|
|
|
|
bad:
|
|
NSLog( @"Got bad IPC packet" );
|
|
[client sendrespEmpty: IPC_MSG_BAD tag: tag];
|
|
}
|
|
|
|
void msg_getbool( enum ipc_msg msgid, benc_val_t * val, int64_t tag, void * arg )
|
|
{
|
|
IPCClient * client = arg;
|
|
|
|
fDefaults = [NSUserDefaults standardUserDefaults];
|
|
|
|
switch( msgid )
|
|
{
|
|
case IPC_MSG_GETAUTOMAP:
|
|
[client sendrespInt:IPC_MSG_AUTOMAP tag:tag val:[fDefaults boolForKey:@"NatTraversal"]];
|
|
break;
|
|
case IPC_MSG_GETAUTOSTART:
|
|
[client sendrespInt:IPC_MSG_AUTOSTART tag:tag val:[fDefaults boolForKey:@"AutoStartDownload"]];
|
|
break;
|
|
case IPC_MSG_GETPEX:
|
|
//warning we dont support this :(
|
|
[client sendrespEmpty: IPC_MSG_FAIL tag: tag];
|
|
break;
|
|
default:
|
|
assert( 0 );
|
|
break;
|
|
}
|
|
}
|
|
|
|
void msg_getint( enum ipc_msg msgid, benc_val_t * val, int64_t tag, void * arg )
|
|
{
|
|
IPCClient * client = arg;
|
|
|
|
fDefaults = [NSUserDefaults standardUserDefaults];
|
|
|
|
int theValue;
|
|
|
|
switch( msgid )
|
|
{
|
|
case IPC_MSG_GETDOWNLIMIT:
|
|
if ( [fDefaults boolForKey:@"CheckDownload"] )
|
|
theValue = [fDefaults integerForKey:@"DownloadLimit"];
|
|
else
|
|
theValue = -1;
|
|
[client sendrespInt:IPC_MSG_DOWNLIMIT tag:tag val:theValue];
|
|
break;
|
|
case IPC_MSG_GETPORT:
|
|
[client sendrespInt:IPC_MSG_PORT tag:tag val:[fDefaults integerForKey:@"BindPort"]];
|
|
break;
|
|
case IPC_MSG_GETUPLIMIT:
|
|
if ( [fDefaults boolForKey:@"CheckUpload"] )
|
|
theValue = [fDefaults integerForKey:@"UploadLimit"];
|
|
else
|
|
theValue = -1;
|
|
[client sendrespInt:IPC_MSG_UPLIMIT tag:tag val:theValue];
|
|
break;
|
|
default:
|
|
assert( 0 );
|
|
break;
|
|
}
|
|
}
|
|
|
|
void msg_getstr( enum ipc_msg msgid, benc_val_t * val, int64_t tag, void * arg )
|
|
{
|
|
IPCClient * client = arg;
|
|
NSString * cryptoValue;
|
|
|
|
fDefaults = [NSUserDefaults standardUserDefaults];
|
|
|
|
switch( msgid )
|
|
{
|
|
|
|
case IPC_MSG_GETDIR:
|
|
[client sendrespStr:IPC_MSG_DIR tag:tag val:[fDefaults stringForKey:@"DownloadFolder"]];
|
|
break;
|
|
|
|
case IPC_MSG_GETCRYPTO:
|
|
if ([fDefaults boolForKey: @"EncryptionPrefer"])
|
|
if ([fDefaults boolForKey: @"EncryptionRequire"])
|
|
cryptoValue = @"required";
|
|
else
|
|
cryptoValue = @"preferred";
|
|
else
|
|
cryptoValue = @"plaintext";
|
|
|
|
[client sendrespStr:IPC_MSG_CRYPTO tag:tag val:cryptoValue];
|
|
break;
|
|
|
|
default:
|
|
assert( 0 );
|
|
break;
|
|
}
|
|
}
|
|
|
|
void msg_setbool( enum ipc_msg msgid, benc_val_t * val, int64_t tag, void * arg )
|
|
{
|
|
IPCClient * client = arg;
|
|
|
|
if( NULL == val || TYPE_INT != val->type )
|
|
{
|
|
NSLog( @"Got bad IPC packet" );
|
|
[client sendrespEmpty: IPC_MSG_BAD tag: tag];
|
|
return;
|
|
}
|
|
|
|
fDefaults = [NSUserDefaults standardUserDefaults];
|
|
|
|
switch( msgid )
|
|
{
|
|
case IPC_MSG_AUTOMAP:
|
|
[fDefaults setBool:(bool)val->val.i forKey:@"NatTraversal"];
|
|
[client sendrespEmpty:IPC_MSG_OK tag:tag];
|
|
break;
|
|
case IPC_MSG_AUTOSTART:
|
|
[fDefaults setBool:(bool)val->val.i forKey:@"AutoStartDownload"];
|
|
[client sendrespEmpty:IPC_MSG_OK tag:tag];
|
|
break;
|
|
case IPC_MSG_PEX:
|
|
//warning we dont support this :(
|
|
[client sendrespEmpty: IPC_MSG_FAIL tag: tag];
|
|
break;
|
|
default:
|
|
assert( 0 );
|
|
break;
|
|
}
|
|
}
|
|
|
|
void msg_setint( enum ipc_msg msgid, benc_val_t * val, int64_t tag, void * arg )
|
|
{
|
|
IPCClient * client = arg;
|
|
|
|
if( NULL == val || TYPE_INT != val->type )
|
|
{
|
|
NSLog( @"Got bad IPC packet" );
|
|
[client sendrespEmpty: IPC_MSG_BAD tag: tag];
|
|
return;
|
|
}
|
|
|
|
fDefaults = [NSUserDefaults standardUserDefaults];
|
|
switch( msgid )
|
|
{
|
|
case IPC_MSG_DOWNLIMIT:
|
|
if ( val->val.i < 0 )
|
|
[fDefaults setBool:NO forKey:@"CheckDownload"];
|
|
else
|
|
{
|
|
[fDefaults setBool:YES forKey:@"CheckDownload"];
|
|
[fDefaults setInteger:val->val.i forKey:@"DownloadLimit"];
|
|
}
|
|
[fPrefsController updateLimitFields];
|
|
[fPrefsController applySpeedSettings: nil];
|
|
break;
|
|
case IPC_MSG_PORT:
|
|
[fPrefsController setPort:[NSNumber numberWithInt:val->val.i]];
|
|
[fPrefsController updatePortField];
|
|
break;
|
|
case IPC_MSG_UPLIMIT:
|
|
if ( val->val.i < 0 )
|
|
[fDefaults setBool:NO forKey:@"CheckUpload"];
|
|
else
|
|
{
|
|
[fDefaults setBool:YES forKey:@"CheckUpload"];
|
|
[fDefaults setInteger:val->val.i forKey:@"UploadLimit"];
|
|
}
|
|
[fPrefsController updateLimitFields];
|
|
[fPrefsController applySpeedSettings: nil];
|
|
break;
|
|
default:
|
|
assert( 0 );
|
|
break;
|
|
}
|
|
[client sendrespEmpty:IPC_MSG_OK tag:tag];
|
|
}
|
|
|
|
void msg_setstr( enum ipc_msg msgid, benc_val_t * val, int64_t tag, void * arg )
|
|
{
|
|
IPCClient * client = arg;
|
|
|
|
if( NULL == val || TYPE_STR != val->type )
|
|
{
|
|
NSLog( @"Got bad IPC packet" );
|
|
[client sendrespEmpty: IPC_MSG_BAD tag: tag];
|
|
return;
|
|
}
|
|
|
|
fDefaults = [NSUserDefaults standardUserDefaults];
|
|
|
|
switch( msgid )
|
|
{
|
|
case IPC_MSG_DIR:
|
|
[fDefaults setObject:[NSString stringWithCString: val->val.s.s] forKey:@"DownloadFolder"];
|
|
[fDefaults setObject: @"Constant" forKey: @"DownloadChoice"]; //not sure about this line
|
|
[client sendrespEmpty: IPC_MSG_OK tag: tag];
|
|
break;
|
|
|
|
case IPC_MSG_CRYPTO:
|
|
if(!strcasecmp(val->val.s.s, "required"))
|
|
{
|
|
[fDefaults setBool:YES forKey: @"EncryptionPrefer"];
|
|
[fDefaults setBool:YES forKey: @"EncryptionRequire"];
|
|
}
|
|
|
|
else if(!strcasecmp(val->val.s.s, "preferred"))
|
|
{
|
|
[fDefaults setBool:YES forKey: @"EncryptionPrefer"];
|
|
[fDefaults setBool:NO forKey: @"EncryptionRequire"];
|
|
}
|
|
|
|
else if(!strcasecmp(val->val.s.s, "plaintext"))
|
|
{
|
|
[fDefaults setBool:NO forKey: @"EncryptionPrefer"];
|
|
[fDefaults setBool:NO forKey: @"EncryptionRequire"];
|
|
}
|
|
|
|
[client sendrespEmpty: IPC_MSG_OK tag: tag];
|
|
break;
|
|
|
|
default:
|
|
assert( 0 );
|
|
break;
|
|
}
|
|
}
|
|
|
|
void msg_empty( enum ipc_msg msgid, benc_val_t * val, int64_t tag, void * arg )
|
|
{
|
|
IPCClient * client = arg;
|
|
|
|
switch( msgid )
|
|
{
|
|
case IPC_MSG_NOOP:
|
|
[client sendrespEmpty: IPC_MSG_OK tag: tag];
|
|
break;
|
|
case IPC_MSG_QUIT:
|
|
[[[client controller] delegate] ipcQuit];
|
|
[client sendrespEmpty: IPC_MSG_OK tag: tag];
|
|
break;
|
|
default:
|
|
assert( 0 );
|
|
break;
|
|
}
|
|
}
|
|
|
|
void msg_sup( enum ipc_msg msgid, benc_val_t * val, int64_t tag, void * arg )
|
|
{
|
|
IPCClient * client = arg;
|
|
benc_val_t packet, * pkval, * name;
|
|
struct ipc_info * ipc;
|
|
int ii;
|
|
enum ipc_msg found;
|
|
uint8_t * buf;
|
|
size_t size;
|
|
|
|
if( NULL == val || TYPE_LIST != val->type )
|
|
goto bad;
|
|
|
|
ipc = [client ipc];
|
|
pkval = ipc_initval( ipc, IPC_MSG_SUP, tag, &packet, TYPE_LIST );
|
|
if( NULL == pkval )
|
|
goto fail;
|
|
if( tr_bencListReserve( pkval, val->val.l.count ) )
|
|
{
|
|
tr_bencFree( &packet );
|
|
goto fail;
|
|
}
|
|
|
|
for( ii = 0; val->val.l.count > ii; ii++ )
|
|
{
|
|
name = &val->val.l.vals[ii];
|
|
if( NULL == name || TYPE_STR != name->type )
|
|
goto bad;
|
|
found = ipc_msgid( ipc, name->val.s.s );
|
|
if( IPC__MSG_COUNT == found || !ipc_ishandled( ipc, found ) )
|
|
{
|
|
continue;
|
|
}
|
|
tr_bencInitStr( tr_bencListAdd( pkval ),
|
|
name->val.s.s, name->val.s.i, 1 );
|
|
}
|
|
|
|
buf = ipc_mkval( &packet, &size );
|
|
tr_bencFree( &packet );
|
|
if( NULL == buf )
|
|
goto fail;
|
|
[client sendresp: buf size: size ];
|
|
return;
|
|
|
|
bad:
|
|
NSLog( @"Got bad IPC packet" );
|
|
[client sendrespEmpty: IPC_MSG_BAD tag: tag];
|
|
return;
|
|
|
|
fail:
|
|
NSLog( @"Failed to create IPC reply packet" );
|
|
[[client controller] killclient: client];
|
|
}
|
|
|
|
void msg_default( enum ipc_msg msgid, benc_val_t * val, int64_t tag, void * arg )
|
|
{
|
|
IPCClient * client = arg;
|
|
|
|
[client sendrespEmpty: IPC_MSG_NOTSUP tag: tag];
|
|
}
|