From 2c79888a935b9f2bb28ead4ee8329e59d360a066 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Sun, 18 May 2008 16:44:30 +0000 Subject: [PATCH] RPC/IPC redesign --- beos/TRWindow.cpp | 6 +- cli/Makefile.am | 1 + cli/transmissioncli.c | 58 +- configure.ac | 2 +- daemon/Makefile.am | 30 +- daemon/bsdqueue.h | 528 ---------- daemon/bsdtree.h | 701 ------------- daemon/client.c | 1072 ------------------- daemon/client.h | 77 -- daemon/daemon.c | 518 +++++----- daemon/errors.c | 112 -- daemon/errors.h | 35 - daemon/misc.c | 188 ---- daemon/misc.h | 148 --- daemon/proxy.c | 359 ------- daemon/remote.c | 1260 +++++------------------ daemon/server.c | 1006 ------------------ daemon/server.h | 35 - daemon/torrents.c | 746 -------------- daemon/torrents.h | 65 -- daemon/transmission-daemon.1 | 9 +- daemon/transmission-proxy.1 | 66 -- daemon/transmission-remote.1 | 11 +- doc/ipcproto.txt | 619 ----------- doc/{rpc-json-spec.txt => rpc-spec.txt} | 58 +- gtk/Makefile.am | 3 +- gtk/add-dialog.c | 24 +- gtk/details.c | 18 +- gtk/ipc.c | 1180 --------------------- gtk/ipc.h | 41 - gtk/main.c | 178 ++-- gtk/notify.c | 2 +- gtk/tr-core-dbus.h | 122 +++ gtk/tr-core-dbus.xml | 10 + gtk/tr-core.c | 152 ++- gtk/tr-core.h | 10 +- gtk/tr-prefs.c | 19 +- gtk/tr-prefs.h | 5 +- gtk/tr-torrent.c | 6 +- gtk/util.c | 44 +- gtk/util.h | 9 +- libtransmission/Makefile.am | 6 +- libtransmission/bencode-test.c | 2 + libtransmission/bencode.c | 8 +- libtransmission/bencode.h | 5 - libtransmission/blocklist.c | 2 +- libtransmission/clients.c | 3 +- libtransmission/fastresume.c | 24 +- libtransmission/fdlimit.c | 3 +- libtransmission/inout.c | 6 +- libtransmission/ipcparse.c | 1101 -------------------- libtransmission/ipcparse.h | 199 ---- libtransmission/json-test.c | 3 +- libtransmission/json.c | 3 +- libtransmission/json.h | 2 +- libtransmission/makemeta.c | 9 +- libtransmission/metainfo.c | 1 - libtransmission/peer-mgr.c | 5 +- libtransmission/peer-msgs.c | 4 +- libtransmission/platform.c | 5 +- libtransmission/resume.c | 48 +- libtransmission/resume.h | 22 +- libtransmission/rpc-server.c | 228 ++++ libtransmission/rpc-server.h | 40 + libtransmission/rpc.c | 393 ++++--- libtransmission/rpc.h | 14 +- libtransmission/session.c | 155 ++- libtransmission/session.h | 20 +- libtransmission/stats.c | 6 +- libtransmission/torrent-ctor.c | 22 +- libtransmission/torrent.c | 53 +- libtransmission/torrent.h | 4 +- libtransmission/tracker.c | 9 +- libtransmission/transmission.h | 140 ++- libtransmission/trcompat.h | 34 - libtransmission/utils.c | 58 +- libtransmission/utils.h | 11 + libtransmission/verify.c | 2 +- po/POTFILES.in | 1 - third-party/shttpd/config.h | 2 +- wx/xmission.cc | 4 +- 81 files changed, 1918 insertions(+), 10272 deletions(-) delete mode 100644 daemon/bsdqueue.h delete mode 100644 daemon/bsdtree.h delete mode 100644 daemon/client.c delete mode 100644 daemon/client.h delete mode 100644 daemon/errors.c delete mode 100644 daemon/errors.h delete mode 100644 daemon/misc.c delete mode 100644 daemon/misc.h delete mode 100644 daemon/proxy.c delete mode 100644 daemon/server.c delete mode 100644 daemon/server.h delete mode 100644 daemon/torrents.c delete mode 100644 daemon/torrents.h delete mode 100644 daemon/transmission-proxy.1 delete mode 100644 doc/ipcproto.txt rename doc/{rpc-json-spec.txt => rpc-spec.txt} (82%) delete mode 100644 gtk/ipc.c delete mode 100644 gtk/ipc.h create mode 100644 gtk/tr-core-dbus.h create mode 100644 gtk/tr-core-dbus.xml delete mode 100644 libtransmission/ipcparse.c delete mode 100644 libtransmission/ipcparse.h create mode 100644 libtransmission/rpc-server.c create mode 100644 libtransmission/rpc-server.h delete mode 100644 libtransmission/trcompat.h diff --git a/beos/TRWindow.cpp b/beos/TRWindow.cpp index 275afc166..272f517dc 100644 --- a/beos/TRWindow.cpp +++ b/beos/TRWindow.cpp @@ -20,7 +20,7 @@ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * - * $Id:$ + * $Id$ */ #include "TRWindow.h" @@ -109,7 +109,7 @@ TRWindow::TRWindow() : BWindow(BRect(10, 40, 350, 110), "Transmission", B_TITLED delete rectFrame; // Bring up the Transmission Engine - engine = tr_init( "beos" ); + engine = tr_sessionInit( "beos" ); LoadSettings(); UpdateList(-1, true); @@ -132,7 +132,7 @@ TRWindow::~TRWindow() { snooze(100000); } /* XXX there's no way to make sure the torrent threads are running so this might crash */ - tr_close(engine); + tr_sessionClose(engine); stop_watching(this); delete quitter; } diff --git a/cli/Makefile.am b/cli/Makefile.am index 9749c9503..ca8b21f19 100644 --- a/cli/Makefile.am +++ b/cli/Makefile.am @@ -12,6 +12,7 @@ transmissioncli_LDADD = \ $(top_builddir)/third-party/libevent/libevent_core.la \ $(top_builddir)/third-party/libnatpmp/libnatpmp.a \ $(top_builddir)/third-party/miniupnp/libminiupnp.a \ + $(top_builddir)/third-party/shttpd/libshttpd.a \ $(INTLLIBS) \ $(OPENSSL_LIBS) \ $(LIBCURL_LIBS) \ diff --git a/cli/transmissioncli.c b/cli/transmissioncli.c index 7fddf38b0..a5034700e 100644 --- a/cli/transmissioncli.c +++ b/cli/transmissioncli.c @@ -68,15 +68,15 @@ static int showScrape = 0; static int showVersion = 0; static int isPrivate = 0; static int verboseLevel = 0; -static int bindPort = TR_DEFAULT_PORT; +static int peerPort = TR_DEFAULT_PORT; static int uploadLimit = 20; static int downloadLimit = -1; static char * torrentPath = NULL; -static char * savePath = "."; static int natTraversal = 0; static int recheckData = 0; static sig_atomic_t gotsig = 0; static sig_atomic_t manualUpdate = 0; +static char downloadDir[MAX_PATH_LENGTH] = { '\0' }; static char * finishCall = NULL; static char * announce = NULL; @@ -135,36 +135,41 @@ main( int argc, char ** argv ) return EXIT_SUCCESS; } - if( bindPort < 1 || bindPort > 65535 ) + if( peerPort < 1 || peerPort > 65535 ) { - printf( "Invalid port '%d'\n", bindPort ); + printf( "Invalid port '%d'\n", peerPort ); return EXIT_FAILURE; } /* don't bind the port if we're just running the CLI * to get metainfo or to create a torrent */ if( showInfo || showScrape || ( sourceFile != NULL ) ) - bindPort = -1; + peerPort = -1; if( configdir == NULL ) configdir = strdup( tr_getDefaultConfigDir( ) ); /* Initialize libtransmission */ - h = tr_initFull( configdir, - "cli", /* tag */ - 1, /* pex enabled */ - natTraversal, /* nat enabled */ - bindPort, /* public port */ - TR_ENCRYPTION_PREFERRED, /* encryption mode */ - uploadLimit >= 0, /* use upload speed limit? */ - uploadLimit, /* upload speed limit */ - downloadLimit >= 0, /* use download speed limit? */ - downloadLimit, /* download speed limit */ - TR_DEFAULT_GLOBAL_PEER_LIMIT, - verboseLevel + 1, /* messageLevel */ - 0, /* is message queueing enabled? */ - 0, /* use the blocklist? */ - TR_DEFAULT_PEER_SOCKET_TOS ); + h = tr_sessionInitFull( + configdir, + "cli", /* tag */ + downloadDir, /* where to download torrents */ + TR_DEFAULT_PEX_ENABLED, + natTraversal, /* nat enabled */ + peerPort, + TR_ENCRYPTION_PREFERRED, + uploadLimit >= 0, + uploadLimit, + downloadLimit >= 0, + downloadLimit, + TR_DEFAULT_GLOBAL_PEER_LIMIT, + verboseLevel + 1, /* messageLevel */ + 0, /* is message queueing enabled? */ + TR_DEFAULT_BLOCKLIST_ENABLED, + TR_DEFAULT_PEER_SOCKET_TOS, + TR_DEFAULT_RPC_ENABLED, + TR_DEFAULT_RPC_PORT, + TR_DEFAULT_RPC_ACL ); if( sourceFile && *sourceFile ) /* creating a torrent */ { @@ -183,7 +188,7 @@ main( int argc, char ** argv ) ctor = tr_ctorNew( h ); tr_ctorSetMetainfoFromFile( ctor, torrentPath ); tr_ctorSetPaused( ctor, TR_FORCE, 0 ); - tr_ctorSetDestination( ctor, TR_FORCE, savePath ); + tr_ctorSetDownloadDir( ctor, TR_FORCE, downloadDir ); if( showInfo ) { @@ -237,7 +242,7 @@ main( int argc, char ** argv ) if( tor == NULL ) { printf( "Failed opening torrent file `%s'\n", torrentPath ); - tr_close( h ); + tr_sessionClose( h ); return EXIT_FAILURE; } @@ -369,7 +374,7 @@ main( int argc, char ** argv ) cleanup: tr_torrentClose( tor ); - tr_close( h ); + tr_sessionClose( h ); return EXIT_SUCCESS; } @@ -417,8 +422,8 @@ parseCommandLine( int argc, char ** argv ) case 'i': showInfo = 1; break; case 'm': comment = optarg; break; case 'n': natTraversal = 1; break; - case 'o': savePath = optarg; - case 'p': bindPort = atoi( optarg ); break; + case 'o': tr_strlcpy( downloadDir, optarg, sizeof( downloadDir ) ); break; + case 'p': peerPort = atoi( optarg ); break; case 'r': isPrivate = 1; break; case 's': showScrape = 1; break; case 'u': uploadLimit = atoi( optarg ); break; @@ -429,6 +434,9 @@ parseCommandLine( int argc, char ** argv ) } } + if( !*downloadDir ) + getcwd( downloadDir, sizeof( downloadDir ) ); + if( showHelp || showVersion ) return 0; diff --git a/configure.ac b/configure.ac index be25ef6a2..591415ea6 100644 --- a/configure.ac +++ b/configure.ac @@ -34,7 +34,7 @@ if test "x$GCC" = "xyes" ; then fi AC_HEADER_STDC AC_HEADER_TIME -AC_CHECK_FUNCS([lrintf strlcpy daemon dirname basename]) +AC_CHECK_FUNCS([lrintf strlcpy daemon dirname basename daemon]) AC_CHECK_SIZEOF([void*]) AC_PROG_INSTALL AC_PROG_MAKE_SET diff --git a/daemon/Makefile.am b/daemon/Makefile.am index f1fdee8c6..bce7461e3 100644 --- a/daemon/Makefile.am +++ b/daemon/Makefile.am @@ -1,33 +1,15 @@ -AM_CPPFLAGS = -I@top_srcdir@ $(LIBEVENT_CPPFLAGS) +AM_CPPFLAGS = -I@top_srcdir@ $(LIBEVENT_CPPFLAGS) -I$(top_srcdir)/third-party/ -DEMBEDDED AM_CFLAGS = $(OPENSSL_CFLAGS) $(LIBCURL_CFLAGS) $(PTHREAD_CFLAGS) -noinst_LIBRARIES = libdaemon.a - dist_man_MANS = \ transmission-daemon.1 \ - transmission-proxy.1 \ transmission-remote.1 -libdaemon_a_SOURCES = \ - errors.c \ - misc.c - -noinst_HEADERS = \ - bsdtree.h \ - bsdqueue.h \ - client.h \ - errors.h \ - misc.h \ - server.h \ - torrents.h - bin_PROGRAMS = \ transmission-daemon \ - transmission-remote \ - transmission-proxy + transmission-remote COMMON_LDADD = \ - ./libdaemon.a \ $(top_builddir)/libtransmission/libtransmission.a \ $(top_builddir)/third-party/miniupnp/libminiupnp.a \ $(top_builddir)/third-party/libnatpmp/libnatpmp.a \ @@ -37,9 +19,7 @@ COMMON_LDADD = \ $(LIBCURL_LIBS) \ $(PTHREAD_LIBS) -lm -transmission_daemon_SOURCES = daemon.c server.c torrents.c -transmission_daemon_LDADD = $(COMMON_LDADD) -transmission_remote_SOURCES = client.c remote.c +transmission_daemon_SOURCES = daemon.c +transmission_daemon_LDADD = $(COMMON_LDADD) $(top_builddir)/third-party/shttpd/libshttpd.a +transmission_remote_SOURCES = remote.c transmission_remote_LDADD = $(COMMON_LDADD) -transmission_proxy_SOURCES = proxy.c -transmission_proxy_LDADD = $(COMMON_LDADD) diff --git a/daemon/bsdqueue.h b/daemon/bsdqueue.h deleted file mode 100644 index 11e0cfc88..000000000 --- a/daemon/bsdqueue.h +++ /dev/null @@ -1,528 +0,0 @@ -/* $Id: bsdqueue.h 1580 2007-03-23 08:41:15Z joshe $ */ -/* $OpenBSD: queue.h,v 1.31 2005/11/25 08:06:25 otto Exp $ */ -/* $NetBSD: queue.h,v 1.11 1996/05/16 05:17:14 mycroft Exp $ */ - -/* - * Copyright (c) 1991, 1993 - * The Regents of the University of California. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)queue.h 8.5 (Berkeley) 8/20/94 - */ - -#ifndef _SYS_QUEUE_H_ -#define _SYS_QUEUE_H_ - -/* - * This file defines five types of data structures: singly-linked lists, - * lists, simple queues, tail queues, and circular queues. - * - * - * A singly-linked list is headed by a single forward pointer. The elements - * are singly linked for minimum space and pointer manipulation overhead at - * the expense of O(n) removal for arbitrary elements. New elements can be - * added to the list after an existing element or at the head of the list. - * Elements being removed from the head of the list should use the explicit - * macro for this purpose for optimum efficiency. A singly-linked list may - * only be traversed in the forward direction. Singly-linked lists are ideal - * for applications with large datasets and few or no removals or for - * implementing a LIFO queue. - * - * A list is headed by a single forward pointer (or an array of forward - * pointers for a hash table header). The elements are doubly linked - * so that an arbitrary element can be removed without a need to - * traverse the list. New elements can be added to the list before - * or after an existing element or at the head of the list. A list - * may only be traversed in the forward direction. - * - * A simple queue is headed by a pair of pointers, one the head of the - * list and the other to the tail of the list. The elements are singly - * linked to save space, so elements can only be removed from the - * head of the list. New elements can be added to the list before or after - * an existing element, at the head of the list, or at the end of the - * list. A simple queue may only be traversed in the forward direction. - * - * A tail queue is headed by a pair of pointers, one to the head of the - * list and the other to the tail of the list. The elements are doubly - * linked so that an arbitrary element can be removed without a need to - * traverse the list. New elements can be added to the list before or - * after an existing element, at the head of the list, or at the end of - * the list. A tail queue may be traversed in either direction. - * - * A circle queue is headed by a pair of pointers, one to the head of the - * list and the other to the tail of the list. The elements are doubly - * linked so that an arbitrary element can be removed without a need to - * traverse the list. New elements can be added to the list before or after - * an existing element, at the head of the list, or at the end of the list. - * A circle queue may be traversed in either direction, but has a more - * complex end of list detection. - * - * For details on the use of these macros, see the queue(3) manual page. - */ - -#ifdef QUEUE_MACRO_DEBUG -#define _Q_INVALIDATE(a) (a) = ((void *)-1) -#else -#define _Q_INVALIDATE(a) -#endif - -/* - * Singly-linked List definitions. - */ -#define SLIST_HEAD(name, type) \ -struct name { \ - struct type *slh_first; /* first element */ \ -} - -#define SLIST_HEAD_INITIALIZER(head) \ - { NULL } - -#define SLIST_ENTRY(type) \ -struct { \ - struct type *sle_next; /* next element */ \ -} - -/* - * Singly-linked List access methods. - */ -#define SLIST_FIRST(head) ((head)->slh_first) -#define SLIST_END(head) NULL -#define SLIST_EMPTY(head) (SLIST_FIRST(head) == SLIST_END(head)) -#define SLIST_NEXT(elm, field) ((elm)->field.sle_next) - -#define SLIST_FOREACH(var, head, field) \ - for((var) = SLIST_FIRST(head); \ - (var) != SLIST_END(head); \ - (var) = SLIST_NEXT(var, field)) - -#define SLIST_FOREACH_PREVPTR(var, varp, head, field) \ - for ((varp) = &SLIST_FIRST((head)); \ - ((var) = *(varp)) != SLIST_END(head); \ - (varp) = &SLIST_NEXT((var), field)) - -/* - * Singly-linked List functions. - */ -#define SLIST_INIT(head) { \ - SLIST_FIRST(head) = SLIST_END(head); \ -} - -#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \ - (elm)->field.sle_next = (slistelm)->field.sle_next; \ - (slistelm)->field.sle_next = (elm); \ -} while (0) - -#define SLIST_INSERT_HEAD(head, elm, field) do { \ - (elm)->field.sle_next = (head)->slh_first; \ - (head)->slh_first = (elm); \ -} while (0) - -#define SLIST_REMOVE_NEXT(head, elm, field) do { \ - (elm)->field.sle_next = (elm)->field.sle_next->field.sle_next; \ -} while (0) - -#define SLIST_REMOVE_HEAD(head, field) do { \ - (head)->slh_first = (head)->slh_first->field.sle_next; \ -} while (0) - -#define SLIST_REMOVE(head, elm, type, field) do { \ - if ((head)->slh_first == (elm)) { \ - SLIST_REMOVE_HEAD((head), field); \ - } else { \ - struct type *curelm = (head)->slh_first; \ - \ - while (curelm->field.sle_next != (elm)) \ - curelm = curelm->field.sle_next; \ - curelm->field.sle_next = \ - curelm->field.sle_next->field.sle_next; \ - _Q_INVALIDATE((elm)->field.sle_next); \ - } \ -} while (0) - -/* - * List definitions. - */ -#define LIST_HEAD(name, type) \ -struct name { \ - struct type *lh_first; /* first element */ \ -} - -#define LIST_HEAD_INITIALIZER(head) \ - { NULL } - -#define LIST_ENTRY(type) \ -struct { \ - struct type *le_next; /* next element */ \ - struct type **le_prev; /* address of previous next element */ \ -} - -/* - * List access methods - */ -#define LIST_FIRST(head) ((head)->lh_first) -#define LIST_END(head) NULL -#define LIST_EMPTY(head) (LIST_FIRST(head) == LIST_END(head)) -#define LIST_NEXT(elm, field) ((elm)->field.le_next) - -#define LIST_FOREACH(var, head, field) \ - for((var) = LIST_FIRST(head); \ - (var)!= LIST_END(head); \ - (var) = LIST_NEXT(var, field)) - -/* - * List functions. - */ -#define LIST_INIT(head) do { \ - LIST_FIRST(head) = LIST_END(head); \ -} while (0) - -#define LIST_INSERT_AFTER(listelm, elm, field) do { \ - if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \ - (listelm)->field.le_next->field.le_prev = \ - &(elm)->field.le_next; \ - (listelm)->field.le_next = (elm); \ - (elm)->field.le_prev = &(listelm)->field.le_next; \ -} while (0) - -#define LIST_INSERT_BEFORE(listelm, elm, field) do { \ - (elm)->field.le_prev = (listelm)->field.le_prev; \ - (elm)->field.le_next = (listelm); \ - *(listelm)->field.le_prev = (elm); \ - (listelm)->field.le_prev = &(elm)->field.le_next; \ -} while (0) - -#define LIST_INSERT_HEAD(head, elm, field) do { \ - if (((elm)->field.le_next = (head)->lh_first) != NULL) \ - (head)->lh_first->field.le_prev = &(elm)->field.le_next;\ - (head)->lh_first = (elm); \ - (elm)->field.le_prev = &(head)->lh_first; \ -} while (0) - -#define LIST_REMOVE(elm, field) do { \ - if ((elm)->field.le_next != NULL) \ - (elm)->field.le_next->field.le_prev = \ - (elm)->field.le_prev; \ - *(elm)->field.le_prev = (elm)->field.le_next; \ - _Q_INVALIDATE((elm)->field.le_prev); \ - _Q_INVALIDATE((elm)->field.le_next); \ -} while (0) - -#define LIST_REPLACE(elm, elm2, field) do { \ - if (((elm2)->field.le_next = (elm)->field.le_next) != NULL) \ - (elm2)->field.le_next->field.le_prev = \ - &(elm2)->field.le_next; \ - (elm2)->field.le_prev = (elm)->field.le_prev; \ - *(elm2)->field.le_prev = (elm2); \ - _Q_INVALIDATE((elm)->field.le_prev); \ - _Q_INVALIDATE((elm)->field.le_next); \ -} while (0) - -/* - * Simple queue definitions. - */ -#define SIMPLEQ_HEAD(name, type) \ -struct name { \ - struct type *sqh_first; /* first element */ \ - struct type **sqh_last; /* addr of last next element */ \ -} - -#define SIMPLEQ_HEAD_INITIALIZER(head) \ - { NULL, &(head).sqh_first } - -#define SIMPLEQ_ENTRY(type) \ -struct { \ - struct type *sqe_next; /* next element */ \ -} - -/* - * Simple queue access methods. - */ -#define SIMPLEQ_FIRST(head) ((head)->sqh_first) -#define SIMPLEQ_END(head) NULL -#define SIMPLEQ_EMPTY(head) (SIMPLEQ_FIRST(head) == SIMPLEQ_END(head)) -#define SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next) - -#define SIMPLEQ_FOREACH(var, head, field) \ - for((var) = SIMPLEQ_FIRST(head); \ - (var) != SIMPLEQ_END(head); \ - (var) = SIMPLEQ_NEXT(var, field)) - -/* - * Simple queue functions. - */ -#define SIMPLEQ_INIT(head) do { \ - (head)->sqh_first = NULL; \ - (head)->sqh_last = &(head)->sqh_first; \ -} while (0) - -#define SIMPLEQ_INSERT_HEAD(head, elm, field) do { \ - if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \ - (head)->sqh_last = &(elm)->field.sqe_next; \ - (head)->sqh_first = (elm); \ -} while (0) - -#define SIMPLEQ_INSERT_TAIL(head, elm, field) do { \ - (elm)->field.sqe_next = NULL; \ - *(head)->sqh_last = (elm); \ - (head)->sqh_last = &(elm)->field.sqe_next; \ -} while (0) - -#define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ - if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\ - (head)->sqh_last = &(elm)->field.sqe_next; \ - (listelm)->field.sqe_next = (elm); \ -} while (0) - -#define SIMPLEQ_REMOVE_HEAD(head, field) do { \ - if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \ - (head)->sqh_last = &(head)->sqh_first; \ -} while (0) - -/* - * Tail queue definitions. - */ -#define TAILQ_HEAD(name, type) \ -struct name { \ - struct type *tqh_first; /* first element */ \ - struct type **tqh_last; /* addr of last next element */ \ -} - -#define TAILQ_HEAD_INITIALIZER(head) \ - { NULL, &(head).tqh_first } - -#define TAILQ_ENTRY(type) \ -struct { \ - struct type *tqe_next; /* next element */ \ - struct type **tqe_prev; /* address of previous next element */ \ -} - -/* - * tail queue access methods - */ -#define TAILQ_FIRST(head) ((head)->tqh_first) -#define TAILQ_END(head) NULL -#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next) -#define TAILQ_LAST(head, headname) \ - (*(((struct headname *)((head)->tqh_last))->tqh_last)) -/* XXX */ -#define TAILQ_PREV(elm, headname, field) \ - (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last)) -#define TAILQ_EMPTY(head) \ - (TAILQ_FIRST(head) == TAILQ_END(head)) - -#define TAILQ_FOREACH(var, head, field) \ - for((var) = TAILQ_FIRST(head); \ - (var) != TAILQ_END(head); \ - (var) = TAILQ_NEXT(var, field)) - -#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \ - for((var) = TAILQ_LAST(head, headname); \ - (var) != TAILQ_END(head); \ - (var) = TAILQ_PREV(var, headname, field)) - -/* - * Tail queue functions. - */ -#define TAILQ_INIT(head) do { \ - (head)->tqh_first = NULL; \ - (head)->tqh_last = &(head)->tqh_first; \ -} while (0) - -#define TAILQ_INSERT_HEAD(head, elm, field) do { \ - if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \ - (head)->tqh_first->field.tqe_prev = \ - &(elm)->field.tqe_next; \ - else \ - (head)->tqh_last = &(elm)->field.tqe_next; \ - (head)->tqh_first = (elm); \ - (elm)->field.tqe_prev = &(head)->tqh_first; \ -} while (0) - -#define TAILQ_INSERT_TAIL(head, elm, field) do { \ - (elm)->field.tqe_next = NULL; \ - (elm)->field.tqe_prev = (head)->tqh_last; \ - *(head)->tqh_last = (elm); \ - (head)->tqh_last = &(elm)->field.tqe_next; \ -} while (0) - -#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ - if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\ - (elm)->field.tqe_next->field.tqe_prev = \ - &(elm)->field.tqe_next; \ - else \ - (head)->tqh_last = &(elm)->field.tqe_next; \ - (listelm)->field.tqe_next = (elm); \ - (elm)->field.tqe_prev = &(listelm)->field.tqe_next; \ -} while (0) - -#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \ - (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \ - (elm)->field.tqe_next = (listelm); \ - *(listelm)->field.tqe_prev = (elm); \ - (listelm)->field.tqe_prev = &(elm)->field.tqe_next; \ -} while (0) - -#define TAILQ_REMOVE(head, elm, field) do { \ - if (((elm)->field.tqe_next) != NULL) \ - (elm)->field.tqe_next->field.tqe_prev = \ - (elm)->field.tqe_prev; \ - else \ - (head)->tqh_last = (elm)->field.tqe_prev; \ - *(elm)->field.tqe_prev = (elm)->field.tqe_next; \ - _Q_INVALIDATE((elm)->field.tqe_prev); \ - _Q_INVALIDATE((elm)->field.tqe_next); \ -} while (0) - -#define TAILQ_REPLACE(head, elm, elm2, field) do { \ - if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != NULL) \ - (elm2)->field.tqe_next->field.tqe_prev = \ - &(elm2)->field.tqe_next; \ - else \ - (head)->tqh_last = &(elm2)->field.tqe_next; \ - (elm2)->field.tqe_prev = (elm)->field.tqe_prev; \ - *(elm2)->field.tqe_prev = (elm2); \ - _Q_INVALIDATE((elm)->field.tqe_prev); \ - _Q_INVALIDATE((elm)->field.tqe_next); \ -} while (0) - -/* - * Circular queue definitions. - */ -#define CIRCLEQ_HEAD(name, type) \ -struct name { \ - struct type *cqh_first; /* first element */ \ - struct type *cqh_last; /* last element */ \ -} - -#define CIRCLEQ_HEAD_INITIALIZER(head) \ - { CIRCLEQ_END(&head), CIRCLEQ_END(&head) } - -#define CIRCLEQ_ENTRY(type) \ -struct { \ - struct type *cqe_next; /* next element */ \ - struct type *cqe_prev; /* previous element */ \ -} - -/* - * Circular queue access methods - */ -#define CIRCLEQ_FIRST(head) ((head)->cqh_first) -#define CIRCLEQ_LAST(head) ((head)->cqh_last) -#define CIRCLEQ_END(head) ((void *)(head)) -#define CIRCLEQ_NEXT(elm, field) ((elm)->field.cqe_next) -#define CIRCLEQ_PREV(elm, field) ((elm)->field.cqe_prev) -#define CIRCLEQ_EMPTY(head) \ - (CIRCLEQ_FIRST(head) == CIRCLEQ_END(head)) - -#define CIRCLEQ_FOREACH(var, head, field) \ - for((var) = CIRCLEQ_FIRST(head); \ - (var) != CIRCLEQ_END(head); \ - (var) = CIRCLEQ_NEXT(var, field)) - -#define CIRCLEQ_FOREACH_REVERSE(var, head, field) \ - for((var) = CIRCLEQ_LAST(head); \ - (var) != CIRCLEQ_END(head); \ - (var) = CIRCLEQ_PREV(var, field)) - -/* - * Circular queue functions. - */ -#define CIRCLEQ_INIT(head) do { \ - (head)->cqh_first = CIRCLEQ_END(head); \ - (head)->cqh_last = CIRCLEQ_END(head); \ -} while (0) - -#define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ - (elm)->field.cqe_next = (listelm)->field.cqe_next; \ - (elm)->field.cqe_prev = (listelm); \ - if ((listelm)->field.cqe_next == CIRCLEQ_END(head)) \ - (head)->cqh_last = (elm); \ - else \ - (listelm)->field.cqe_next->field.cqe_prev = (elm); \ - (listelm)->field.cqe_next = (elm); \ -} while (0) - -#define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do { \ - (elm)->field.cqe_next = (listelm); \ - (elm)->field.cqe_prev = (listelm)->field.cqe_prev; \ - if ((listelm)->field.cqe_prev == CIRCLEQ_END(head)) \ - (head)->cqh_first = (elm); \ - else \ - (listelm)->field.cqe_prev->field.cqe_next = (elm); \ - (listelm)->field.cqe_prev = (elm); \ -} while (0) - -#define CIRCLEQ_INSERT_HEAD(head, elm, field) do { \ - (elm)->field.cqe_next = (head)->cqh_first; \ - (elm)->field.cqe_prev = CIRCLEQ_END(head); \ - if ((head)->cqh_last == CIRCLEQ_END(head)) \ - (head)->cqh_last = (elm); \ - else \ - (head)->cqh_first->field.cqe_prev = (elm); \ - (head)->cqh_first = (elm); \ -} while (0) - -#define CIRCLEQ_INSERT_TAIL(head, elm, field) do { \ - (elm)->field.cqe_next = CIRCLEQ_END(head); \ - (elm)->field.cqe_prev = (head)->cqh_last; \ - if ((head)->cqh_first == CIRCLEQ_END(head)) \ - (head)->cqh_first = (elm); \ - else \ - (head)->cqh_last->field.cqe_next = (elm); \ - (head)->cqh_last = (elm); \ -} while (0) - -#define CIRCLEQ_REMOVE(head, elm, field) do { \ - if ((elm)->field.cqe_next == CIRCLEQ_END(head)) \ - (head)->cqh_last = (elm)->field.cqe_prev; \ - else \ - (elm)->field.cqe_next->field.cqe_prev = \ - (elm)->field.cqe_prev; \ - if ((elm)->field.cqe_prev == CIRCLEQ_END(head)) \ - (head)->cqh_first = (elm)->field.cqe_next; \ - else \ - (elm)->field.cqe_prev->field.cqe_next = \ - (elm)->field.cqe_next; \ - _Q_INVALIDATE((elm)->field.cqe_prev); \ - _Q_INVALIDATE((elm)->field.cqe_next); \ -} while (0) - -#define CIRCLEQ_REPLACE(head, elm, elm2, field) do { \ - if (((elm2)->field.cqe_next = (elm)->field.cqe_next) == \ - CIRCLEQ_END(head)) \ - (head).cqh_last = (elm2); \ - else \ - (elm2)->field.cqe_next->field.cqe_prev = (elm2); \ - if (((elm2)->field.cqe_prev = (elm)->field.cqe_prev) == \ - CIRCLEQ_END(head)) \ - (head).cqh_first = (elm2); \ - else \ - (elm2)->field.cqe_prev->field.cqe_next = (elm2); \ - _Q_INVALIDATE((elm)->field.cqe_prev); \ - _Q_INVALIDATE((elm)->field.cqe_next); \ -} while (0) - -#endif /* !_SYS_QUEUE_H_ */ diff --git a/daemon/bsdtree.h b/daemon/bsdtree.h deleted file mode 100644 index 54b7e047a..000000000 --- a/daemon/bsdtree.h +++ /dev/null @@ -1,701 +0,0 @@ -/* $Id: bsdtree.h 1580 2007-03-23 08:41:15Z joshe $ */ -/* $NetBSD: tree.h,v 1.14 2007/01/20 20:15:13 ad Exp $ */ -/* $OpenBSD: tree.h,v 1.7 2002/10/17 21:51:54 art Exp $ */ -/* - * Copyright 2002 Niels Provos - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef _SYS_TREE_H_ -#define _SYS_TREE_H_ - -#ifndef __unused -#ifdef __GNUC__ -#define __unused __attribute__((__unused__)) -#else -#define __unused -#endif -#endif - -/* - * This file defines data structures for different types of trees: - * splay trees and red-black trees. - * - * A splay tree is a self-organizing data structure. Every operation - * on the tree causes a splay to happen. The splay moves the requested - * node to the root of the tree and partly rebalances it. - * - * This has the benefit that request locality causes faster lookups as - * the requested nodes move to the top of the tree. On the other hand, - * every lookup causes memory writes. - * - * The Balance Theorem bounds the total access time for m operations - * and n inserts on an initially empty tree as O((m + n)lg n). The - * amortized cost for a sequence of m accesses to a splay tree is O(lg n); - * - * A red-black tree is a binary search tree with the node color as an - * extra attribute. It fulfills a set of conditions: - * - every search path from the root to a leaf consists of the - * same number of black nodes, - * - each red node (except for the root) has a black parent, - * - each leaf node is black. - * - * Every operation on a red-black tree is bounded as O(lg n). - * The maximum height of a red-black tree is 2lg (n+1). - */ - -#define SPLAY_HEAD(name, type) \ -struct name { \ - struct type *sph_root; /* root of the tree */ \ -} - -#define SPLAY_INITIALIZER(root) \ - { NULL } - -#define SPLAY_INIT(root) do { \ - (root)->sph_root = NULL; \ -} while (/*CONSTCOND*/ 0) - -#define SPLAY_ENTRY(type) \ -struct { \ - struct type *spe_left; /* left element */ \ - struct type *spe_right; /* right element */ \ -} - -#define SPLAY_LEFT(elm, field) (elm)->field.spe_left -#define SPLAY_RIGHT(elm, field) (elm)->field.spe_right -#define SPLAY_ROOT(head) (head)->sph_root -#define SPLAY_EMPTY(head) (SPLAY_ROOT(head) == NULL) - -/* SPLAY_ROTATE_{LEFT,RIGHT} expect that tmp hold SPLAY_{RIGHT,LEFT} */ -#define SPLAY_ROTATE_RIGHT(head, tmp, field) do { \ - SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(tmp, field); \ - SPLAY_RIGHT(tmp, field) = (head)->sph_root; \ - (head)->sph_root = tmp; \ -} while (/*CONSTCOND*/ 0) - -#define SPLAY_ROTATE_LEFT(head, tmp, field) do { \ - SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(tmp, field); \ - SPLAY_LEFT(tmp, field) = (head)->sph_root; \ - (head)->sph_root = tmp; \ -} while (/*CONSTCOND*/ 0) - -#define SPLAY_LINKLEFT(head, tmp, field) do { \ - SPLAY_LEFT(tmp, field) = (head)->sph_root; \ - tmp = (head)->sph_root; \ - (head)->sph_root = SPLAY_LEFT((head)->sph_root, field); \ -} while (/*CONSTCOND*/ 0) - -#define SPLAY_LINKRIGHT(head, tmp, field) do { \ - SPLAY_RIGHT(tmp, field) = (head)->sph_root; \ - tmp = (head)->sph_root; \ - (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field); \ -} while (/*CONSTCOND*/ 0) - -#define SPLAY_ASSEMBLE(head, node, left, right, field) do { \ - SPLAY_RIGHT(left, field) = SPLAY_LEFT((head)->sph_root, field); \ - SPLAY_LEFT(right, field) = SPLAY_RIGHT((head)->sph_root, field);\ - SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(node, field); \ - SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(node, field); \ -} while (/*CONSTCOND*/ 0) - -/* Generates prototypes and inline functions */ - -#define SPLAY_PROTOTYPE(name, type, field, cmp) \ -void name##_SPLAY(struct name *, struct type *); \ -void name##_SPLAY_MINMAX(struct name *, int); \ -struct type *name##_SPLAY_INSERT(struct name *, struct type *); \ -struct type *name##_SPLAY_REMOVE(struct name *, struct type *); \ - \ -/* Finds the node with the same key as elm */ \ -static __inline struct type * \ -name##_SPLAY_FIND(struct name *head, struct type *elm) \ -{ \ - if (SPLAY_EMPTY(head)) \ - return(NULL); \ - name##_SPLAY(head, elm); \ - if ((cmp)(elm, (head)->sph_root) == 0) \ - return (head->sph_root); \ - return (NULL); \ -} \ - \ -static __inline struct type * \ -name##_SPLAY_NEXT(struct name *head, struct type *elm) \ -{ \ - name##_SPLAY(head, elm); \ - if (SPLAY_RIGHT(elm, field) != NULL) { \ - elm = SPLAY_RIGHT(elm, field); \ - while (SPLAY_LEFT(elm, field) != NULL) { \ - elm = SPLAY_LEFT(elm, field); \ - } \ - } else \ - elm = NULL; \ - return (elm); \ -} \ - \ -static __inline struct type * \ -name##_SPLAY_MIN_MAX(struct name *head, int val) \ -{ \ - name##_SPLAY_MINMAX(head, val); \ - return (SPLAY_ROOT(head)); \ -} - -/* Main splay operation. - * Moves node close to the key of elm to top - */ -#define SPLAY_GENERATE(name, type, field, cmp) \ -struct type * \ -name##_SPLAY_INSERT(struct name *head, struct type *elm) \ -{ \ - if (SPLAY_EMPTY(head)) { \ - SPLAY_LEFT(elm, field) = SPLAY_RIGHT(elm, field) = NULL; \ - } else { \ - int __comp; \ - name##_SPLAY(head, elm); \ - __comp = (cmp)(elm, (head)->sph_root); \ - if(__comp < 0) { \ - SPLAY_LEFT(elm, field) = SPLAY_LEFT((head)->sph_root, field);\ - SPLAY_RIGHT(elm, field) = (head)->sph_root; \ - SPLAY_LEFT((head)->sph_root, field) = NULL; \ - } else if (__comp > 0) { \ - SPLAY_RIGHT(elm, field) = SPLAY_RIGHT((head)->sph_root, field);\ - SPLAY_LEFT(elm, field) = (head)->sph_root; \ - SPLAY_RIGHT((head)->sph_root, field) = NULL; \ - } else \ - return ((head)->sph_root); \ - } \ - (head)->sph_root = (elm); \ - return (NULL); \ -} \ - \ -struct type * \ -name##_SPLAY_REMOVE(struct name *head, struct type *elm) \ -{ \ - struct type *__tmp; \ - if (SPLAY_EMPTY(head)) \ - return (NULL); \ - name##_SPLAY(head, elm); \ - if ((cmp)(elm, (head)->sph_root) == 0) { \ - if (SPLAY_LEFT((head)->sph_root, field) == NULL) { \ - (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field);\ - } else { \ - __tmp = SPLAY_RIGHT((head)->sph_root, field); \ - (head)->sph_root = SPLAY_LEFT((head)->sph_root, field);\ - name##_SPLAY(head, elm); \ - SPLAY_RIGHT((head)->sph_root, field) = __tmp; \ - } \ - return (elm); \ - } \ - return (NULL); \ -} \ - \ -void \ -name##_SPLAY(struct name *head, struct type *elm) \ -{ \ - struct type __node, *__left, *__right, *__tmp; \ - int __comp; \ -\ - SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\ - __left = __right = &__node; \ -\ - while ((__comp = (cmp)(elm, (head)->sph_root)) != 0) { \ - if (__comp < 0) { \ - __tmp = SPLAY_LEFT((head)->sph_root, field); \ - if (__tmp == NULL) \ - break; \ - if ((cmp)(elm, __tmp) < 0){ \ - SPLAY_ROTATE_RIGHT(head, __tmp, field); \ - if (SPLAY_LEFT((head)->sph_root, field) == NULL)\ - break; \ - } \ - SPLAY_LINKLEFT(head, __right, field); \ - } else if (__comp > 0) { \ - __tmp = SPLAY_RIGHT((head)->sph_root, field); \ - if (__tmp == NULL) \ - break; \ - if ((cmp)(elm, __tmp) > 0){ \ - SPLAY_ROTATE_LEFT(head, __tmp, field); \ - if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\ - break; \ - } \ - SPLAY_LINKRIGHT(head, __left, field); \ - } \ - } \ - SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \ -} \ - \ -/* Splay with either the minimum or the maximum element \ - * Used to find minimum or maximum element in tree. \ - */ \ -void name##_SPLAY_MINMAX(struct name *head, int __comp) \ -{ \ - struct type __node, *__left, *__right, *__tmp; \ -\ - SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\ - __left = __right = &__node; \ -\ - while (1) { \ - if (__comp < 0) { \ - __tmp = SPLAY_LEFT((head)->sph_root, field); \ - if (__tmp == NULL) \ - break; \ - if (__comp < 0){ \ - SPLAY_ROTATE_RIGHT(head, __tmp, field); \ - if (SPLAY_LEFT((head)->sph_root, field) == NULL)\ - break; \ - } \ - SPLAY_LINKLEFT(head, __right, field); \ - } else if (__comp > 0) { \ - __tmp = SPLAY_RIGHT((head)->sph_root, field); \ - if (__tmp == NULL) \ - break; \ - if (__comp > 0) { \ - SPLAY_ROTATE_LEFT(head, __tmp, field); \ - if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\ - break; \ - } \ - SPLAY_LINKRIGHT(head, __left, field); \ - } \ - } \ - SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \ -} - -#define SPLAY_NEGINF -1 -#define SPLAY_INF 1 - -#define SPLAY_INSERT(name, x, y) name##_SPLAY_INSERT(x, y) -#define SPLAY_REMOVE(name, x, y) name##_SPLAY_REMOVE(x, y) -#define SPLAY_FIND(name, x, y) name##_SPLAY_FIND(x, y) -#define SPLAY_NEXT(name, x, y) name##_SPLAY_NEXT(x, y) -#define SPLAY_MIN(name, x) (SPLAY_EMPTY(x) ? NULL \ - : name##_SPLAY_MIN_MAX(x, SPLAY_NEGINF)) -#define SPLAY_MAX(name, x) (SPLAY_EMPTY(x) ? NULL \ - : name##_SPLAY_MIN_MAX(x, SPLAY_INF)) - -#define SPLAY_FOREACH(x, name, head) \ - for ((x) = SPLAY_MIN(name, head); \ - (x) != NULL; \ - (x) = SPLAY_NEXT(name, head, x)) - -/* Macros that define a red-black tree */ -#define RB_HEAD(name, type) \ -struct name { \ - struct type *rbh_root; /* root of the tree */ \ -} - -#define RB_INITIALIZER(root) \ - { NULL } - -#define RB_INIT(root) do { \ - (root)->rbh_root = NULL; \ -} while (/*CONSTCOND*/ 0) - -#define RB_BLACK 0 -#define RB_RED 1 -#define RB_ENTRY(type) \ -struct { \ - struct type *rbe_left; /* left element */ \ - struct type *rbe_right; /* right element */ \ - struct type *rbe_parent; /* parent element */ \ - int rbe_color; /* node color */ \ -} - -#define RB_ENTRY_INITIALIZER() \ - { NULL, NULL, NULL, 0 } - -#define RB_LEFT(elm, field) (elm)->field.rbe_left -#define RB_RIGHT(elm, field) (elm)->field.rbe_right -#define RB_PARENT(elm, field) (elm)->field.rbe_parent -#define RB_COLOR(elm, field) (elm)->field.rbe_color -#define RB_ROOT(head) (head)->rbh_root -#define RB_EMPTY(head) (RB_ROOT(head) == NULL) - -#define RB_SET(elm, parent, field) do { \ - RB_PARENT(elm, field) = parent; \ - RB_LEFT(elm, field) = RB_RIGHT(elm, field) = NULL; \ - RB_COLOR(elm, field) = RB_RED; \ -} while (/*CONSTCOND*/ 0) - -#define RB_SET_BLACKRED(black, red, field) do { \ - RB_COLOR(black, field) = RB_BLACK; \ - RB_COLOR(red, field) = RB_RED; \ -} while (/*CONSTCOND*/ 0) - -#ifndef RB_AUGMENT -#define RB_AUGMENT(x) (void)(x) -#endif - -#define RB_ROTATE_LEFT(head, elm, tmp, field) do { \ - (tmp) = RB_RIGHT(elm, field); \ - if ((RB_RIGHT(elm, field) = RB_LEFT(tmp, field)) != NULL) { \ - RB_PARENT(RB_LEFT(tmp, field), field) = (elm); \ - } \ - RB_AUGMENT(elm); \ - if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field)) != NULL) { \ - if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \ - RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \ - else \ - RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \ - } else \ - (head)->rbh_root = (tmp); \ - RB_LEFT(tmp, field) = (elm); \ - RB_PARENT(elm, field) = (tmp); \ - RB_AUGMENT(tmp); \ - if ((RB_PARENT(tmp, field))) \ - RB_AUGMENT(RB_PARENT(tmp, field)); \ -} while (/*CONSTCOND*/ 0) - -#define RB_ROTATE_RIGHT(head, elm, tmp, field) do { \ - (tmp) = RB_LEFT(elm, field); \ - if ((RB_LEFT(elm, field) = RB_RIGHT(tmp, field)) != NULL) { \ - RB_PARENT(RB_RIGHT(tmp, field), field) = (elm); \ - } \ - RB_AUGMENT(elm); \ - if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field)) != NULL) { \ - if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \ - RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \ - else \ - RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \ - } else \ - (head)->rbh_root = (tmp); \ - RB_RIGHT(tmp, field) = (elm); \ - RB_PARENT(elm, field) = (tmp); \ - RB_AUGMENT(tmp); \ - if ((RB_PARENT(tmp, field))) \ - RB_AUGMENT(RB_PARENT(tmp, field)); \ -} while (/*CONSTCOND*/ 0) - -/* Generates prototypes and inline functions */ -#define RB_PROTOTYPE(name, type, field, cmp) \ - RB_PROTOTYPE_INTERNAL(name, type, field, cmp,) -#define RB_PROTOTYPE_STATIC(name, type, field, cmp) \ - RB_PROTOTYPE_INTERNAL(name, type, field, cmp, __unused static) -#define RB_PROTOTYPE_INTERNAL(name, type, field, cmp, attr) \ -attr void name##_RB_INSERT_COLOR(struct name *, struct type *); \ -attr void name##_RB_REMOVE_COLOR(struct name *, struct type *, struct type *);\ -attr struct type *name##_RB_REMOVE(struct name *, struct type *); \ -attr struct type *name##_RB_INSERT(struct name *, struct type *); \ -attr struct type *name##_RB_FIND(struct name *, struct type *); \ -attr struct type *name##_RB_NEXT(struct type *); \ -attr struct type *name##_RB_MINMAX(struct name *, int); \ - \ - -/* Main rb operation. - * Moves node close to the key of elm to top - */ -#define RB_GENERATE(name, type, field, cmp) \ - RB_GENERATE_INTERNAL(name, type, field, cmp,) -#define RB_GENERATE_STATIC(name, type, field, cmp) \ - RB_GENERATE_INTERNAL(name, type, field, cmp, __unused static) -#define RB_GENERATE_INTERNAL(name, type, field, cmp, attr) \ -attr void \ -name##_RB_INSERT_COLOR(struct name *head, struct type *elm) \ -{ \ - struct type *parent, *gparent, *tmp; \ - while ((parent = RB_PARENT(elm, field)) != NULL && \ - RB_COLOR(parent, field) == RB_RED) { \ - gparent = RB_PARENT(parent, field); \ - if (parent == RB_LEFT(gparent, field)) { \ - tmp = RB_RIGHT(gparent, field); \ - if (tmp && RB_COLOR(tmp, field) == RB_RED) { \ - RB_COLOR(tmp, field) = RB_BLACK; \ - RB_SET_BLACKRED(parent, gparent, field);\ - elm = gparent; \ - continue; \ - } \ - if (RB_RIGHT(parent, field) == elm) { \ - RB_ROTATE_LEFT(head, parent, tmp, field);\ - tmp = parent; \ - parent = elm; \ - elm = tmp; \ - } \ - RB_SET_BLACKRED(parent, gparent, field); \ - RB_ROTATE_RIGHT(head, gparent, tmp, field); \ - } else { \ - tmp = RB_LEFT(gparent, field); \ - if (tmp && RB_COLOR(tmp, field) == RB_RED) { \ - RB_COLOR(tmp, field) = RB_BLACK; \ - RB_SET_BLACKRED(parent, gparent, field);\ - elm = gparent; \ - continue; \ - } \ - if (RB_LEFT(parent, field) == elm) { \ - RB_ROTATE_RIGHT(head, parent, tmp, field);\ - tmp = parent; \ - parent = elm; \ - elm = tmp; \ - } \ - RB_SET_BLACKRED(parent, gparent, field); \ - RB_ROTATE_LEFT(head, gparent, tmp, field); \ - } \ - } \ - RB_COLOR(head->rbh_root, field) = RB_BLACK; \ -} \ - \ -attr void \ -name##_RB_REMOVE_COLOR(struct name *head, struct type *parent, struct type *elm) \ -{ \ - struct type *tmp; \ - while ((elm == NULL || RB_COLOR(elm, field) == RB_BLACK) && \ - elm != RB_ROOT(head)) { \ - if (RB_LEFT(parent, field) == elm) { \ - tmp = RB_RIGHT(parent, field); \ - if (RB_COLOR(tmp, field) == RB_RED) { \ - RB_SET_BLACKRED(tmp, parent, field); \ - RB_ROTATE_LEFT(head, parent, tmp, field);\ - tmp = RB_RIGHT(parent, field); \ - } \ - if ((RB_LEFT(tmp, field) == NULL || \ - RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\ - (RB_RIGHT(tmp, field) == NULL || \ - RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\ - RB_COLOR(tmp, field) = RB_RED; \ - elm = parent; \ - parent = RB_PARENT(elm, field); \ - } else { \ - if (RB_RIGHT(tmp, field) == NULL || \ - RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK) {\ - struct type *oleft; \ - if ((oleft = RB_LEFT(tmp, field)) \ - != NULL) \ - RB_COLOR(oleft, field) = RB_BLACK;\ - RB_COLOR(tmp, field) = RB_RED; \ - RB_ROTATE_RIGHT(head, tmp, oleft, field);\ - tmp = RB_RIGHT(parent, field); \ - } \ - RB_COLOR(tmp, field) = RB_COLOR(parent, field);\ - RB_COLOR(parent, field) = RB_BLACK; \ - if (RB_RIGHT(tmp, field)) \ - RB_COLOR(RB_RIGHT(tmp, field), field) = RB_BLACK;\ - RB_ROTATE_LEFT(head, parent, tmp, field);\ - elm = RB_ROOT(head); \ - break; \ - } \ - } else { \ - tmp = RB_LEFT(parent, field); \ - if (RB_COLOR(tmp, field) == RB_RED) { \ - RB_SET_BLACKRED(tmp, parent, field); \ - RB_ROTATE_RIGHT(head, parent, tmp, field);\ - tmp = RB_LEFT(parent, field); \ - } \ - if ((RB_LEFT(tmp, field) == NULL || \ - RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\ - (RB_RIGHT(tmp, field) == NULL || \ - RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\ - RB_COLOR(tmp, field) = RB_RED; \ - elm = parent; \ - parent = RB_PARENT(elm, field); \ - } else { \ - if (RB_LEFT(tmp, field) == NULL || \ - RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) {\ - struct type *oright; \ - if ((oright = RB_RIGHT(tmp, field)) \ - != NULL) \ - RB_COLOR(oright, field) = RB_BLACK;\ - RB_COLOR(tmp, field) = RB_RED; \ - RB_ROTATE_LEFT(head, tmp, oright, field);\ - tmp = RB_LEFT(parent, field); \ - } \ - RB_COLOR(tmp, field) = RB_COLOR(parent, field);\ - RB_COLOR(parent, field) = RB_BLACK; \ - if (RB_LEFT(tmp, field)) \ - RB_COLOR(RB_LEFT(tmp, field), field) = RB_BLACK;\ - RB_ROTATE_RIGHT(head, parent, tmp, field);\ - elm = RB_ROOT(head); \ - break; \ - } \ - } \ - } \ - if (elm) \ - RB_COLOR(elm, field) = RB_BLACK; \ -} \ - \ -attr struct type * \ -name##_RB_REMOVE(struct name *head, struct type *elm) \ -{ \ - struct type *child, *parent, *old = elm; \ - int color; \ - if (RB_LEFT(elm, field) == NULL) \ - child = RB_RIGHT(elm, field); \ - else if (RB_RIGHT(elm, field) == NULL) \ - child = RB_LEFT(elm, field); \ - else { \ - struct type *left; \ - elm = RB_RIGHT(elm, field); \ - while ((left = RB_LEFT(elm, field)) != NULL) \ - elm = left; \ - child = RB_RIGHT(elm, field); \ - parent = RB_PARENT(elm, field); \ - color = RB_COLOR(elm, field); \ - if (child) \ - RB_PARENT(child, field) = parent; \ - if (parent) { \ - if (RB_LEFT(parent, field) == elm) \ - RB_LEFT(parent, field) = child; \ - else \ - RB_RIGHT(parent, field) = child; \ - RB_AUGMENT(parent); \ - } else \ - RB_ROOT(head) = child; \ - if (RB_PARENT(elm, field) == old) \ - parent = elm; \ - (elm)->field = (old)->field; \ - if (RB_PARENT(old, field)) { \ - if (RB_LEFT(RB_PARENT(old, field), field) == old)\ - RB_LEFT(RB_PARENT(old, field), field) = elm;\ - else \ - RB_RIGHT(RB_PARENT(old, field), field) = elm;\ - RB_AUGMENT(RB_PARENT(old, field)); \ - } else \ - RB_ROOT(head) = elm; \ - RB_PARENT(RB_LEFT(old, field), field) = elm; \ - if (RB_RIGHT(old, field)) \ - RB_PARENT(RB_RIGHT(old, field), field) = elm; \ - if (parent) { \ - left = parent; \ - do { \ - RB_AUGMENT(left); \ - } while ((left = RB_PARENT(left, field)) != NULL); \ - } \ - goto color; \ - } \ - parent = RB_PARENT(elm, field); \ - color = RB_COLOR(elm, field); \ - if (child) \ - RB_PARENT(child, field) = parent; \ - if (parent) { \ - if (RB_LEFT(parent, field) == elm) \ - RB_LEFT(parent, field) = child; \ - else \ - RB_RIGHT(parent, field) = child; \ - RB_AUGMENT(parent); \ - } else \ - RB_ROOT(head) = child; \ -color: \ - if (color == RB_BLACK) \ - name##_RB_REMOVE_COLOR(head, parent, child); \ - return (old); \ -} \ - \ -/* Inserts a node into the RB tree */ \ -attr struct type * \ -name##_RB_INSERT(struct name *head, struct type *elm) \ -{ \ - struct type *tmp; \ - struct type *parent = NULL; \ - int comp = 0; \ - tmp = RB_ROOT(head); \ - while (tmp) { \ - parent = tmp; \ - comp = (cmp)(elm, parent); \ - if (comp < 0) \ - tmp = RB_LEFT(tmp, field); \ - else if (comp > 0) \ - tmp = RB_RIGHT(tmp, field); \ - else \ - return (tmp); \ - } \ - RB_SET(elm, parent, field); \ - if (parent != NULL) { \ - if (comp < 0) \ - RB_LEFT(parent, field) = elm; \ - else \ - RB_RIGHT(parent, field) = elm; \ - RB_AUGMENT(parent); \ - } else \ - RB_ROOT(head) = elm; \ - name##_RB_INSERT_COLOR(head, elm); \ - return (NULL); \ -} \ - \ -/* Finds the node with the same key as elm */ \ -attr struct type * \ -name##_RB_FIND(struct name *head, struct type *elm) \ -{ \ - struct type *tmp = RB_ROOT(head); \ - int comp; \ - while (tmp) { \ - comp = cmp(elm, tmp); \ - if (comp < 0) \ - tmp = RB_LEFT(tmp, field); \ - else if (comp > 0) \ - tmp = RB_RIGHT(tmp, field); \ - else \ - return (tmp); \ - } \ - return (NULL); \ -} \ - \ -/* ARGSUSED */ \ -attr struct type * \ -name##_RB_NEXT(struct type *elm) \ -{ \ - if (RB_RIGHT(elm, field)) { \ - elm = RB_RIGHT(elm, field); \ - while (RB_LEFT(elm, field)) \ - elm = RB_LEFT(elm, field); \ - } else { \ - if (RB_PARENT(elm, field) && \ - (elm == RB_LEFT(RB_PARENT(elm, field), field))) \ - elm = RB_PARENT(elm, field); \ - else { \ - while (RB_PARENT(elm, field) && \ - (elm == RB_RIGHT(RB_PARENT(elm, field), field)))\ - elm = RB_PARENT(elm, field); \ - elm = RB_PARENT(elm, field); \ - } \ - } \ - return (elm); \ -} \ - \ -attr struct type * \ -name##_RB_MINMAX(struct name *head, int val) \ -{ \ - struct type *tmp = RB_ROOT(head); \ - struct type *parent = NULL; \ - while (tmp) { \ - parent = tmp; \ - if (val < 0) \ - tmp = RB_LEFT(tmp, field); \ - else \ - tmp = RB_RIGHT(tmp, field); \ - } \ - return (parent); \ -} - -#define RB_NEGINF -1 -#define RB_INF 1 - -#define RB_INSERT(name, x, y) name##_RB_INSERT(x, y) -#define RB_REMOVE(name, x, y) name##_RB_REMOVE(x, y) -#define RB_FIND(name, x, y) name##_RB_FIND(x, y) -#define RB_NEXT(name, x, y) name##_RB_NEXT(y) -#define RB_MIN(name, x) name##_RB_MINMAX(x, RB_NEGINF) -#define RB_MAX(name, x) name##_RB_MINMAX(x, RB_INF) - -#define RB_FOREACH(x, name, head) \ - for ((x) = RB_MIN(name, head); \ - (x) != NULL; \ - (x) = name##_RB_NEXT(x)) - -#endif /* _SYS_TREE_H_ */ diff --git a/daemon/client.c b/daemon/client.c deleted file mode 100644 index cfd8b96df..000000000 --- a/daemon/client.c +++ /dev/null @@ -1,1072 +0,0 @@ -/****************************************************************************** - * $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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "bsdtree.h" -#include "bsdqueue.h" -#include "client.h" -#include "errors.h" -#include "misc.h" - -/* time out server after this many seconds */ -#define SERVER_TIMEOUT ( 15 ) - -struct con -{ - int infd; - int outfd; - struct ipc_info * ipc; - struct bufferevent * evin; - struct bufferevent * evout; -}; - -struct req -{ - enum ipc_msg id; - int64_t tag; - struct strlist * strs; - int64_t num; - char * str; - size_t listlen; - int64_t * numlist; - uint8_t * buf; - int types; - SLIST_ENTRY( req ) next; -}; - -SLIST_HEAD( reqlist, req ); - -struct resp -{ - int64_t tag; - cl_infofunc infocb; - cl_statfunc statcb; - RB_ENTRY( resp ) links; -}; - -RB_HEAD( resptree, resp ); - -static struct req * addreq ( enum ipc_msg, int64_t, struct resp ** ); -static int addintlistreq ( enum ipc_msg, size_t, const int * ); -static void noop ( struct bufferevent *, void * ); -static void noway ( struct bufferevent *, void * ); -static void didwrite ( struct bufferevent *, void * ); -static void ohshit ( struct bufferevent *, short, void * ); -static void canread ( struct bufferevent *, void * ); -static void flushreqs ( struct con * ); -static int sendvers ( struct con * ); -static void infomsg ( enum ipc_msg, benc_val_t *, int64_t, void * ); -static void statmsg ( enum ipc_msg, benc_val_t *, int64_t, void * ); -static void defmsg ( enum ipc_msg, benc_val_t *, int64_t, void * ); -static void cbdone ( struct resp * ); -static int64_t getinfoint ( enum ipc_msg, benc_val_t *, int, int64_t ); -static char * getinfostr ( enum ipc_msg, benc_val_t *, int, char * ); -static int resptagcmp ( struct resp *, struct resp * ); - -RB_GENERATE_STATIC( resptree, resp, links, resptagcmp ) -INTCMP_FUNC( resptagcmp, resp, tag ) - -static struct event_base * gl_base = NULL; -static struct ipc_funcs * gl_tree = NULL; -static struct reqlist gl_reqs = SLIST_HEAD_INITIALIZER( &gl_reqs ); -static struct resptree gl_resps = RB_INITIALIZER( &gl_resps ); -static int64_t gl_tag = 0; -static int gl_proxy = -1; - -int -client_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; - } - - ipc_addmsg( gl_tree, IPC_MSG_INFO, infomsg ); - ipc_addmsg( gl_tree, IPC_MSG_STAT, statmsg ); - ipc_setdefmsg( gl_tree, defmsg ); - - return 0; -} - -int -client_new_sock( const char * path ) -{ - struct sockaddr_un sa; - int fd; - struct con * con; - - assert( gl_base ); - assert( path ); - assert( 0 > gl_proxy ); - - gl_proxy = 0; - - memset( &sa, 0, sizeof sa ); - sa.sun_family = AF_LOCAL; - strlcpy( sa.sun_path, path, sizeof sa.sun_path ); - - fd = socket( AF_UNIX, SOCK_STREAM, 0 ); - if( 0 > fd ) - { - errnomsg( "failed to create socket" ); - return -1; - } - - if( 0 > connect( fd, ( struct sockaddr * )&sa, SUN_LEN( &sa ) ) ) - { - errnomsg( "failed to connect to socket file: %s", path ); - close( fd ); - return -1; - } - con = calloc( 1, sizeof *con ); - if( NULL == con ) - { - mallocmsg( sizeof *con ); - close( fd ); - return -1; - } - con->ipc = ipc_newcon( gl_tree ); - if( NULL == con->ipc ) - { - close( fd ); - free( con ); - } - con->infd = fd; - con->evin = bufferevent_new( fd, canread, didwrite, ohshit, con ); - if( NULL == con->evin ) - { - errnomsg( "failed to create bufferevent" ); - close( fd ); - ipc_freecon( con->ipc ); - free( con ); - return -1; - } - con->outfd = con->infd; - con->evout = con->evin; - /* XXX bufferevent_base_set( gl_base, con->evin ); */ - bufferevent_settimeout( con->evin, SERVER_TIMEOUT, SERVER_TIMEOUT ); - bufferevent_enable( con->evin, EV_READ ); - if( 0 > sendvers( con ) ) - { - exit( 1 ); - } - - return 0; -} - -int -client_new_cmd( char * const * cmd ) -{ - struct con * con; - int tocmd[2], fromcmd[2]; - pid_t kid; - - assert( NULL != gl_base ); - assert( 0 > gl_proxy ); - assert( NULL != cmd && NULL != cmd[0] ); - - gl_proxy = 1; - - if( 0 > pipe( tocmd ) ) - { - errnomsg( "failed to create pipe" ); - return -1; - } - - if( 0 > pipe( fromcmd ) ) - { - errnomsg( "failed to create pipe" ); - close( tocmd[0] ); - close( tocmd[1] ); - return -1; - } - - kid = fork(); - if( 0 > kid ) - { - close( tocmd[0] ); - close( tocmd[1] ); - close( fromcmd[0] ); - close( fromcmd[1] ); - return -1; - } - else if( 0 == kid ) - { - if( 0 > dup2( tocmd[0], STDIN_FILENO ) || - 0 > dup2( fromcmd[1], STDOUT_FILENO ) ) - { - errnomsg( "failed to duplicate descriptors" ); - _exit( 1 ); - } - close( tocmd[0] ); - close( tocmd[1] ); - close( fromcmd[0] ); - close( fromcmd[1] ); - execvp( cmd[0], cmd ); - errnomsg( "failed to execute: %s", cmd[0] ); - _exit( 1 ); - } - - close( tocmd[0] ); - close( fromcmd[1] ); - - con = calloc( 1, sizeof *con ); - if( NULL == con ) - { - mallocmsg( sizeof *con ); - close( tocmd[1] ); - close( fromcmd[0] ); - return -1; - } - - con->infd = fromcmd[0]; - con->evin = bufferevent_new( con->infd, canread, noop, ohshit, con ); - if( NULL == con->evin ) - { - free( con ); - close( tocmd[1] ); - close( fromcmd[0] ); - return -1; - } - /* XXX bufferevent_base_set( gl_base, con->evin ); */ - bufferevent_settimeout( con->evin, SERVER_TIMEOUT, SERVER_TIMEOUT ); - bufferevent_enable( con->evin, EV_READ ); - - con->outfd = tocmd[1]; - con->evout = bufferevent_new( con->outfd, noway, didwrite, ohshit, con ); - if( NULL == con->evout ) - { - bufferevent_free( con->evin ); - bufferevent_free( con->evout ); - free( con ); - close( tocmd[1] ); - close( fromcmd[0] ); - return -1; - } - /* XXX bufferevent_base_set( gl_base, con->evout ); */ - bufferevent_settimeout( con->evout, SERVER_TIMEOUT, SERVER_TIMEOUT ); - bufferevent_enable( con->evout, EV_READ ); - - con->ipc = ipc_newcon( gl_tree ); - if( NULL == con->ipc ) - { - bufferevent_free( con->evin ); - bufferevent_free( con->evout ); - free( con ); - close( tocmd[1] ); - close( fromcmd[0] ); - return -1; - } - - if( 0 > sendvers( con ) ) - { - exit( 1 ); - } - - return 0; -} - -struct req * -addreq( enum ipc_msg id, int64_t tag, struct resp ** resp ) -{ - struct req * req; - - assert( ( 0 < tag && NULL != resp ) || ( 0 >= tag && NULL == resp ) ); - - req = calloc( 1, sizeof *req ); - if( NULL == req ) - { - mallocmsg( sizeof *req ); - return NULL; - } - - if( NULL != resp ) - { - *resp = calloc( 1, sizeof **resp ); - if( NULL == *resp ) - { - mallocmsg( sizeof **resp ); - free( req ); - return NULL; - } - (*resp)->tag = tag; - RB_INSERT( resptree, &gl_resps, *resp ); - } - - req->id = id; - req->tag = tag; - SLIST_INSERT_HEAD( &gl_reqs, req, next ); - - return req; -} - -int -client_quit( void ) -{ - return ( NULL == addreq( IPC_MSG_QUIT, -1, NULL ) ? -1 : 0 ); -} - -int -client_addfiles( struct strlist * list ) -{ - struct stritem * ii; - uint8_t * buf; - size_t size; - struct req * req; - - if( gl_proxy ) - { - SLIST_FOREACH( ii, list, next ) - { - buf = readfile( ii->str, &size ); - req = addreq( IPC_MSG_ADDONEFILE, -1, NULL ); - if( NULL == req ) - { - free( buf ); - return -1; - } - req->buf = buf; - req->listlen = size; - } - } - else - { - req = addreq( IPC_MSG_ADDMANYFILES, -1, NULL ); - if( NULL == req ) - { - return -1; - } - - /* XXX need to move arg parsing back here or something */ - req->strs = list; - } - - return 0; -} - -int -client_automap( int automap ) -{ - struct req * req; - - req = addreq( IPC_MSG_AUTOMAP, -1, NULL ); - if( NULL == req ) - { - return -1; - } - - req->num = ( automap ? 1 : 0 ); - - return 0; -} - -int -client_pex( int pex ) -{ - struct req * req; - - req = addreq( IPC_MSG_PEX, -1, NULL ); - if( NULL == req ) - { - return -1; - } - - req->num = ( pex ? 1 : 0 ); - - return 0; -} - -int -client_port( int port ) -{ - struct req * req; - - req = addreq( IPC_MSG_PORT, -1, NULL ); - if( NULL == req ) - { - return -1; - } - - req->num = port; - - return 0; -} - -int -client_downlimit( int limit ) -{ - struct req * req; - - req = addreq( IPC_MSG_DOWNLIMIT, -1, NULL ); - if( NULL == req ) - { - return -1; - } - - req->num = ( 0 > limit ? -1 : limit ); - - return 0; -} - -int -client_uplimit( int limit ) -{ - struct req * req; - - req = addreq( IPC_MSG_UPLIMIT, -1, NULL ); - if( NULL == req ) - { - return -1; - } - - req->num = ( 0 > limit ? -1 : limit ); - - return 0; -} - -int -client_dir( const char * dir ) -{ - struct req * req; - char * dircpy; - - dircpy = strdup( dir ); - if( NULL == dircpy ) - { - mallocmsg( strlen( dir ) ); - return -1; - } - - req = addreq( IPC_MSG_DIR, -1, NULL ); - if( NULL == req ) - { - free( dircpy ); - return -1; - } - - req->str = dircpy; - - return 0; -} - -int -client_crypto( const char * mode ) -{ - struct req * req; - char * modecpy; - - modecpy = strdup( mode ); - if( NULL == modecpy ) - { - mallocmsg( strlen( mode ) ); - return -1; - } - - req = addreq( IPC_MSG_CRYPTO, -1, NULL ); - if( NULL == req ) - { - free( modecpy ); - return -1; - } - - req->str = modecpy; - - return 0; -} - -int -addintlistreq( enum ipc_msg which, size_t len, const int * list ) -{ - struct req * req; - int64_t * duplist; - size_t ii; - - assert( ( 0 == len && NULL == list ) || ( 0 < len && NULL != list ) ); - - duplist = NULL; - if( NULL != list ) - { - duplist = calloc( len, sizeof duplist[0] ); - if( NULL == duplist ) - { - mallocmsg( len * sizeof( duplist[0] ) ); - return -1; - } - } - - req = addreq( which, -1, NULL ); - if( NULL == req ) - { - free( duplist ); - return -1; - } - - for( ii = 0; len > ii; ii++ ) - { - duplist[ii] = list[ii]; - } - req->listlen = len; - req->numlist = duplist; - - return 0; -} - -int -client_start( size_t len, const int * list ) -{ - const enum ipc_msg id = list ? IPC_MSG_START : IPC_MSG_STARTALL; - return addintlistreq( id, len, list ); -} - -int -client_stop( size_t len, const int * list ) -{ - const enum ipc_msg id = list ? IPC_MSG_STOP : IPC_MSG_STOPALL; - return addintlistreq( id, len, list ); -} - -int -client_remove( size_t len, const int * list ) -{ - const enum ipc_msg id = list ? IPC_MSG_REMOVE : IPC_MSG_REMOVEALL; - return addintlistreq( id, len, list ); -} - -int -client_verify( size_t len, const int * list ) -{ - return list ? addintlistreq( IPC_MSG_VERIFY, len, list ) : -1; -} - -int -client_list( cl_infofunc func ) -{ - struct req * req; - struct resp * resp; - - req = addreq( IPC_MSG_GETINFOALL, ++gl_tag, &resp ); - if( NULL == req ) - { - return -1; - } - - resp->infocb = func; - req->types = IPC_INF_NAME | IPC_INF_HASH; - - return 0; -} - -int -client_info( cl_infofunc func ) -{ - struct req * req; - struct resp * resp; - - req = addreq( IPC_MSG_GETINFOALL, ++gl_tag, &resp ); - if( NULL == req ) - { - return -1; - } - - resp->infocb = func; - req->types = IPC_INF_NAME | IPC_INF_HASH | IPC_INF_SIZE; - - return 0; -} - -int -client_hashids( cl_infofunc func ) -{ - struct req * req; - struct resp * resp; - - req = addreq( IPC_MSG_GETINFOALL, ++gl_tag, &resp ); - if( NULL == req ) - { - return -1; - } - - resp->infocb = func; - req->types = IPC_INF_HASH; - - return 0; -} - -int -client_status( cl_statfunc func ) -{ - struct req * req; - struct resp * resp; - - req = addreq( IPC_MSG_GETSTATALL, ++gl_tag, &resp ); - if( NULL == req ) - { - return -1; - } - - resp->statcb = func; - req->types = IPC_ST_STATE | IPC_ST_ETA | IPC_ST_COMPLETED | - IPC_ST_DOWNSPEED | IPC_ST_UPSPEED | IPC_ST_DOWNTOTAL | IPC_ST_UPTOTAL | - IPC_ST_ERROR | IPC_ST_ERRMSG; - - return 0; -} - -void -noop( struct bufferevent * ev UNUSED, void * arg UNUSED ) -{ - /* libevent prior to 1.2 couldn't handle a NULL write callback */ -} - -void -noway( struct bufferevent * evin, void * arg UNUSED ) -{ - /* this shouldn't happen, but let's drain the buffer anyway */ - evbuffer_drain( EVBUFFER_INPUT( evin ), - EVBUFFER_LENGTH( EVBUFFER_INPUT( evin ) ) ); -} - -void -didwrite( struct bufferevent * evout, void * arg ) -{ - struct con * con = arg; - - assert( evout == con->evout ); - flushreqs( con ); -} - -void -ohshit( struct bufferevent * ev UNUSED, short what, void * arg UNUSED ) -{ - if( EVBUFFER_EOF & what ) - { - errmsg( "server closed connection" ); - } - else if( EVBUFFER_TIMEOUT & what ) - { - errmsg( "server connection timed out" ); - } - else if( EVBUFFER_READ & what ) - { - errmsg( "read error on server connection" ); - } - else if( EVBUFFER_WRITE & what ) - { - errmsg( "write error on server connection" ); - } - else if( EVBUFFER_ERROR & what ) - { - errmsg( "error on server connection" ); - } - else - { - errmsg( "unknown error on server connection: 0x%x", what ); - } - exit( 1 ); -} - -void -canread( struct bufferevent * evin, void * arg ) -{ - struct con * con = arg; - uint8_t * buf; - size_t len; - ssize_t res; - - assert( evin == con->evin ); - buf = EVBUFFER_DATA( EVBUFFER_INPUT( evin ) ); - len = EVBUFFER_LENGTH( EVBUFFER_INPUT( evin ) ); - - if( IPC_MIN_MSG_LEN > len ) - { - return; - } - - res = ipc_handleMessages( con->ipc, buf, len, con ); - if( 0 > res ) - { - switch( errno ) - { - case EPERM: - errmsg( "unsupported protocol version" ); - break; - case EINVAL: - errmsg( "protocol parse error" ); - break; - default: - errnomsg( "parsing failed" ); - break; - } - exit( 1 ); - } - - if( 0 < res ) - { - evbuffer_drain( EVBUFFER_INPUT( evin ), res ); - flushreqs( con ); - } -} - -void -flushreqs( struct con * con ) -{ - struct req * req; - uint8_t * buf; - size_t buflen, ii; - benc_val_t pk, * val; - struct stritem * jj; - - if( !ipc_hasvers( con->ipc ) ) - { - return; - } - - if( SLIST_EMPTY( &gl_reqs ) && RB_EMPTY( &gl_resps ) ) - { - exit( 0 ); - } - - while( !SLIST_EMPTY( &gl_reqs ) ) - { - req = SLIST_FIRST( &gl_reqs ); - SLIST_REMOVE_HEAD( &gl_reqs, next ); - buf = NULL; - switch( req->id ) - { - case IPC_MSG_QUIT: - case IPC_MSG_STARTALL: - case IPC_MSG_STOPALL: - case IPC_MSG_REMOVEALL: - buf = ipc_mkempty( con->ipc, &buflen, req->id, req->tag ); - break; - case IPC_MSG_ADDMANYFILES: - ii = 0; - SLIST_FOREACH( jj, req->strs, next ) - { - ii++; - } - val = ipc_initval( con->ipc, req->id, -1, &pk, TYPE_LIST ); - if( NULL != val && !tr_bencListReserve( val, ii ) ) - { - SLIST_FOREACH( jj, req->strs, next ) - { - tr_bencInitStr( tr_bencListAdd( val ), - jj->str, -1, 1 ); - } - buf = ipc_serialize( &pk, &buflen ); - SAFEBENCFREE( &pk ); - } - SAFEFREESTRLIST( req->strs ); - break; - case IPC_MSG_ADDONEFILE: - val = ipc_initval( con->ipc, req->id, -1, &pk, TYPE_DICT ); - if( NULL != val && !tr_bencDictReserve( val, 1 ) ) - { - tr_bencInitStr( tr_bencDictAdd( val, "data" ), - req->buf, req->listlen, 1 ); - buf = ipc_serialize( &pk, &buflen ); - SAFEBENCFREE( &pk ); - } - SAFEFREE( req->buf ); - break; - case IPC_MSG_AUTOMAP: - case IPC_MSG_PORT: - case IPC_MSG_DOWNLIMIT: - case IPC_MSG_UPLIMIT: - case IPC_MSG_PEX: - buf = ipc_mkint( con->ipc, &buflen, req->id, -1, req->num ); - break; - case IPC_MSG_CRYPTO: - case IPC_MSG_DIR: - buf = ipc_mkstr( con->ipc, &buflen, req->id, -1, req->str ); - SAFEFREE( req->str ); - break; - case IPC_MSG_START: - case IPC_MSG_STOP: - case IPC_MSG_REMOVE: - case IPC_MSG_VERIFY: - val = ipc_initval( con->ipc, req->id, -1, &pk, TYPE_LIST ); - if( NULL != val && !tr_bencListReserve( val, req->listlen ) ) - { - for( ii = 0; ii < req->listlen; ii++ ) - { - tr_bencInitInt( tr_bencListAdd( val ), - req->numlist[ii] ); - } - buf = ipc_serialize( &pk, &buflen ); - SAFEBENCFREE( &pk ); - } - SAFEFREE( req->numlist ); - break; - case IPC_MSG_GETINFOALL: - case IPC_MSG_GETSTATALL: - buf = ipc_createInfoRequest( con->ipc, &buflen, req->id, req->tag, - req->types, NULL ); - break; - default: - assert( 0 ); - return; - } - - SAFEFREE( req ); - if( NULL == buf ) - { - if( EPERM == errno ) - { - errmsg( "message not supported by server" ); - } - else - { - errnomsg( "failed to create message" ); - } - exit( 1 ); - } - if( 0 > bufferevent_write( con->evout, buf, buflen ) ) - { - errmsg( "failed to buffer %zd bytes of data for write", buflen ); - exit( 1 ); - } - free( buf ); - } -} - -int -sendvers( struct con * con ) -{ - uint8_t * buf; - size_t len; - - buf = ipc_mkvers( &len, "Transmission remote" LONG_VERSION_STRING ); - if( NULL == buf ) - { - if( EPERM == errno ) - { - errmsg( "message not supported by server" ); - } - else - { - errnomsg( "failed to create message" ); - } - return -1; - } - - if( 0 > bufferevent_write( con->evout, buf, len ) ) - { - free( buf ); - errmsg( "failed to buffer %i bytes of data for write", ( int )len ); - return -1; - } - - free( buf ); - - return 0; -} - -void -infomsg( enum ipc_msg msgid, benc_val_t * list, int64_t tag, - void * arg UNUSED ) -{ - int ii; - struct cl_info inf; - int64_t id; - struct resp * resp, key; - - assert( IPC_MSG_INFO == msgid ); - - if( !tr_bencIsList( list ) ) - return; - - memset( &key, 0, sizeof key ); - key.tag = tag; - resp = RB_FIND( resptree, &gl_resps, &key ); - if( NULL == resp || NULL == resp->infocb ) - { - return; - } - RB_REMOVE( resptree, &gl_resps, resp ); - - for( ii = 0; list->val.l.count > ii; ii++ ) - { - tr_benc * dict = &list->val.l.vals[ii]; - - if( !tr_bencIsDict( dict ) ) - continue; - - id = getinfoint( msgid, dict, IPC_INF_ID, -1 ); - inf.name = getinfostr( msgid, dict, IPC_INF_NAME, NULL ); - inf.hash = getinfostr( msgid, dict, IPC_INF_HASH, NULL ); - inf.size = getinfoint( msgid, dict, IPC_INF_SIZE, -1 ); - - if( !TORRENT_ID_VALID( id ) ) - { - continue; - } - - inf.id = id; - resp->infocb( &inf ); - } - - cbdone( resp ); - free( resp ); -} - -void -statmsg( enum ipc_msg msgid, benc_val_t * list, int64_t tag, - void * arg UNUSED ) -{ - int ii; - int64_t id; - struct cl_stat st; - struct resp * resp, key; - - assert( IPC_MSG_STAT == msgid ); - - if( !tr_bencIsList( list ) ) - return; - - memset( &key, 0, sizeof key ); - key.tag = tag; - resp = RB_FIND( resptree, &gl_resps, &key ); - if( NULL == resp || NULL == resp->statcb ) - { - return; - } - RB_REMOVE( resptree, &gl_resps, resp ); - - for( ii = 0; list->val.l.count > ii; ii++ ) - { - tr_benc * dict = &list->val.l.vals[ii]; - - if( !tr_bencIsDict( dict ) ) - continue; - - id = getinfoint( msgid, dict, IPC_ST_ID, -1 ); - st.state = getinfostr( msgid, dict, IPC_ST_STATE, NULL ); - st.eta = getinfoint( msgid, dict, IPC_ST_ETA, -1 ); - st.done = getinfoint( msgid, dict, IPC_ST_COMPLETED, -1 ); - st.ratedown = getinfoint( msgid, dict, IPC_ST_DOWNSPEED, -1 ); - st.rateup = getinfoint( msgid, dict, IPC_ST_UPSPEED, -1 ); - st.totaldown = getinfoint( msgid, dict, IPC_ST_DOWNTOTAL, -1 ); - st.totalup = getinfoint( msgid, dict, IPC_ST_UPTOTAL, -1 ); - st.error = getinfostr( msgid, dict, IPC_ST_ERROR, NULL ); - st.errmsg = getinfostr( msgid, dict, IPC_ST_ERRMSG, NULL ); - - if( !TORRENT_ID_VALID( id ) ) - { - continue; - } - - st.id = id; - resp->statcb( &st ); - } - - cbdone( resp ); - free( resp ); -} - -void -defmsg( enum ipc_msg msgid, benc_val_t * val, int64_t tag, void * arg UNUSED ) -{ - struct resp * resp, key; - - switch( msgid ) - { - case IPC_MSG_FAIL: - if( TYPE_STR == val->type && NULL != val->val.s.s ) - { - errmsg( "request failed: %s", val->val.s.s ); - } - else - { - errmsg( "request failed" ); - } - break; - case IPC_MSG_NOTSUP: - errmsg( "request message not supported" ); - break; - default: - break; - } - - memset( &key, 0, sizeof key ); - key.tag = tag; - resp = RB_FIND( resptree, &gl_resps, &key ); - if( NULL == resp ) - { - return; - } - RB_REMOVE( resptree, &gl_resps, resp ); - - cbdone( resp ); - free( resp ); -} - -void -cbdone( struct resp * resp ) -{ - if( resp->infocb ) - { - resp->infocb( NULL ); - } - else if( resp->statcb ) - { - resp->statcb( NULL ); - } -} - -int64_t -getinfoint( enum ipc_msg msgid, benc_val_t * dict, int type, int64_t defval ) -{ - const char * key = ipc_infoname( msgid, type ); - benc_val_t * val = tr_bencDictFind( dict, key ); - return tr_bencIsInt( val ) ? val->val.i : defval; -} - -char * -getinfostr( enum ipc_msg msgid, benc_val_t * dict, int type, char * defval ) -{ - const char * key = ipc_infoname( msgid, type ); - benc_val_t * val = tr_bencDictFind( dict, key ); - return tr_bencIsString( val ) ? val->val.s.s : defval; -} diff --git a/daemon/client.h b/daemon/client.h deleted file mode 100644 index 7f253a72f..000000000 --- a/daemon/client.h +++ /dev/null @@ -1,77 +0,0 @@ -/****************************************************************************** - * $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. - *****************************************************************************/ - -#ifndef TR_DAEMON_CLIENT_H -#define TR_DAEMON_CLIENT_H - -struct event_base; -struct strlist; - -struct cl_info -{ - int id; - const char * name; - const char * hash; - int64_t size; -}; - -struct cl_stat -{ - int id; - const char * state; - int64_t eta; - int64_t done; - int64_t ratedown; - int64_t rateup; - int64_t totaldown; - int64_t totalup; - const char * error; - const char * errmsg; -}; - -typedef void ( * cl_infofunc )( const struct cl_info * ); -typedef void ( * cl_statfunc )( const struct cl_stat * ); - -int client_init ( struct event_base * ); -int client_new_sock ( const char * ); -int client_new_cmd ( char * const * ); -int client_quit ( void ); -int client_addfiles ( struct strlist * ); -int client_port ( int ); -int client_automap ( int ); -int client_pex ( int ); -int client_downlimit( int ); -int client_uplimit ( int ); -int client_dir ( const char * ); -int client_crypto ( const char * ); -int client_start ( size_t, const int * ); -int client_stop ( size_t, const int * ); -int client_remove ( size_t, const int * ); -int client_verify ( size_t, const int * ); -int client_list ( cl_infofunc ); -int client_info ( cl_infofunc ); -int client_hashids ( cl_infofunc ); -int client_status ( cl_statfunc ); - -#endif /* TR_DAEMON_CLIENT_H */ diff --git a/daemon/daemon.c b/daemon/daemon.c index 914faf90a..d442b4942 100644 --- a/daemon/daemon.c +++ b/daemon/daemon.c @@ -1,350 +1,294 @@ -/****************************************************************************** - * $Id$ +/* + * This file Copyright (C) 2008 Charles Kerr * - * Copyright (c) 2007 Joshua Elsasser + * This file is licensed by the GPL version 2. Works owned by the + * Transmission project are granted a special exemption to clause 2(b) + * so that the bulk of its code can remain under the MIT license. + * This exemption does not extend to derived works not owned by + * the Transmission project. * - * 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 -#include -#include -#include -#include -#include -#include + * $Id:$ + */ + #include #include -#include +#include /* printf */ +#include /* exit, atoi */ +#include /* strcmp */ + +#include /* open */ #include #include -#include -#include -#include -#include -#include - -#include -#include -#include +#include /* daemon, getcwd */ + +#include +#include +#include +#include /* tr_strdup */ #include -#include "errors.h" -#include "misc.h" -#include "server.h" -#include "torrents.h" - -static void usage ( const char *, ... ); -static void readargs ( int, char **, int *, int *, char **, char **, char ** ); -static int trylocksock ( const char * configdir, const char * sockpath ); -static int getsock ( const char * ); -static void exitcleanup ( void ); -static void setupsigs ( struct event_base * ); -static void gotsig ( int, short, void * ); -static int savepid ( const char * ); - -static char gl_lockpath[MAXPATHLEN] = ""; -static int gl_sockfd = -1; -static char gl_sockpath[MAXPATHLEN] = ""; -static char gl_pidfile[MAXPATHLEN] = ""; +#define MY_NAME "transmission-daemon" -int -main( int argc, char ** argv ) -{ - struct event_base * evbase; - int nofork, debug, sockfd; - char * configdir, * sockpath, * pidfile; - - setmyname( argv[0] ); - readargs( argc, argv, &nofork, &debug, &configdir, &sockpath, &pidfile ); +static int closing = FALSE; +static tr_handle * gl_session; +static char gl_configfile[MAX_PATH_LENGTH]; - if( !nofork ) { - if( 0 > daemon( 1, 0 ) ) { - errnomsg( "failed to daemonize" ); - exit( 1 ); - } - errsyslog( 1 ); - } - - if( configdir == NULL ) - configdir = (char*) tr_getDefaultConfigDir( ); - - atexit( exitcleanup ); - sockfd = trylocksock( configdir, sockpath ); - if( 0 > sockfd ) - { - exit( 1 ); - } - if( NULL != pidfile && 0 > savepid( pidfile ) ) - { - exit( 1 ); +static void +saveState( tr_handle * h ) +{ + tr_benc d; + const char * str; + + tr_bencInitDict( &d, 12 ); + tr_bencDictAddStr( &d, "download-dir", tr_sessionGetDownloadDir( h ) ); + tr_bencDictAddInt( &d, "peer-limit", tr_sessionGetPeerLimit( h ) ); + tr_bencDictAddInt( &d, "pex-allowed", tr_sessionIsPexEnabled( h ) ); + tr_bencDictAddInt( &d, "port", tr_sessionGetPublicPort( h ) ); + tr_bencDictAddInt( &d, "port-forwarding-enabled", + tr_sessionIsPortForwardingEnabled( h ) ); + tr_bencDictAddStr( &d, "rpc-acl", tr_sessionGetRPCACL( h ) ); + tr_bencDictAddInt( &d, "rpc-port", tr_sessionGetRPCPort( h ) ); + tr_bencDictAddInt( &d, "speed-limit-up", + tr_sessionGetSpeedLimit( h, TR_UP ) ); + tr_bencDictAddInt( &d, "speed-limit-up-enabled", + tr_sessionIsSpeedLimitEnabled( h, TR_UP ) ); + tr_bencDictAddInt( &d, "speed-limit-down", + tr_sessionGetSpeedLimit( h, TR_DOWN ) ); + tr_bencDictAddInt( &d, "speed-limit-down-enabled", + tr_sessionIsSpeedLimitEnabled( h, TR_DOWN ) ); + switch( tr_sessionGetEncryption( h ) ) { + case TR_PLAINTEXT_PREFERRED: str = "tolerated"; break; + case TR_ENCRYPTION_REQUIRED: str = "required"; break; + default: str = "preferred"; break; } + tr_bencDictAddStr( &d, "encryption", str ); - evbase = event_init(); - setupsigs( evbase ); - torrent_init( configdir, evbase ); - server_init( evbase ); - server_debug( debug ); - server_listen( sockfd ); + tr_ninf( MY_NAME, "saving \"%s\"\n", gl_configfile ); + tr_bencSaveFile( gl_configfile, &d ); - event_base_dispatch( evbase ); + tr_bencFree( &d ); - return 1; } -void -usage( const char * msg, ... ) +static void +session_init( const char * configDir, int rpc_port, const char * rpc_acl ) { - va_list ap; - - if( NULL != msg ) + tr_benc state; + int have_state; + int64_t peer_port = TR_DEFAULT_PORT; + int64_t peers = TR_DEFAULT_GLOBAL_PEER_LIMIT; + int64_t pex_enabled = TR_DEFAULT_PEX_ENABLED; + int64_t fwd_enabled = TR_DEFAULT_PORT_FORWARDING_ENABLED; + int64_t up_limit = 100; + int64_t up_limited = FALSE; + int64_t down_limit = 100; + int64_t down_limited = FALSE; + int encryption = TR_ENCRYPTION_PREFERRED; + char downloadDir[MAX_PATH_LENGTH] = { '\0' }; + const char * rpc_acl_fallback = TR_DEFAULT_RPC_ACL; + int64_t rpc_port_fallback = TR_DEFAULT_RPC_PORT; + tr_ctor * ctor; + tr_torrent ** torrents; + + assert( !gl_session ); + + if(( have_state = !tr_bencLoadFile( gl_configfile, &state ))) { - printf( "%s: ", getmyname() ); - va_start( ap, msg ); - vprintf( msg, ap ); - va_end( ap ); - printf( "\n" ); + const char * str; + tr_ninf( MY_NAME, "loading settings from \"%s\"", gl_configfile ); + + if( tr_bencDictFindStr( &state, "download-dir", &str ) ) + tr_strlcpy( downloadDir, str, sizeof( downloadDir ) ); + tr_bencDictFindInt( &state, "port", &peer_port ); + tr_bencDictFindInt( &state, "port-forwarding-enabled", &fwd_enabled ); + tr_bencDictFindInt( &state, "peer-limit", &peers ); + tr_bencDictFindInt( &state, "pex-allowed", &pex_enabled ); + tr_bencDictFindStr( &state, "rpc-acl", &rpc_acl_fallback ); + tr_bencDictFindInt( &state, "rpc-port", &rpc_port_fallback ); + tr_bencDictFindInt( &state, "speed-limit-down", &down_limit ); + tr_bencDictFindInt( &state, "speed-limit-down-enabled", &down_limited ); + tr_bencDictFindInt( &state, "speed-limit-up", &up_limit ); + tr_bencDictFindInt( &state, "speed-limit-up-enabled", &up_limited ); + if( tr_bencDictFindStr( &state, "encryption", &str ) ) { + if( !strcmp( str, "required" ) ) + encryption = TR_ENCRYPTION_REQUIRED; + else if( !strcmp( str, "tolerated" ) ) + encryption = TR_PLAINTEXT_PREFERRED; + } } - printf( - "usage: %s [-dfh] [-p file] [-s file]\n" - "\n" - "Transmission %s http://www.transmissionbt.com/\n" - "A fast and easy BitTorrent client\n" - "\n" - " -d --debug Print data send and received, implies -f\n" - " -f --foreground Run in the foreground and log to stderr\n" - " -g --config-dir Where to look for configuration files\n" - " -h --help Display this message and exit\n" - " -p --pidfile Save the process id in a file at \n" - " -s --socket Place the socket file at \n" - "\n" - "To add torrents or set options, use the transmission-remote program.\n", - getmyname(), LONG_VERSION_STRING ); + /* fallbacks */ + if( !*downloadDir ) + getcwd( downloadDir, sizeof( downloadDir ) ); + if( rpc_port < 1 ) + rpc_port = rpc_port_fallback; + if( !rpc_acl || !*rpc_acl ) + rpc_acl = rpc_acl_fallback; + + /* start the session */ + gl_session = tr_sessionInitFull( configDir, "daemon", downloadDir, + pex_enabled, fwd_enabled, peer_port, + encryption, + up_limit, up_limited, + down_limit, down_limited, + peers, + TR_MSG_INF, 0, + FALSE, /* is the blocklist enabled? */ + TR_DEFAULT_PEER_SOCKET_TOS, + TRUE, rpc_port, rpc_acl ); + + /* load the torrents */ + ctor = tr_ctorNew( gl_session ); + torrents = tr_sessionLoadTorrents( gl_session, ctor, NULL ); + tr_free( torrents ); + tr_ctorFree( ctor ); + + if( have_state ) + tr_bencFree( &state ); +} + +static void +daemonUsage( void ) +{ + puts( "usage: " MY_NAME " [-dfh] [-p file] [-s file]\n" + "\n" + "Transmission " LONG_VERSION_STRING " http://www.transmissionbt.com/\n" + "A fast and easy BitTorrent client\n" + "\n" + " -a --acl Access Control List. (Default: "TR_DEFAULT_RPC_ACL")\n" + " -f --foreground Run in the foreground and log to stderr\n" + " -g --config-dir Where to look for torrents and daemon-config.benc\n" + " -h --help Display this message and exit\n" + " -p --port n Port to listen to for requests (Default: "TR_DEFAULT_RPC_PORT_STR")\n" + "\n" + MY_NAME" is a headless Transmission session\n" + "that can be controlled via transmission-remote or Clutch.\n" ); exit( 0 ); } -void -readargs( int argc, char ** argv, int * nofork, int * debug, - char ** configdir, char ** sock, char ** pidfile ) +static void +readargs( int argc, char ** argv, + int * nofork, int * port, char ** acl, + char ** configDir ) { - char optstr[] = "dfg:hp:s:"; - struct option longopts[] = - { - { "debug", no_argument, NULL, 'd' }, - { "foreground", no_argument, NULL, 'f' }, - { "config-dir", required_argument, NULL, 'g' }, - { "help", no_argument, NULL, 'h' }, - { "pidfile", required_argument, NULL, 'p' }, - { "socket", required_argument, NULL, 's' }, - { NULL, 0, NULL, 0 } - }; int opt; - - *nofork = 0; - *debug = 0; - *sock = NULL; - *pidfile = NULL; - *configdir = NULL; - - while( 0 <= ( opt = getopt_long( argc, argv, optstr, longopts, NULL ) ) ) { + char optstr[] = "a:fg:hp:"; + struct option longopts[] = { + { "acl", required_argument, NULL, 'a' }, + { "foreground", no_argument, NULL, 'f' }, + { "config-dir", required_argument, NULL, 'g' }, + { "help", no_argument, NULL, 'h' }, + { "port", required_argument, NULL, 'p' }, + { NULL, 0, NULL, '\0' } + }; + while((( opt = getopt_long( argc, argv, optstr, longopts, NULL ))) != -1 ) { switch( opt ) { - case 'd': *debug = 1; /* FALLTHROUGH */ + case 'a': *acl = tr_strdup( optarg ); break; case 'f': *nofork = 1; break; - case 'g': *configdir = strdup( optarg ); break; - case 'p': *pidfile = optarg; break; - case 's': *sock = optarg; break; - default: usage( NULL ); break; + case 'g': *configDir = tr_strdup( optarg ); break; + case 'p': *port = atoi( optarg ); break; + default: daemonUsage( ); break; } } } -static int -getlock( const char * filename ) +static void +gotsig( int sig UNUSED ) { - const int state = tr_lockfile( filename ); - const int success = state == TR_LOCKFILE_SUCCESS; + closing = TRUE; +} - if( !success ) switch( state ) { - case TR_LOCKFILE_EOPEN: - errnomsg( "failed to open file: %s", filename ); - break; - case TR_LOCKFILE_ELOCK: - errmsg( "another copy of %s is already running", getmyname() ); +#if !defined(HAVE_DAEMON) +static int +daemon( int nochdir, int noclose ) +{ + switch( fork( ) ) { + case 0: break; + case -1: + tr_nerr( MY_NAME, "Error daemonizing (fork)! %d - %s\n", errno, strerror(errno) ); + return -1; default: - errmsg( "unhandled tr_lockfile error: %d", state ); - break; + _exit(0); } - return success; -} - - -int -trylocksock( const char * configdir, const char * sockpath ) -{ - int fd; - char path[MAXPATHLEN]; - - confpath( path, sizeof path, configdir, NULL, CONF_PATH_TYPE_DAEMON ); - if( tr_mkdirp( path, 0777 ) && EEXIST != errno ) - { - errnomsg( "failed to create directory: %s", path ); + if( setsid() < 0 ) { + tr_nerr( MY_NAME, "Error daemonizing (setsid)! %d - %s\n", errno, strerror(errno) ); return -1; } - confpath( path, sizeof path, configdir, CONF_FILE_LOCK, 0 ); - if( !getlock( path ) ) - return -1; - strlcpy( gl_lockpath, path, sizeof gl_lockpath ); - - if( !sockpath ) - { - confpath( path, sizeof path, configdir, CONF_FILE_SOCKET, 0 ); - sockpath = path; - } - fd = getsock( sockpath ); - if( 0 > fd ) - { - return -1; + switch( fork( ) ) { + case 0: + break; + case -1: + tr_nerr( MY_NAME, "Error daemonizing (fork2)! %d - %s\n", errno, strerror(errno) ); + return -1; + default: + _exit(0); } - gl_sockfd = fd; - strlcpy( gl_sockpath, sockpath, sizeof gl_sockpath ); - return fd; -} - -int -getsock( const char * path ) -{ - struct sockaddr_un sa; - int fd; - - fd = socket( PF_LOCAL, SOCK_STREAM, 0 ); - if( 0 > fd ) - { - errnomsg( "failed to create socket file: %s", path ); + if( !nochdir && 0 > chdir( "/" ) ) { + tr_nerr( MY_NAME, "Error daemonizing (chdir)! %d - %s\n", errno, strerror(errno) ); return -1; } - memset( &sa, 0, sizeof sa ); - sa.sun_family = AF_LOCAL; - strlcpy( sa.sun_path, path, sizeof sa.sun_path ); - unlink( path ); - if( 0 > bind( fd, ( struct sockaddr * )&sa, SUN_LEN( &sa ) ) ) - { - /* bind can sometimes fail on the first call */ - unlink( path ); - if( 0 > bind( fd, ( struct sockaddr * )&sa, SUN_LEN( &sa ) ) ) - { - errnomsg( "failed to bind socket file: %s", path ); + if( !noclose ) { + int fd; + if((( fd = open("/dev/null", O_RDONLY))) != 0 ) { + dup2( fd, 0 ); + close( fd ); + } + if((( fd = open("/dev/null", O_WRONLY))) != 1 ) { + dup2( fd, 1 ); + close( fd ); + } + if((( fd = open("/dev/null", O_WRONLY))) != 2 ) { + dup2( fd, 2 ); close( fd ); - return -1; } } - return fd; -} - -void -exitcleanup( void ) -{ - if( 0 <= gl_sockfd ) - { - unlink( gl_sockpath ); - close( gl_sockfd ); - } - if( 0 != gl_pidfile[0] ) - { - unlink( gl_pidfile ); - } - - if( *gl_lockpath ) - unlink( gl_lockpath ); + return 0; } +#endif -void -setupsigs( struct event_base * base ) +int +main( int argc, char ** argv ) { - static struct event ev_int; - static struct event ev_quit; - static struct event ev_term; - - signal_set( &ev_int, SIGINT, gotsig, NULL ); - event_base_set( base, &ev_int ); - signal_add( &ev_int, NULL ); - - signal_set( &ev_quit, SIGQUIT, gotsig, NULL ); - event_base_set( base, &ev_quit ); - signal_add( &ev_quit, NULL ); - - signal_set( &ev_term, SIGTERM, gotsig, NULL ); - event_base_set( base, &ev_term ); - signal_add( &ev_term, NULL ); - + int nofork = 0; + int port = TR_DEFAULT_RPC_PORT; + char * configDir = NULL; + char * acl = NULL; + + signal( SIGINT, gotsig ); + signal( SIGQUIT, gotsig ); + signal( SIGTERM, gotsig ); signal( SIGPIPE, SIG_IGN ); signal( SIGHUP, SIG_IGN ); -} -void -gotsig( int sig, short what UNUSED, void * arg UNUSED ) -{ - static int exiting = 0; + readargs( argc, argv, &nofork, &port, &acl, &configDir ); + if( configDir == NULL ) + configDir = tr_strdup_printf( "%s-daemon", tr_getDefaultConfigDir() ); + tr_buildPath( gl_configfile, sizeof( gl_configfile ), + configDir, "daemon-config.benc", NULL ); - if( !exiting ) - { - exiting = 1; - errmsg( "received fatal signal %i, attempting to exit cleanly", sig ); - server_quit(); - } - else - { - errmsg( "received fatal signal %i while exiting, exiting immediately", - sig ); - signal( sig, SIG_DFL ); - raise( sig ); + if( !nofork ) { + if( 0 > daemon( 1, 0 ) ) { + fprintf( stderr, "failed to daemonize: %s\n", strerror( errno ) ); + exit( 1 ); + } } -} - -int -savepid( const char * file ) -{ - FILE * pid; - pid = fopen( file, "wb" ); - if( NULL == pid ) - { - errnomsg( "failed to open pid file: %s", file ); - return -1; - } + session_init( configDir, port, acl ); - if( 0 > fprintf( pid, "%d\n", (int) getpid() ) ) - { - errnomsg( "failed to write pid to file: %s", file ); - fclose( pid ); - unlink( file ); - return -1; - } + while( !closing ) + sleep( 1 ); - fclose( pid ); - strlcpy( gl_pidfile, file, sizeof gl_pidfile ); + saveState( gl_session ); + printf( "Closing transmission session..." ); + tr_sessionClose( gl_session ); + printf( " done.\n" ); + tr_free( configDir ); return 0; } diff --git a/daemon/errors.c b/daemon/errors.c deleted file mode 100644 index 892fb8c7b..000000000 --- a/daemon/errors.c +++ /dev/null @@ -1,112 +0,0 @@ -/****************************************************************************** - * $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 -#include -#include -#include -#include -#include - -#include "errors.h" - -static void verrmsg( int, const char *, va_list ); - -static int gl_syslog = 0; - -void -errsyslog( int useit ) -{ - gl_syslog = useit; - - if( useit ) - { - openlog( getmyname(), 0, LOG_DAEMON ); - } -} - -void -errmsg( const char * fmt, ... ) -{ - va_list ap; - - va_start( ap, fmt ); - verrmsg( -1, fmt, ap ); - va_end( ap ); -} - -void -errnomsg( const char * fmt, ... ) -{ - va_list ap; - - va_start( ap, fmt ); - verrmsg( errno, fmt, ap ); - va_end( ap ); -} - -void -verrmsg( int errnum, const char * fmt, va_list ap ) -{ - char buf[1024]; - - vsnprintf( buf, sizeof buf, fmt, ap ); - - if( gl_syslog ) - { - if( 0 > errnum ) - { - syslog( LOG_ERR, "%s", buf ); - } - else - { - syslog( LOG_ERR, "%s: %s", buf, strerror( errnum ) ); - } - } - else - { - if( 0 > errnum ) - { - fprintf( stderr, "%s: %s\n", getmyname(), buf ); - } - else - { - fprintf( stderr, "%s: %s: %s\n", getmyname(), buf, - strerror( errnum ) ); - } - } -} - -void -mallocmsg( size_t size ) -{ - if( 0 < size ) - { - errmsg( "failed to allocate %i bytes of memory", ( int )size ); - } - else - { - errmsg( "failed to allocate memory" ); - } -} diff --git a/daemon/errors.h b/daemon/errors.h deleted file mode 100644 index 8cc546570..000000000 --- a/daemon/errors.h +++ /dev/null @@ -1,35 +0,0 @@ -/****************************************************************************** - * $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. - *****************************************************************************/ - -#ifndef TR_DAEMON_ERRORS_H -#define TR_DAEMON_ERRORS_H - -#include "misc.h" - -void errsyslog( int ); -void errmsg( const char *, ... ) PRINTF( 1, 2 ); -void errnomsg( const char *, ... ) PRINTF( 1, 2 ); -void mallocmsg( size_t ); - -#endif /* TR_DAEMON_ERRORS_H */ diff --git a/daemon/misc.c b/daemon/misc.c deleted file mode 100644 index d2695054d..000000000 --- a/daemon/misc.c +++ /dev/null @@ -1,188 +0,0 @@ -/****************************************************************************** - * $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 -#include -#include -#include -#include -#include /* basename */ -#include -#include -#include -#include - -#include -#include -#include /* tr_loadFile() */ - -#include "errors.h" -#include "misc.h" - -static char gl_myname[256]; - -void -setmyname( const char * argv0 ) -{ - char tmp[MAX_PATH_LENGTH]; - strlcpy( tmp, argv0, sizeof( tmp ) ); - strlcpy( gl_myname, basename( tmp ), sizeof( gl_myname ) ); -} - -const char * -getmyname( void ) -{ - return gl_myname; -} - -void -confpath( char * buf, - size_t len, - const char * configDir, - const char * file, - enum confpathtype type ) -{ - switch( type ) - { - case CONF_PATH_TYPE_DAEMON: - tr_buildPath( buf, len, configDir, "daemon", file, NULL ); - break; - case CONF_PATH_TYPE_GTK: - tr_buildPath( buf, len, configDir, "gtk", file, NULL ); - break; - case CONF_PATH_TYPE_OSX: - tr_buildPath( buf, len, configDir, file, NULL ); - break; - default: - assert( 0 ); - break; - } -} - -void -absolutify( char * buf, size_t len, const char * path ) -{ - if( *path == '/' ) - strlcpy( buf, path, len ); - else { - char cwd[MAX_PATH_LENGTH]; - getcwd( cwd, sizeof( cwd ) ); - tr_buildPath( buf, len, cwd, path, NULL ); - } -} - -int -writefile( const char * name, uint8_t * buf, ssize_t len ) -{ - int fd; - ssize_t res; - - fd = open( name, O_WRONLY | O_CREAT | O_TRUNC, 0666 ); - if( 0 > fd ) - { - errnomsg( "Couldn't open \"%1$s\": %2$s", name, tr_strerror( errno ) ); - return -1; - } - - res = write( fd, buf, len ); - if( 0 > res ) - { - errnomsg( "failed to write to %s", name ); - return -1; - } - if( len > res ) - { - errmsg( "failed to write all data to %s", name ); - return -1; - } - - close( fd ); - - return 0; -} - -uint8_t * -readfile( const char * filename, size_t * len ) -{ - return tr_loadFile( filename, len ); -} - -#ifndef HAVE_DAEMON - -int -daemon( int nochdir, int noclose ) -{ - switch( fork( ) ) { - case 0: - break; - case -1: - fprintf( stderr, "Error daemonizing (fork)! %d - %s\n", errno, strerror(errno) ); - return -1; - default: - _exit(0); - } - - if( setsid() < 0 ) { - fprintf( stderr, "Error daemonizing (setsid)! %d - %s\n", errno, strerror(errno) ); - return -1; - } - - switch( fork( ) ) { - case 0: - break; - case -1: - fprintf( stderr, "Error daemonizing (fork2)! %d - %s\n", errno, strerror(errno) ); - return -1; - default: - _exit(0); - } - - if( !nochdir && 0 > chdir( "/" ) ) { - fprintf( stderr, "Error daemonizing (chdir)! %d - %s\n", errno, strerror(errno) ); - return -1; - } - - if( !noclose ) { - int fd; - fd = open("/dev/null", O_RDONLY); - if( fd != 0 ) { - dup2( fd, 0 ); - close( fd ); - } - fd = open("/dev/null", O_WRONLY); - if( fd != 1 ) { - dup2( fd, 1 ); - close( fd ); - } - fd = open("/dev/null", O_WRONLY); - if( fd != 2 ) { - dup2( fd, 2 ); - close( fd ); - } - } - - return 0; -} - -#endif diff --git a/daemon/misc.h b/daemon/misc.h deleted file mode 100644 index 1e2ad5b78..000000000 --- a/daemon/misc.h +++ /dev/null @@ -1,148 +0,0 @@ -/****************************************************************************** - * $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. - *****************************************************************************/ - -#ifndef TR_DAEMON_MISC_H -#define TR_DAEMON_MISC_H - -#include -#include -#include - -#include "bsdqueue.h" - -#define CONF_FILE_LOCK "lock" -#define CONF_FILE_SOCKET "socket" -#define CONF_FILE_STATE "state" - -enum confpathtype -{ - CONF_PATH_TYPE_DAEMON, - CONF_PATH_TYPE_GTK, - CONF_PATH_TYPE_OSX, -}; - -struct bufferevent; - -#ifdef __GNUC__ -# define UNUSED __attribute__((unused)) -#else -# define UNUSED -#endif - -#ifdef __GNUC__ -# define PRINTF( fmt, args ) __attribute__((format (printf, fmt, args))) -#else -# define PRINTF( fmt, args ) -#endif - -#define ARRAYLEN( ary ) ( sizeof( ary ) / sizeof( (ary)[0] ) ) - -#ifndef MIN -#define MIN( aa, bb ) ( (aa) < (bb) ? (aa) : (bb) ) -#endif -#ifndef MAX -#define MAX( aa, bb ) ( (aa) > (bb) ? (aa) : (bb) ) -#endif - -#undef NULL -#define NULL ( ( void * )0 ) - -#ifndef AF_LOCAL -#define AF_LOCAL AF_UNIX -#endif - -#ifndef PF_LOCAL -#define PF_LOCAL PF_UNIX -#endif - -#ifndef SUN_LEN -#define SUN_LEN( sun ) \ - ( sizeof( *(sun) ) - sizeof( (sun)->sun_path ) + strlen( (sun)->sun_path ) ) -#endif - -#define SAFEFREE( ptr ) \ - do \ - { \ - int saved = errno; \ - free( ptr ); \ - errno = saved; \ - } \ - while( 0 ) -#define SAFEFREESTRLIST( ptr ) \ - do \ - { \ - int saved = errno; \ - FREESTRLIST( ptr ); \ - errno = saved; \ - } \ - while( 0 ) -#define SAFEBENCFREE( val ) \ - do \ - { \ - int saved = errno; \ - tr_bencFree( val ); \ - errno = saved; \ - } \ - while( 0 ) - -#define INTCMP_FUNC( name, type, id ) \ -static int \ -name( struct type * _icf_first, struct type * _icf_second ) \ -{ \ - if( _icf_first->id < _icf_second->id ) \ - return -1; \ - if( _icf_first->id > _icf_second->id ) \ - return 1; \ - return 0; \ -} - -struct stritem -{ - char * str; - SLIST_ENTRY( stritem ) next; -}; - -SLIST_HEAD( strlist, stritem ); - -#define FREESTRLIST( _fl_head ) \ - while( !SLIST_EMPTY( _fl_head ) ) \ - { \ - struct stritem * _fl_dead = SLIST_FIRST( _fl_head ); \ - SLIST_REMOVE_HEAD( _fl_head, next ); \ - free( _fl_dead->str ); \ - free( _fl_dead ); \ - } - -void setmyname ( const char * ); -const char * getmyname ( void ); -void confpath ( char *, size_t, const char *, const char *, enum confpathtype ); -void absolutify( char *, size_t, const char * ); -int writefile ( const char *, uint8_t *, ssize_t ); -uint8_t * readfile ( const char *, size_t * ); - -#ifndef HAVE_DAEMON -int daemon( int, int ); -#endif - -#endif /* TR_DAEMON_MISC_H */ diff --git a/daemon/proxy.c b/daemon/proxy.c deleted file mode 100644 index b85904610..000000000 --- a/daemon/proxy.c +++ /dev/null @@ -1,359 +0,0 @@ -/****************************************************************************** - * $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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "errors.h" -#include "misc.h" - -static void usage ( const char *, ... ); -static enum confpathtype readargs ( int, char **, char **, char ** ); -static int makesock ( enum confpathtype, const char *, const char * ); -static void inread ( struct bufferevent *, void * ); -static void noop ( struct bufferevent *, void * ); -static void inerr ( struct bufferevent *, short, void * ); -static void wtf ( struct bufferevent *, void * ); -static void outerr ( struct bufferevent *, short, void * ); -static void sockread ( struct bufferevent *, void * ); -static void sockwrite( struct bufferevent *, void * ); -static void sockerr ( struct bufferevent *, short, void * ); - -static struct bufferevent * gl_in = NULL; -static struct bufferevent * gl_out = NULL; -static struct bufferevent * gl_sock = NULL; - -int -main( int argc, char ** argv ) -{ - struct event_base * base; - enum confpathtype type; - char * configdir; - char * sockpath; - int sockfd; - - setmyname( argv[0] ); - type = readargs( argc, argv, &configdir, &sockpath ); - if( configdir == NULL ) - configdir = strdup( tr_getDefaultConfigDir( ) ); - - base = event_init(); - - sockfd = makesock( type, configdir, sockpath ); - if( 0 > sockfd ) - { - return EXIT_FAILURE; - } - - gl_in = bufferevent_new( STDIN_FILENO, inread, noop, inerr, NULL ); - if( NULL == gl_in ) - { - errnomsg( "failed to set up event buffer for stdin" ); - return EXIT_FAILURE; - } - /* XXX bufferevent_base_set( base, gl_in ); */ - bufferevent_enable( gl_in, EV_READ ); - bufferevent_disable( gl_in, EV_WRITE ); - - gl_out = bufferevent_new( STDOUT_FILENO, wtf, noop, outerr, NULL ); - if( NULL == gl_in ) - { - errnomsg( "failed to set up event buffer for stdin" ); - return EXIT_FAILURE; - } - /* XXX bufferevent_base_set( base, gl_out ); */ - bufferevent_disable( gl_out, EV_READ ); - bufferevent_enable( gl_out, EV_WRITE ); - - gl_sock = bufferevent_new( sockfd, sockread, sockwrite, sockerr, NULL ); - if( NULL == gl_in ) - { - errnomsg( "failed to set up event buffer for stdin" ); - return EXIT_FAILURE; - } - /* XXX bufferevent_base_set( base, gl_sock ); */ - bufferevent_enable( gl_sock, EV_READ ); - bufferevent_enable( gl_sock, EV_WRITE ); - - event_dispatch(); - /* XXX event_base_dispatch( base ); */ - - return EXIT_FAILURE; -} - -void -usage( const char * msg, ... ) -{ - va_list ap; - - if( NULL != msg ) - { - printf( "%s: ", getmyname() ); - va_start( ap, msg ); - vprintf( msg, ap ); - va_end( ap ); - printf( "\n" ); - } - - printf( - "usage: %s [options] [files]...\n" - "\n" - "Transmission %s http://www.transmissionbt.com/\n" - "A fast and easy BitTorrent client\n" - "\n" - " -h --help Display this message and exit\n" - " -g --config-dir Where to look for configuration files\n" - " -t --type daemon Use the daemon frontend, transmission-daemon\n" - " -t --type gtk Use the GTK+ frontend, transmission\n" - " -t --type mac Use the Mac OS X frontend\n", - getmyname(), LONG_VERSION_STRING ); - exit( EXIT_SUCCESS ); -} - -enum confpathtype -readargs( int argc, char ** argv, char ** configdir, char ** sockpath ) -{ - char optstr[] = "g:ht:"; - struct option longopts[] = - { - { "config-dir", required_argument, NULL, 'g' }, - { "help", no_argument, NULL, 'h' }, - { "type", required_argument, NULL, 't' }, - { NULL, 0, NULL, 0 } - }; - enum confpathtype type; - int opt; - - type = CONF_PATH_TYPE_DAEMON; - *configdir = NULL; - *sockpath = NULL; - - while( 0 <= ( opt = getopt_long( argc, argv, optstr, longopts, NULL ) ) ) - { - switch( opt ) - { - case 'g': - *configdir = strdup( optarg ); - break; - - case 't': - if( 0 == strcasecmp( "daemon", optarg ) ) - { - type = CONF_PATH_TYPE_DAEMON; - *sockpath = NULL; - } - else if( 0 == strcasecmp( "gtk", optarg ) ) - { - type = CONF_PATH_TYPE_GTK; - *sockpath = NULL; - } - else if( 0 == strcasecmp( "mac", optarg ) || - 0 == strcasecmp( "osx", optarg ) || - 0 == strcasecmp( "macos", optarg ) || - 0 == strcasecmp( "macosx", optarg ) ) - { - type = CONF_PATH_TYPE_OSX; - *sockpath = NULL; - } - else - { - *sockpath = optarg; - } - break; - default: - usage( NULL ); - break; - } - } - - return type; -} - -int -makesock( enum confpathtype type, - const char * configdir, - const char * sockpath ) -{ - struct sockaddr_un sa; - int fd; - - memset( &sa, 0, sizeof sa ); - sa.sun_family = AF_LOCAL; - if( !sockpath ) - { - confpath( sa.sun_path, sizeof sa.sun_path, configdir, CONF_FILE_SOCKET, type ); - } - else - { - strlcpy( sa.sun_path, sockpath, sizeof sa.sun_path ); - } - - fd = socket( AF_UNIX, SOCK_STREAM, 0 ); - if( 0 > fd ) - { - errnomsg( "failed to create socket" ); - return -1; - } - - if( 0 > connect( fd, ( struct sockaddr * )&sa, SUN_LEN( &sa ) ) ) - { - errnomsg( "failed to connect to socket file: %s", sa.sun_path ); - close( fd ); - return -1; - } - - return fd; -} - -void -inread( struct bufferevent * ev UNUSED, void * arg UNUSED ) -{ - bufferevent_write_buffer( gl_sock, EVBUFFER_INPUT( gl_in ) ); -} - -void -noop( struct bufferevent * ev UNUSED, void * arg UNUSED ) -{ -} - -void -inerr( struct bufferevent * ev UNUSED, short what, void * arg UNUSED ) -{ - if( EVBUFFER_EOF & what ) - { - bufferevent_free( gl_in ); - gl_in = NULL; - sockwrite( NULL, NULL ); - return; - } - - if( EVBUFFER_TIMEOUT & what ) - { - errmsg( "timed out reading from stdin" ); - } - else if( EVBUFFER_READ & what ) - { - errmsg( "read error on stdin" ); - } - else if( EVBUFFER_ERROR & what ) - { - errmsg( "error on stdin" ); - } - else - { - errmsg( "unknown error on stdin: 0x%x", what ); - } - - exit( EXIT_FAILURE ); -} - -void -wtf( struct bufferevent * ev, void * arg UNUSED ) -{ - /* this shouldn't happen, but let's drain the buffer anyway */ - evbuffer_drain( EVBUFFER_INPUT( ev ), - EVBUFFER_LENGTH( EVBUFFER_INPUT( ev ) ) ); -} - -void -outerr( struct bufferevent * ev UNUSED, short what, void * arg UNUSED ) -{ - if( EVBUFFER_TIMEOUT & what ) - { - errmsg( "timed out writing to stdout" ); - } - else if( EVBUFFER_WRITE & what ) - { - errmsg( "write error on stdout" ); - } - else if( EVBUFFER_ERROR & what ) - { - errmsg( "error on client stdout" ); - } - else - { - errmsg( "unknown error on stdout connection: 0x%x", what ); - } - - exit( EXIT_FAILURE ); -} - -void -sockread( struct bufferevent * ev UNUSED, void * arg UNUSED ) -{ - bufferevent_write_buffer( gl_out, EVBUFFER_INPUT( gl_sock ) ); -} - -void -sockwrite( struct bufferevent * ev UNUSED, void * arg UNUSED ) -{ - if( NULL == gl_in && 0 == EVBUFFER_LENGTH( EVBUFFER_OUTPUT( gl_sock ) ) ) - { - exit( EXIT_SUCCESS ); - } -} - -void -sockerr( struct bufferevent * ev UNUSED, short what, void * arg UNUSED ) -{ - if( EVBUFFER_EOF & what ) - { - errmsg( "server closed connection" ); - } - else if( EVBUFFER_TIMEOUT & what ) - { - errmsg( "server connection timed out" ); - } - else if( EVBUFFER_READ & what ) - { - errmsg( "read error on server connection" ); - } - else if( EVBUFFER_WRITE & what ) - { - errmsg( "write error on server connection" ); - } - else if( EVBUFFER_ERROR & what ) - { - errmsg( "error on server connection" ); - } - else - { - errmsg( "unknown error on server connection: 0x%x", what ); - } - - exit( EXIT_FAILURE ); -} diff --git a/daemon/remote.c b/daemon/remote.c index e8e1bc5d9..e04cf28bf 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -1,279 +1,112 @@ -/****************************************************************************** - * $Id$ +/* + * This file Copyright (C) 2008 Charles Kerr * - * Copyright (c) 2007 Joshua Elsasser + * This file is licensed by the GPL version 2. Works owned by the + * Transmission project are granted a special exemption to clause 2(b) + * so that the bulk of its code can remain under the MIT license. + * This exemption does not extend to derived works not owned by + * the Transmission project. * - * 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. - *****************************************************************************/ + * $Id:$ + */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include -#include -#include - -#include -#include -#include +#include +#include /* strcmp */ -#include "bsdtree.h" -#include "bsdqueue.h" -#include "client.h" -#include "errors.h" -#include "misc.h" +#include +#include /* getcwd */ -#define BESTDECIMAL(d) (10.0 > (d) ? 2 : (100.0 > (d) ? 1 : 0)) +#include +#include -struct opts -{ - int proxy; - char ** proxycmd; - enum confpathtype type; - const char * sock; - struct strlist files; - int sendquit; - int port; - int map; - int uplimit; - int up; - int downlimit; - int down; - int listquick; - int listfull; - struct strlist verify; - int startall; - struct strlist start; - int stopall; - struct strlist stop; - int removeall; - struct strlist remove; - char dir[MAXPATHLEN]; - int pex; - const char * crypto; - const char * configdir; -}; +#include +#include +#include +#include +#include +#include -struct torinfo -{ - int id; - int infogood; - int statgood; - char * name; - int64_t size; - char * state; - int64_t eta; - int64_t done; - int64_t ratedown; - int64_t rateup; - int64_t totaldown; - int64_t totalup; - char * errorcode; - char * errormsg; - RB_ENTRY( torinfo ) idlinks; - RB_ENTRY( torinfo ) namelinks; -}; +#define MY_NAME "transmission-remote" +#define DEFAULT_HOST "localhost" +#define DEFAULT_PORT TR_DEFAULT_RPC_PORT -RB_HEAD( torlist, torinfo ); -RB_HEAD( tornames, torinfo ); +enum { TAG_LIST }; -struct torhash +static void +showUsage( void ) { - char hash[SHA_DIGEST_LENGTH*2+1]; - int id; - RB_ENTRY( torhash ) link; -}; - -RB_HEAD( torhashes, torhash ); - -static void usage ( const char *, ... ); -static int readargs ( int, char **, struct opts * ); -static int numarg ( const char * ); -static int hasharg ( const char *, struct strlist *, int * ); -static int fileargs ( struct strlist *, int, char * const * ); -static void listmsg ( const struct cl_info * ); -static void infomsg ( const struct cl_info * ); -static void statmsg ( const struct cl_stat * ); -static void hashmsg ( const struct cl_info * ); -static float fmtsize ( int64_t, const char ** ); -static char * strdup_noctrl( const char * ); -static void print_eta ( int64_t ); -static void printlisting ( void ); -static int sendidreqs ( void ); -static int toridcmp ( struct torinfo *, struct torinfo * ); -static int tornamecmp ( struct torinfo *, struct torinfo * ); -static int torhashcmp ( struct torhash *, struct torhash * ); - -static struct torlist gl_torinfo = RB_INITIALIZER( &gl_torinfo ); -static struct strlist * gl_starthashes = NULL; -static struct strlist * gl_stophashes = NULL; -static struct strlist * gl_removehashes = NULL; -static struct strlist * gl_verifyhashes = NULL; -static struct torhashes gl_hashids = RB_INITIALIZER( &gl_hashids ); -static int gl_gotlistinfo = 0; -static int gl_gotliststat = 0; - -RB_GENERATE_STATIC( torlist, torinfo, idlinks, toridcmp ) -RB_GENERATE_STATIC( tornames, torinfo, namelinks, tornamecmp ) -RB_GENERATE_STATIC( torhashes, torhash, link, torhashcmp ) + puts( "Transmission "LONG_VERSION_STRING" http://www.transmissionbt.com/\n" + "A fast and easy BitTorrent client\n" + "\n" + "Usage: "MY_NAME" [host] [options]\n" + " "MY_NAME" [port] [options]\n" + " "MY_NAME" [host:port] [options]\n" + "\n" + "Options:\n" + " -a --add Add a torrent\n" + " -c --encryption required Require encryption for all peers\n" + " -c --encryption preferred Prefer peers to use encryption\n" + " -c --encryption tolerated Prefer peers to use plaintext\n" + " -d --download-limit Max download rate in KiB/s\n" + " -D --download-unlimited No download rate limit\n" + " -e --enable-pex Enable peer exchange\n" + " -E --disable-pex Disable peer exchange\n" + " -f --folder Folder to set for new torrents\n" + " -g --debug Print debugging information\n" + " -h --help Display this message and exit\n" + " -l --list Long list of all torrent and status\n" + " -m --port-mapping Automatic port mapping via NAT-PMP or UPnP\n" + " -M --no-port-mapping Disable automatic port mapping\n" + " -p --port Port to listen for incoming peers\n" + " -r --remove Remove the torrent with the given ID\n" + " -r --remove all Remove all torrents\n" + " -s --start Start the torrent with the given ID\n" + " -s --start all Start all stopped torrents\n" + " -S --stop Stop the torrent with the given ID\n" + " -S --stop all Stop all running torrents\n" + " -u --upload-limit Max upload rate in KiB/s\n" + " -U --upload-unlimited No upload rate limit\n" + " -v --verify Verify the torrent's local data\n" ); + exit( 0 ); +} -int -main( int argc, char ** argv ) +static int +numarg( const char * arg ) { - struct event_base * evbase; - struct opts o; - - setmyname( argv[0] ); - - if( 0 > readargs( argc, argv, &o ) ) - exit( 1 ); - - if( o.configdir == NULL ) - o.configdir = strdup( tr_getDefaultConfigDir( ) ); - - signal( SIGPIPE, SIG_IGN ); - - evbase = event_init(); - client_init( evbase ); - - if( o.proxy ) - { - client_new_cmd( o.proxycmd ); - } - else - { - if( o.sock ) - client_new_sock( o.sock ); - else { - char sockpath[MAXPATHLEN]; - confpath( sockpath, sizeof sockpath, o.configdir, CONF_FILE_SOCKET, o.type ); - client_new_sock( sockpath ); - } - } - - if( ( o.sendquit && 0 > client_quit ( ) ) || - ( '\0' != o.dir[0] && 0 > client_dir ( o.dir ) ) || - ( !SLIST_EMPTY( &o.files ) && 0 > client_addfiles ( &o.files ) ) || - ( o.crypto && 0 > client_crypto ( o.crypto ) ) || - ( o.startall && 0 > client_start ( 0, NULL ) ) || - ( o.stopall && 0 > client_stop ( 0, NULL ) ) || - ( o.removeall && 0 > client_remove ( 0, NULL ) ) || - ( o.port && 0 > client_port ( o.port ) ) || - ( 0 <= o.map && 0 > client_automap ( o.map ) ) || - ( 0 <= o.pex && 0 > client_pex ( o.pex ) ) || - ( o.uplimit && 0 > client_uplimit ( o.up ) ) || - ( o.downlimit && 0 > client_downlimit( o.down ) ) || - ( o.listquick && 0 > client_list ( listmsg ) ) || - ( o.listfull && ( 0 > client_info ( infomsg ) || - 0 > client_status ( statmsg ) ) ) ) - { - exit( 1 ); - } - - if( ( !o.startall && !SLIST_EMPTY( &o.start ) ) || - ( !o.stopall && !SLIST_EMPTY( &o.stop ) ) || - ( !SLIST_EMPTY( &o.verify ) ) || - ( !o.removeall && !SLIST_EMPTY( &o.remove ) ) ) - { - if( 0 > client_hashids( hashmsg ) ) - { - exit( 1 ); - } - gl_starthashes = ( o.startall ? NULL : &o.start ); - gl_stophashes = ( o.stopall ? NULL : &o.stop ); - gl_removehashes = ( o.removeall ? NULL : &o.remove ); - gl_verifyhashes = ( &o.verify ); + char * end = NULL; + const long num = strtol( arg, &end, 10 ); + if( *end ) { + fprintf( stderr, "Not a number: \"%s\"\n", arg ); + showUsage( ); } - - event_dispatch(); - /* event_base_dispatch( evbase ); */ - - return 1; + return num; } -void -usage( const char * msg, ... ) -{ - va_list ap; - - if( NULL != msg ) - { - printf( "%s: ", getmyname() ); - va_start( ap, msg ); - vprintf( msg, ap ); - va_end( ap ); - printf( "\n" ); - } +static char * reqs[256]; /* arbitrary max */ +static int reqCount = 0; +static int debug = 0; - printf( - "usage: %s [options] [files...]\n" - " %s -x [options] proxy-command [args...]\n" - "\n" - "Transmission %s http://www.transmissionbt.com/\n" - "A fast and easy BitTorrent client\n" - "\n" - " -a --add Add a torrent\n" - " -c --encryption required Require encryption for all peers\n" - " -c --encryption preferred Prefer peers to use encryption\n" - " -c --encryption tolerated Prefer peers to use plaintext\n" - " -d --download-limit Max download rate in KiB/s\n" - " -D --download-unlimited No download rate limit\n" - " -e --enable-pex Enable peer exchange\n" - " -E --disable-pex Disable peer exchange\n" - " -f --folder Folder to set for new torrents\n" - " -g --config-dir Where to look for configuration files\n" - " -h --help Display this message and exit\n" - " -i --info List all torrents with info hashes\n" - " -l --list List all torrents with status\n" - " -m --port-mapping Automatic port mapping via NAT-PMP or UPnP\n" - " -M --no-port-mapping Disable automatic port mapping\n" - " -p --port Port to listen for incoming connections on\n" - " -q --quit Quit the daemon\n" - " -r --remove Remove the torrent with the given hash\n" - " -r --remove all Remove all torrents\n" - " -s --start Start the torrent with the given hash\n" - " -s --start all Start all stopped torrents\n" - " -S --stop Stop the torrent with the given hash\n" - " -S --stop all Stop all running torrents\n" - " -t --type daemon Use the daemon frontend, transmission-daemon\n" - " -t --type gtk Use the GTK+ frontend, transmission\n" - " -t --type mac Use the MacOS X frontend\n" - " -u --upload-limit Max upload rate in KiB/s\n" - " -U --upload-unlimited No upload rate limit\n" - " -v --verify Verify the torrent's local data\n" - " -x --proxy Use proxy command to connect to frontend\n", - getmyname(), getmyname(), LONG_VERSION_STRING ); - exit( 0 ); +static char* +absolutify( char * buf, size_t len, const char * path ) +{ + if( *path == '/' ) + tr_strlcpy( buf, path, len ); + else { + char cwd[MAX_PATH_LENGTH]; + getcwd( cwd, sizeof( cwd ) ); + tr_buildPath( buf, len, cwd, path, NULL ); + } + return buf; } -int -readargs( int argc, char ** argv, struct opts * opts ) +static void +readargs( int argc, char ** argv ) { - char optstr[] = "a:c:d:DeEf:g:hilmMp:qr:s:S:t:u:Uxv:"; - struct option longopts[] = + int opt; + char optstr[] = "a:c:d:DeEf:ghlmMp:r:s:S:u:Uv:"; + + const struct option longopts[] = { { "add", required_argument, NULL, 'a' }, { "encryption", required_argument, NULL, 'c' }, @@ -282,829 +115,216 @@ readargs( int argc, char ** argv, struct opts * opts ) { "enable-pex", no_argument, NULL, 'e' }, { "disable-pex", no_argument, NULL, 'E' }, { "folder", required_argument, NULL, 'f' }, - { "config-dir", required_argument, NULL, 'g' }, + { "debug", no_argument, NULL, 'g' }, { "help", no_argument, NULL, 'h' }, - { "info", no_argument, NULL, 'i' }, { "list", no_argument, NULL, 'l' }, { "port-mapping", no_argument, NULL, 'm' }, { "no-port-mapping", no_argument, NULL, 'M' }, { "port", required_argument, NULL, 'p' }, - { "quit", no_argument, NULL, 'q' }, { "remove", required_argument, NULL, 'r' }, { "start", required_argument, NULL, 's' }, { "stop", required_argument, NULL, 'S' }, - { "type", required_argument, NULL, 't' }, { "upload-limit", required_argument, NULL, 'u' }, { "upload-unlimited", no_argument, NULL, 'U' }, { "verify", required_argument, NULL, 'v' }, - { "proxy", no_argument, NULL, 'x' }, { NULL, 0, NULL, 0 } }; - int opt, gotmsg; - - gotmsg = 0; - memset( opts, 0, sizeof *opts ); - opts->type = CONF_PATH_TYPE_DAEMON; - SLIST_INIT( &opts->files ); - opts->map = -1; - opts->pex = -1; - opts->configdir = NULL; - SLIST_INIT( &opts->start ); - SLIST_INIT( &opts->stop ); - SLIST_INIT( &opts->remove ); - - while( 0 <= ( opt = getopt_long( argc, argv, optstr, longopts, NULL ) ) ) - { - switch( opt ) - { - case 'a': - if( 0 > fileargs( &opts->files, 1, &optarg ) ) - { - return -1; - } - break; - case 'c': - if(!strcasecmp(optarg, "required")) - opts->crypto = "required"; - else if(!strcasecmp(optarg, "preferred")) - opts->crypto = "preferred"; - else if(!strcasecmp(optarg, "tolerated")) - opts->crypto = "tolerated"; - else - usage("invalid encryption mode: %s", optarg); - break; - case 'd': - opts->downlimit = 1; - opts->down = numarg( optarg ); - break; - case 'D': - opts->downlimit = 1; - opts->down = -1; - break; - case 'e': - opts->pex = 1; - break; - case 'E': - opts->pex = 0; - break; - case 'f': - absolutify( opts->dir, sizeof opts->dir, optarg ); - break; - case 'g': - opts->configdir = strdup( optarg ); - break; - case 'i': - opts->listquick = 1; - break; - case 'l': - opts->listfull = 1; - break; - case 'm': - opts->map = 1; - break; - case 'M': - opts->map = 0; - break; - case 'p': - opts->port = numarg( optarg ); - if( 0 >= opts->port || 0xffff <= opts->port ) - { - usage( "invalid port: %i", opts->port ); - } - break; - case 'q': - opts->sendquit = 1; - break; - case 'r': - if( 0 > hasharg( optarg, &opts->remove, &opts->removeall ) ) - { - return -1; - } - break; - case 's': - if( 0 > hasharg( optarg, &opts->start, &opts->startall ) ) - { - return -1; - } - break; - case 'S': - if( 0 > hasharg( optarg, &opts->stop, &opts->stopall ) ) - { - return -1; - } - break; - case 't': - if( 0 == strcasecmp( "daemon", optarg ) ) - { - opts->type = CONF_PATH_TYPE_DAEMON; - opts->sock = NULL; - } - else if( 0 == strcasecmp( "gtk", optarg ) ) - { - opts->type = CONF_PATH_TYPE_GTK; - opts->sock = NULL; - } - else if( 0 == strcasecmp( "mac", optarg ) || - 0 == strcasecmp( "osx", optarg ) || - 0 == strcasecmp( "macos", optarg ) || - 0 == strcasecmp( "macosx", optarg ) ) - { - opts->type = CONF_PATH_TYPE_OSX; - opts->sock = NULL; - } - else - { - opts->sock = optarg; - } - break; - case 'u': - opts->uplimit = 1; - opts->up = numarg( optarg ); - break; - case 'U': - opts->uplimit = 1; - opts->up = -1; - break; - case 'v': - if( 0 > hasharg( optarg, &opts->verify, NULL ) ) - { - return -1; - } - break; - case 'x': - opts->proxy = 1; - continue; /* don't set gotmsg, -x isn't a message */ - default: - usage( NULL ); - break; - } - gotmsg = 1; - } - - if( !gotmsg && argc == optind ) - { - usage( NULL ); - } - - if( opts->proxy ) - { - if( argc == optind ) - usage( "can't use -x without any arguments" ); - opts->proxycmd = argv + optind; - } - else if( 0 > fileargs( &opts->files, argc - optind, argv + optind ) ) - { - return -1; - } - - return 0; -} - -int -numarg( const char * arg ) -{ - char * end; - long num; - - end = NULL; - num = strtol( arg, &end, 10 ); - if( NULL != end && '\0' != *end ) - { - usage( "not a number: %s", arg ); - return -1; - } - - return num; -} - -int -hasharg( const char * arg, struct strlist * list, int * all ) -{ - struct stritem * listitem; - struct torhash * treeitem, key, * foo; - size_t len, ii; - - /* check for special "all" value */ - if( 0 == strcasecmp( "all", arg ) ) - { - if( all ) - *all = 1; - return 0; - } - - /* check hash length */ - len = strlen( arg ); - if( SHA_DIGEST_LENGTH * 2 != len ) - { - usage( "torrent info hash length is not %i: %s", - SHA_DIGEST_LENGTH * 2, arg ); - return -1; - } - - /* allocate list item */ - listitem = calloc( 1, sizeof *listitem ); - if( NULL == listitem ) - { - mallocmsg( sizeof *listitem ); - return -1; - } - listitem->str = calloc( len + 1, 1 ); - if( NULL == listitem->str ) - { - mallocmsg( len + 1 ); - free( listitem ); - return -1; - } - - /* check that the hash is all hex and copy it in lowercase */ - for( ii = 0; len > ii; ii++ ) - { - if( !isxdigit( arg[ii] ) ) - { - usage( "torrent info hash is not hex: %s", arg ); - free( listitem->str ); - free( listitem ); - return -1; - } - listitem->str[ii] = tolower( arg[ii] ); - } - - /* try to look up the hash in the hash tree */ - memset( &key, 0, sizeof key ); - strlcpy( key.hash, listitem->str, sizeof key.hash ); - treeitem = RB_FIND( torhashes, &gl_hashids, &key ); - if( NULL == treeitem ) - { - /* the hash isn't in the tree, allocate a tree item and insert it */ - treeitem = calloc( 1, sizeof *treeitem ); - if( NULL == treeitem ) - { - mallocmsg( sizeof *treeitem ); - free( listitem->str ); - free( listitem ); - return -1; - } - treeitem->id = -1; - strlcpy( treeitem->hash, listitem->str, sizeof treeitem->hash ); - foo = RB_INSERT( torhashes, &gl_hashids, treeitem ); - assert( NULL == foo ); - } - - /* finally, add the list item to the list */ - SLIST_INSERT_HEAD( list, listitem, next ); - - return 0; -} - -int -fileargs( struct strlist * list, int argc, char * const * argv ) -{ - struct stritem * item; - int ii; - char path[MAXPATHLEN]; - - for( ii = 0; argc > ii; ii++ ) - { - item = calloc( 1, sizeof *item ); - if( NULL == item ) - { - mallocmsg( sizeof *item ); - return -1; - } - if( '/' == argv[ii][0] ) - { - item->str = strdup( argv[ii] ); - if( NULL == item->str ) - { - mallocmsg( strlen( argv[ii] ) + 1 ); - free( item ); - return -1; - } - } - else - { - absolutify( path, sizeof path, argv[ii] ); - item->str = strdup( path ); - if( NULL == item->str ) - { - mallocmsg( strlen( path ) + 1 ); - free( item ); - return -1; - } - } - SLIST_INSERT_HEAD( list, item, next ); - } - - return 0; -} - -void -listmsg( const struct cl_info * inf ) -{ - char * newname; - size_t ii; - - if( NULL == inf ) - { - return; - } - if( NULL == inf->name ) + while((( opt = getopt_long( argc, argv, optstr, longopts, NULL ))) != -1 ) { - errmsg( "missing torrent name from server" ); - return; - } - else if( NULL == inf->hash ) - { - errmsg( "missing torrent hash from server" ); - return; - } - - newname = strdup_noctrl( inf->name ); - if( NULL == newname ) - { - return; - } - - for( ii = 0; '\0' != inf->hash[ii]; ii++ ) - { - if( isxdigit( inf->hash[ii] ) ) - { - putchar( tolower( inf->hash[ii] ) ); - } - } - putchar( ' ' ); - printf( "%s\n", newname ); - free( newname ); -} - -void -infomsg( const struct cl_info * cinfo ) -{ - struct torinfo * tinfo, key; - - gl_gotlistinfo = 1; + char * req = NULL; + char buf[MAX_PATH_LENGTH]; - if( NULL == cinfo ) - { - if( gl_gotliststat ) + switch( opt ) { - printlisting(); + case 'g': debug = 1; break; + case 'h': showUsage( ); break; + case 'a': req = tr_strdup_printf( "method=torrent-add&filename=%s", optarg ); break; + case 'c': req = tr_strdup_printf( "method=session-set&encryption=%s", optarg ); break; + case 'd': req = tr_strdup_printf( "method=session-set&speed-limit-down=%d&speed-limit-down-enabled=1", numarg(optarg) ); break; + case 'D': req = tr_strdup( "method=session-set&speed-limit-down-enabled=0" ); break; + case 'u': req = tr_strdup_printf( "method=session-set&speed-limit-up=%d&speed-limit-up-enabled:1", numarg(optarg) ); break; + case 'U': req = tr_strdup( "method=session-set&speed-limit-up-enabled=0" ); break; + case 'e': req = tr_strdup( "method=session-set&pex-allowed=1" ); break; + case 'E': req = tr_strdup( "method=session-set&pex-allowed=0" ); break; + case 'f': req = tr_strdup_printf( "method=session-set&download-dir=%s", absolutify(buf,sizeof(buf),optarg)); break; + case 'l': req = tr_strdup_printf( "method=torrent-list&tag=%d", TAG_LIST ); break; + case 'm': req = tr_strdup( "method=session-set&port-forwarding-enabled=1" ); break; + case 'M': req = tr_strdup( "method=session-set&port-forwarding-enabled=0" ); break; + case 'p': req = tr_strdup_printf( "method=session-set&port=%d", numarg( optarg ) ); break; + case 'r': req = strcmp( optarg, "all" ) + ? tr_strdup_printf( "method=torrent-remove&ids=%s", optarg ) + : tr_strdup ( "method=torrent-remove" ); break; + case 's': req = strcmp( optarg, "all" ) + ? tr_strdup_printf( "method=torrent-start&ids=%s", optarg ) + : tr_strdup ( "method=torrent-start" ); break; + case 'S': req = strcmp( optarg, "all" ) + ? tr_strdup_printf( "method=torrent-stop&ids=%s", optarg ) + : tr_strdup ( "method=torrent-stop" ); break; + case 'v': req = strcmp( optarg, "all" ) + ? tr_strdup_printf( "method=torrent-verify&ids=%s", optarg ) + : tr_strdup ( "method=torrent-verify" ); break; } - return; - } - - if( NULL == cinfo->name || 0 >= cinfo->size ) - { - errmsg( "missing torrent info from server" ); - return; - } - memset( &key, 0, sizeof key ); - key.id = cinfo->id; - tinfo = RB_FIND( torlist, &gl_torinfo, &key ); - if( NULL == tinfo ) - { - tinfo = calloc( 1, sizeof *tinfo ); - if( NULL == tinfo ) - { - mallocmsg( sizeof *tinfo ); - return; - } - tinfo->id = cinfo->id; - RB_INSERT( torlist, &gl_torinfo, tinfo ); + if( req ) + reqs[reqCount++] = req; } - - tinfo->infogood = 1; - free( tinfo->name ); - tinfo->name = strdup_noctrl( cinfo->name ); - tinfo->size = cinfo->size; } -void -statmsg( const struct cl_stat * st ) +/* [host:port] or [host] or [port] */ +static void +getHostAndPort( int * argc, char ** argv, char ** host, int * port ) { - struct torinfo * info, key; - - gl_gotliststat = 1; - - if( NULL == st ) - { - if( gl_gotlistinfo ) - { - printlisting(); - } - return; - } - - if( NULL == st->state || 0 > st->done || - 0 > st->ratedown || 0 > st->rateup || - 0 > st->totaldown || 0 > st->totalup ) - { - errmsg( "missing torrent status from server" ); - return; - } - - memset( &key, 0, sizeof key ); - key.id = st->id; - info = RB_FIND( torlist, &gl_torinfo, &key ); - if( NULL == info ) - { - info = calloc( 1, sizeof *info ); - if( NULL == info ) - { - mallocmsg( sizeof *info ); - return; + if( *argv[1] != '-' ) + { + int i; + const char * s = argv[1]; + const char * delim = strchr( s, ':' ); + if( delim ) { /* user passed in both host and port */ + *host = tr_strndup( s, delim-s ); + *port = atoi( delim+1 ); + } else { + char * end; + const int i = strtol( s, &end, 10 ); + if( !*end ) /* user passed in a port */ + *port = i; + else /* user passed in a host */ + *host = tr_strdup( s ); } - info->id = st->id; - RB_INSERT( torlist, &gl_torinfo, info ); - } - - info->statgood = 1; - free( info->state ); - info->state = strdup_noctrl( st->state ); - info->eta = st->eta; - info->done = st->done; - info->ratedown = st->ratedown; - info->rateup = st->rateup; - info->totaldown = st->totaldown; - info->totalup = st->totalup; - if( NULL != st->error && '\0' != st->error[0] ) - { - info->errorcode = strdup_noctrl( st->error ); - } - if( NULL != st->errmsg && '\0' != st->errmsg[0] ) - { - info->errormsg = strdup_noctrl( st->errmsg ); - } -} - -void -hashmsg( const struct cl_info * inf ) -{ - struct torhash key, * found; - - if( NULL == inf ) - { - sendidreqs(); - return; - } - - if( NULL == inf->hash ) - { - errmsg( "missing torrent hash from server" ); - return; - } - - memset( &key, 0, sizeof key ); - strlcpy( key.hash, inf->hash, sizeof key.hash ); - found = RB_FIND( torhashes, &gl_hashids, &key ); - if( NULL != found ) - { - found->id = inf->id; - } -} - -float -fmtsize( int64_t num, const char ** units ) -{ - static const char * sizes[] = - { - "B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", - }; - float ret; - size_t ii; - ret = num; - for( ii = 0; ARRAYLEN( sizes ) > ii && 1000.0 < ret; ii++ ) - { - ret /= 1024.0; + *argc -= 1; + for( i=1; i<*argc; ++i ) + argv[i] = argv[i+1]; } - - if( NULL != units ) - { - *units = sizes[ii]; - } - - return ret; } -char * -strdup_noctrl( const char * str ) +static size_t +writeFunc( void * ptr, size_t size, size_t nmemb, void * buf ) { - char * ret; - size_t ii; - - ii = strlen( str ); - ret = malloc( ii + 1 ); - if( NULL == ret ) - { - mallocmsg( ii + 1 ); - return NULL; - } - - for( ii = 0; '\0' != str[ii]; ii++ ) - { - ret[ii] = ( iscntrl( str[ii] ) ? ' ' : str[ii] ); - } - ret[ii] = '\0'; - - return ret; + const size_t byteCount = size * nmemb; + evbuffer_add( buf, ptr, byteCount ); + return byteCount; } -void -print_eta( int64_t secs ) +static const char* +torrentStatusToString( int i ) { - static const struct - { - const char * label; - int64_t div; - } - units[] = - { - { "second", 60 }, - { "minute", 60 }, - { "hour", 24 }, - { "day", 7 }, - { "week", 1 } - }; - int readable[ ARRAYLEN( units ) ]; - int printed; - size_t ii; - - if( 0 > secs ) - { - printf( "stalled" ); - return; - } - - for( ii = 0; ARRAYLEN( units ) > ii; ii++ ) + switch( i ) { - readable[ii] = secs % units[ii].div; - secs /= units[ii].div; - } - readable[ii-1] = MIN( INT_MAX, secs ); - - printf( "done in" ); - for( printed = 0; 0 < ii && 2 > printed; ii-- ) - { - if( 0 != readable[ii-1] || - ( 0 == printed && 1 == ii ) ) - { - printf( " %i %s%s", readable[ii-1], units[ii-1].label, - ( 1 == readable[ii-1] ? "" : "s" ) ); - printed++; - } + case TR_STATUS_CHECK_WAIT: return "Will Verify"; + case TR_STATUS_CHECK: return "Verifying"; + case TR_STATUS_DOWNLOAD: return "Downloading"; + case TR_STATUS_SEED: return "Seeding"; + case TR_STATUS_STOPPED: return "Stopped"; + default: return "Error"; } } -int -sendidreqs( void ) +static void +processResponse( const void * response, size_t len ) { - struct - { - struct strlist * list; - int ( * func )( size_t, const int * ); - } - reqs[] = - { - { gl_starthashes, client_start }, - { gl_stophashes, client_stop }, - { gl_removehashes, client_remove }, - { gl_verifyhashes, client_verify }, - }; - struct stritem * jj; - size_t ii; - int * ids, count, ret; - struct torhash key, * found; + tr_benc top; - ret = -1; - memset( &key, 0, sizeof key ); - - for( ii = 0; ARRAYLEN( reqs ) > ii; ii++) + if( tr_jsonParse( response, len, &top, NULL ) ) + tr_nerr( MY_NAME, "Unable to parse response\n" ); + else { - if( NULL == reqs[ii].list || SLIST_EMPTY( reqs[ii].list ) ) - { - continue; - } - - count = 0; - SLIST_FOREACH( jj, reqs[ii].list, next ) - { - count++; - } - count++; - - ids = calloc( count, sizeof ids[0] ); - if( NULL == ids ) - { - mallocmsg( count * sizeof ids[0] ); - return -1; - } + tr_benc *args, *list; + int64_t tag = -1; + const char * str; + tr_bencDictFindInt( &top, "tag", &tag ); - count = 0; - SLIST_FOREACH( jj, reqs[ii].list, next ) - { - strlcpy( key.hash, jj->str, sizeof key.hash ); - found = RB_FIND( torhashes, &gl_hashids, &key ); - if( NULL != found && TORRENT_ID_VALID( found->id ) ) - { - ids[count] = found->id; - count++; - } - } + if( tr_bencDictFindStr( &top, "result", &str ) ) + printf( "Server responded: \"%s\"\n", str ); - if( 0 < count ) + if( ( tag == TAG_LIST ) && + ( tr_bencDictFindDict( &top, "arguments", &args ) ) && + ( tr_bencDictFindList( args, "list", &list ) ) ) { - if( 0 > reqs[ii].func( count, ids ) ) + int i, n; + for( i=0, n=tr_bencListSize( list ); iinfogood || !ii->statgood ) - { - goto free; - } - - /* massage some numbers into a better format for printing */ - size = fmtsize( ii->size, &units ); - upspeed = fmtsize( ii->rateup, &upunits ); - downspeed = fmtsize( ii->ratedown, &downunits ); - name = ( NULL == ii->name ? "???" : ii->name ); - progress = ( float )ii->done / ( float )ii->size * 100.0; - progress = MIN( 100.0, progress ); - progress = MAX( 0.0, progress ); - - /* print name and size */ - printf( "%s (%.*f %s) - ", name, BESTDECIMAL( size ), size, units ); - - /* print hash check progress */ - if( 0 == strcasecmp( "checking", ii->state ) ) - { - printf( "%.*f%% checking files", - BESTDECIMAL( progress ), progress ); - } - /* print progress */ - else if( 0 == strcasecmp( "waiting to checking", ii->state ) ) - { - printf( "%.*f%% waiting to check files", - BESTDECIMAL( progress ), progress ); - } - /* print download progress, speeds, and eta */ - else if( 0 == strcasecmp( "downloading", ii->state ) ) - { - progress = MIN( 99.0, progress ); - printf( "%.*f%% downloading at %.*f %s/s (UL at %.*f %s/s), ", - BESTDECIMAL( progress ), progress, - BESTDECIMAL( downspeed ), downspeed, downunits, - BESTDECIMAL( upspeed ), upspeed, upunits ); - print_eta( ii->eta ); - } - /* print seeding speed */ - else if( 0 == strcasecmp( "seeding", ii->state ) ) - { - if( 0 == ii->totalup && 0 == ii->totaldown ) - { - printf( "100%% seeding at %.*f %s/s [N/A]", - BESTDECIMAL( upspeed ), upspeed, upunits ); - } - else if( 0 == ii->totaldown ) - { - printf( "100%% seeding at %.*f %s/s [INF]", - BESTDECIMAL( upspeed ), upspeed, upunits ); - } - else - { - ratio = ( float )ii->totalup / ( float )ii->totaldown; - printf( "100%% seeding at %.*f %s/s [%.*f]", - BESTDECIMAL( upspeed ), upspeed, upunits, - BESTDECIMAL( ratio ), ratio ); - } - } - /* print stopping message */ - else if( 0 == strcasecmp( "stopping", ii->state ) ) - { - printf( "%.*f%% stopping...", BESTDECIMAL( progress ), progress ); - } - /* print stopped message with progress */ - else if( 0 == strcasecmp( "paused", ii->state ) ) - { - printf( "%.*f%% stopped", BESTDECIMAL( progress ), progress ); - } - /* unknown status, just print it with progress */ + int i; + CURL * curl; + struct evbuffer * buf = evbuffer_new( ); + + curl = curl_easy_init( ); + curl_easy_setopt( curl, CURLOPT_VERBOSE, debug ); + curl_easy_setopt( curl, CURLOPT_USERAGENT, MY_NAME"/"LONG_VERSION_STRING ); + curl_easy_setopt( curl, CURLOPT_WRITEFUNCTION, writeFunc, 0 ); + curl_easy_setopt( curl, CURLOPT_WRITEDATA, buf, 0 ); + + for( i=0; istate ); - } - - /* print any error */ - if( NULL != ii->errorcode || NULL != ii->errormsg ) - { - if( NULL == ii->errorcode ) - { - printf( " [error - %s]", ii->errormsg ); - } - else if( NULL == ii->errormsg ) - { - printf( " [error (%s)]", ii->errorcode ); - } - else - { - printf( " [error (%s) - %s]", ii->errorcode, ii->errormsg ); - } - } + processResponse( EVBUFFER_DATA( buf ), EVBUFFER_LENGTH( buf ) ); - /* don't forget the newline */ - putchar( '\n' ); - - /* free the stuff for this torrent */ - free: - free( ii->name ); - free( ii->state ); - free( ii->errorcode ); - free( ii->errormsg ); - free( ii ); + evbuffer_drain( buf, EVBUFFER_LENGTH( buf ) ); + tr_free( url ); } + + /* cleanup */ + evbuffer_free( buf ); + curl_easy_cleanup( curl ); } int -tornamecmp( struct torinfo * left, struct torinfo * right ) +main( int argc, char ** argv ) { - int ret; + int i; + int port = DEFAULT_PORT; + char * host = NULL; - /* we know they're equal if they're the same struct */ - if( left == right ) - { - return 0; - } - /* we might be missing a name, fall back on torrent ID */ - else if( NULL == left->name && NULL == right->name ) - { - return toridcmp( left, right ); - } - else if( NULL == left->name ) - { - return -1; - } - else if( NULL == right->name ) - { - return 1; - } + if( argc < 2 ) + showUsage( ); - /* if we have two names, compare them */ - ret = strcasecmp( left->name, right->name ); - if( 0 != ret ) - { - return ret; - } - /* if the names are the same then fall back on the torrent ID, - this is to handle different torrents with the same name */ + getHostAndPort( &argc, argv, &host, &port ); + if( host == NULL ) + host = tr_strdup( DEFAULT_HOST ); + + readargs( argc, argv ); + if( reqCount ) + processRequests( host, port, (const char**)reqs, reqCount ); else - { - return toridcmp( left, right ); - } -} + showUsage( ); -int -torhashcmp( struct torhash * left, struct torhash * right ) -{ - return strcasecmp( left->hash, right->hash ); -} + for( i=0; i -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include /* tr_free */ - -#include "bsdtree.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; - } - - ipc_addmsg( gl_tree, IPC_MSG_ADDMANYFILES, addmsg1 ); - ipc_addmsg( gl_tree, IPC_MSG_ADDONEFILE, addmsg2 ); - ipc_addmsg( gl_tree, IPC_MSG_AUTOMAP, intmsg ); - ipc_addmsg( gl_tree, IPC_MSG_AUTOSTART, intmsg ); - ipc_addmsg( gl_tree, IPC_MSG_CRYPTO, strmsg ); - ipc_addmsg( gl_tree, IPC_MSG_DOWNLIMIT, intmsg ); - ipc_addmsg( gl_tree, IPC_MSG_DIR, strmsg ); - ipc_addmsg( gl_tree, IPC_MSG_GETAUTOMAP, prefmsg ); - ipc_addmsg( gl_tree, IPC_MSG_GETAUTOSTART, prefmsg ); - ipc_addmsg( gl_tree, IPC_MSG_GETCRYPTO, prefmsg ); - ipc_addmsg( gl_tree, IPC_MSG_GETDOWNLIMIT, prefmsg ); - ipc_addmsg( gl_tree, IPC_MSG_GETDIR, prefmsg ); - ipc_addmsg( gl_tree, IPC_MSG_GETINFO, infomsg ); - ipc_addmsg( gl_tree, IPC_MSG_GETINFOALL, infomsg ); - ipc_addmsg( gl_tree, IPC_MSG_GETPEX, prefmsg ); - ipc_addmsg( gl_tree, IPC_MSG_GETPORT, prefmsg ); - ipc_addmsg( gl_tree, IPC_MSG_GETSTAT, infomsg ); - ipc_addmsg( gl_tree, IPC_MSG_GETSTATALL, infomsg ); - ipc_addmsg( gl_tree, IPC_MSG_GETUPLIMIT, prefmsg ); - ipc_addmsg( gl_tree, IPC_MSG_GETSUP, supmsg ); - ipc_addmsg( gl_tree, IPC_MSG_LOOKUP, lookmsg ); - ipc_addmsg( gl_tree, IPC_MSG_NOOP, noopmsg ); - ipc_addmsg( gl_tree, IPC_MSG_PEX, intmsg ); - ipc_addmsg( gl_tree, IPC_MSG_PORT, intmsg ); - ipc_addmsg( gl_tree, IPC_MSG_QUIT, quitmsg ); - ipc_addmsg( gl_tree, IPC_MSG_REMOVE, tormsg ); - ipc_addmsg( gl_tree, IPC_MSG_REMOVEALL, tormsg ); - ipc_addmsg( gl_tree, IPC_MSG_START, tormsg ); - ipc_addmsg( gl_tree, IPC_MSG_STARTALL, tormsg ); - ipc_addmsg( gl_tree, IPC_MSG_STOP, tormsg ); - ipc_addmsg( gl_tree, IPC_MSG_STOPALL, tormsg ); - ipc_addmsg( gl_tree, IPC_MSG_UPLIMIT, intmsg ); - ipc_addmsg( gl_tree, IPC_MSG_VERIFY, tormsg ); - - 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 ) - { - 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_handleMessages( 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 ); - } -} - -static 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; -} - -static int -queuepkmsg( struct client * client, tr_benc * pk ) -{ - size_t buflen; - uint8_t * buf = ipc_serialize( pk, &buflen ); - int ret = queuemsg( client, buf, buflen ); - tr_free( buf ); - return ret; -} - -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; - int ii, tor; - - if( !tr_bencIsList( val ) ) - { - 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++ ) - { - tr_benc * file = &val->val.l.vals[ii]; - if( !tr_bencIsString( file ) ) - 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 ) ) - { - if( 0 > ipc_addinfo( added, tor, torrent_handle( tor ), 0 ) ) - { - errnomsg( "failed to build message" ); - tr_bencFree( &pk ); - byebye( client->ev, EVBUFFER_EOF, NULL ); - return; - } - } - } - - queuepkmsg( client, &pk ); - tr_bencFree( &pk ); -} - -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; - const char * dir; - - if( !tr_bencIsDict( dict ) ) - { - msgresp( client, tag, IPC_MSG_BAD ); - return; - } - - val = tr_bencDictFind( dict, "directory" ); - dir = tr_bencIsString( val ) ? val->val.s.s : NULL; - val = tr_bencDictFind( dict, "autostart" ); - start = tr_bencIsInt( val ) ? (val->val.i!=0) : -1; - val = tr_bencDictFind( dict, "data" ); - if( tr_bencIsString( val ) ) - { - /* 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( !tr_bencIsString( val ) ) - { - 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 ) ) - { - 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; - } - if( 0 > ipc_addinfo( val, tor, torrent_handle( tor ), 0 ) ) - { - errnomsg( "failed to build message" ); - tr_bencFree( &pk ); - byebye( client->ev, EVBUFFER_EOF, NULL ); - return; - } - - queuepkmsg( client, &pk ); - tr_bencFree( &pk ); - } - 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( !tr_bencIsInt( val ) ) - { - 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( !tr_bencIsString( val ) ) - { - msgresp( client, tag, IPC_MSG_BAD ); - return; - } - - switch( id ) - { - case IPC_MSG_CRYPTO: - if( !strcasecmp( val->val.s.s, "required" ) ) - torrent_set_encryption( TR_ENCRYPTION_REQUIRED ); - else if( !strcasecmp( val->val.s.s, "preferred" ) ) - torrent_set_encryption( TR_ENCRYPTION_PREFERRED ); - else if( !strcasecmp( val->val.s.s, "tolerated" ) ) - torrent_set_encryption( TR_PLAINTEXT_PREFERRED ); - 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; - 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( !tr_bencIsList( val ) ) - { - 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( !tr_bencIsDict( val ) ) - { - msgresp( client, tag, IPC_MSG_BAD ); - tr_bencFree( &pk ); - return; - } - typelist = tr_bencDictFind( val, "type" ); - idlist = tr_bencDictFind( val, "id" ); - if( !tr_bencIsList(typelist) || !tr_bencIsList(idlist) ) - { - 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; - } - } - } - - queuepkmsg( client, &pk ); - tr_bencFree( &pk ); -} - -int -addinfo( benc_val_t * list, int id, int types ) -{ - tr_torrent * tor = torrent_handle( id ); - return tor ? ipc_addinfo( list, id, tor, types ) : 0; -} - -int -addstat( benc_val_t * list, int id, int types ) -{ - tr_torrent * tor = torrent_handle( id ); - return tor ? ipc_addstat( list, id, tor, 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; - case IPC_MSG_VERIFY: - all = 0; - func = torrent_verify; - 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( !tr_bencIsList( val ) ) - { - 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; - int ii; - benc_val_t * hash, pk, * pkinf; - int64_t found; - - if( !tr_bencIsList( val ) ) - { - 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++ ) - { - hash = &val->val.l.vals[ii]; - if( !tr_bencIsString(hash) || 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; - } - if( 0 > ipc_addinfo( pkinf, found, torrent_handle( found ), IPC_INF_HASH ) ) - { - errnomsg( "failed to build message" ); - tr_bencFree( &pk ); - byebye( client->ev, EVBUFFER_EOF, NULL ); - return; - } - } - - queuepkmsg( client, &pk ); - tr_bencFree( &pk ); -} - -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_ENCRYPTION_REQUIRED: strval = "required"; break; - case TR_ENCRYPTION_PREFERRED: strval = "preferred"; break; - case TR_PLAINTEXT_PREFERRED: strval = "tolerated"; 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; - int ii; - benc_val_t pk, *pkval; - enum ipc_msg found; - - if( !tr_bencIsList( val ) ) - { - 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++ ) - { - tr_benc * name = &val->val.l.vals[ii]; - if( !tr_bencIsString( name ) ) - { - 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 ); - } - - queuepkmsg( client, &pk ); - tr_bencFree( &pk ); -} diff --git a/daemon/server.h b/daemon/server.h deleted file mode 100644 index 1cfa47532..000000000 --- a/daemon/server.h +++ /dev/null @@ -1,35 +0,0 @@ -/****************************************************************************** - * $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. - *****************************************************************************/ - -#ifndef TR_DAEMON_SERVER_H -#define TR_DAEMON_SERVER_H - -struct event_base; - -int server_init( struct event_base * ); -void server_debug( int ); -int server_listen( int ); -void server_quit( void ); - -#endif /* TR_DAEMON_SERVER_H */ diff --git a/daemon/torrents.c b/daemon/torrents.c deleted file mode 100644 index c19216576..000000000 --- a/daemon/torrents.c +++ /dev/null @@ -1,746 +0,0 @@ -/****************************************************************************** - * $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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "bsdtree.h" -#include "errors.h" -#include "misc.h" -#include "torrents.h" - -#define EXIT_TIMEOUT 10 /* how many seconds to wait on exit */ -#define TIMER_SECS 1 /* timer interval seconds */ -#define TIMER_USECS 0 /* timer interval microseconds */ - -struct tor -{ - int id; - uint8_t hash[SHA_DIGEST_LENGTH]; - tr_torrent * tor; - RB_ENTRY( tor ) idlinks; - RB_ENTRY( tor ) hashlinks; -}; - -RB_HEAD( tortree, tor ); -RB_HEAD( hashtree, tor ); - -static struct tor * opentor ( const char *, const char *, uint8_t *, size_t, - const char *, int start ); -static void closetor ( struct tor *, int ); -static void starttimer ( int ); -static void timerfunc ( int, short, void * ); -static int savestate ( void ); - -static struct event_base * gl_base = NULL; -static tr_handle * gl_handle = NULL; -static struct tortree gl_tree = RB_INITIALIZER( &gl_tree ); -static struct hashtree gl_hashes = RB_INITIALIZER( &gl_hashes ); -static int gl_lastid = 0; -static struct event gl_event; -static time_t gl_exiting = 0; -static int gl_exitval = 0; -static char gl_state[MAXPATHLEN]; -static char gl_newstate[MAXPATHLEN]; - -static int gl_autostart = 1; -static int gl_pex = 1; -static int gl_port = TR_DEFAULT_PORT; -static int gl_mapping = 0; -static int gl_uplimit = -1; -static int gl_downlimit = -1; -static char gl_dir[MAXPATHLEN]; -static tr_encryption_mode gl_crypto = TR_ENCRYPTION_PREFERRED; - -static int -torhashcmp( struct tor * left, struct tor * right ) -{ - return memcmp( left->hash, right->hash, sizeof left->hash ); -} - -RB_GENERATE_STATIC( hashtree, tor, hashlinks, torhashcmp ) - -INTCMP_FUNC( toridcmp, tor, id ) -RB_GENERATE_STATIC( tortree, tor, idlinks, toridcmp ) - -void -torrent_init( const char * configdir, struct event_base * base ) -{ - tr_benc state, * torrents; - int have_state; - assert( !gl_handle && !gl_base ); - - confpath( gl_state, sizeof gl_state, configdir, CONF_FILE_STATE, 0 ); - snprintf( gl_newstate, sizeof( gl_newstate ), "%s.new", gl_state ); - absolutify( gl_dir, sizeof gl_dir, "." ); - - /* initialize the session variables */ - if(( have_state = !tr_bencLoadFile( gl_state, &state ))) - { - int64_t i; - const char * str; - - if( tr_bencDictFindInt( &state, "autostart", &i ) ) - gl_autostart = i != 0; - if( tr_bencDictFindInt( &state, "port", &i ) && ( 0= 0, gl_uplimit, - gl_downlimit >= 0, gl_downlimit, - TR_DEFAULT_GLOBAL_PEER_LIMIT, - TR_MSG_INF, 0, - 0, /* is the blocklist enabled? */ - TR_DEFAULT_PEER_SOCKET_TOS ); - - /* now load the torrents */ - if( have_state && tr_bencDictFindList( &state, "torrents", &torrents ) ) - { - int i, n; - for( i=0, n=tr_bencListSize(torrents); iid; -} - -int -torrent_add_data( uint8_t * data, size_t size, const char * dir, int start ) -{ - struct tor * tor; - - assert( gl_handle ); - assert( !gl_exiting ); - - if( start < 0 ) - start = gl_autostart; - - tor = opentor( NULL, NULL, data, size, dir, start ); - if( !tor ) - return -1; - - savestate(); - - return tor->id; -} - -static struct tor * -idlookup( int id ) -{ - struct tor * found = NULL; - - if( gl_handle && !gl_exiting ) - { - struct tor key; - memset( &key, 0, sizeof key ); - key.id = id; - found = RB_FIND( tortree, &gl_tree, &key ); - } - - return found; -} - -void -torrent_start( int id ) -{ - struct tor * tor = idlookup( id ); - if( tor && !TR_STATUS_IS_ACTIVE( tr_torrentStat( tor->tor )->status ) ) { - tr_torrentStart( tor->tor ); - savestate(); - } - -} - -void -torrent_stop( int id ) -{ - struct tor * tor = idlookup( id ); - if( tor && TR_STATUS_IS_ACTIVE( tr_torrentStat( tor->tor )->status ) ) { - tr_torrentStop( tor->tor ); - savestate( ); - } -} - -void -torrent_verify( int id ) -{ - struct tor * tor = idlookup( id ); - if( tor ) - tr_torrentVerify( tor->tor ); -} - -void -torrent_remove( int id ) -{ - struct tor * tor = idlookup( id ); - if( tor ) { - closetor( tor, 1 ); - savestate(); - } -} - -tr_torrent * -torrent_handle( int id ) -{ - const struct tor * tor = idlookup( id ); - return tor ? tor->tor : NULL; -} - -const tr_info * -torrent_info( int id ) -{ - return tr_torrentInfo( torrent_handle( id ) ); -} - -const tr_stat * -torrent_stat( int id ) -{ - return tr_torrentStat( torrent_handle( id ) ); -} - -static struct tor * -hashlookup( const uint8_t * hash ) -{ - struct tor key, * found; - - memset( &key, 0, sizeof key ); - memcpy( key.hash, hash, sizeof key.hash ); - found = RB_FIND( hashtree, &gl_hashes, &key ); - - return found; -} - -int -torrent_lookup( const uint8_t * hashstr ) -{ - uint8_t hash[SHA_DIGEST_LENGTH]; - size_t ii; - struct tor * tor; - char buf[3]; - - assert( NULL != gl_handle ); - assert( !gl_exiting ); - - memset( buf, 0, sizeof buf ); - for( ii = 0; sizeof( hash ) > ii; ii++ ) - { - if( !isxdigit( hashstr[2*ii] ) || !isxdigit( hashstr[1+2*ii] ) ) - { - return -1; - } - memcpy( buf, &hashstr[2*ii], 2 ); - hash[ii] = strtol( buf, NULL, 16 ); - } - - tor = hashlookup( hash ); - if( NULL == tor ) - { - return -1; - } - - return tor->id; -} - -static struct tor * -iterate( struct tor * tor ) -{ - struct tor * next = NULL; - - if( gl_handle && !gl_exiting ) - next = tor ? RB_NEXT( tortree, &gl_tree, tor ) - : RB_MIN( tortree, &gl_tree ); - - return next; -} - -void * -torrent_iter( void * cur, int * id ) -{ - struct tor * next = iterate( cur ); - if( next ) - *id = next->id; - return next; -} - -void -torrent_exit( int exitval ) -{ - struct tor * tor; - - assert( NULL != gl_handle ); - assert( !gl_exiting ); - gl_exiting = time( NULL ); - gl_exitval = exitval; - - RB_FOREACH( tor, tortree, &gl_tree ) - { - closetor( tor, 0 ); - } - - tr_sessionSetPortForwardingEnabled( gl_handle, 0 ); - starttimer( 1 ); -} - -void -torrent_set_autostart( int autostart ) -{ - assert( NULL != gl_handle ); - assert( !gl_exiting ); - gl_autostart = autostart; - savestate(); -} - -int -torrent_get_autostart( void ) -{ - return gl_autostart; -} - -void -torrent_set_port( int port ) -{ - assert( NULL != gl_handle ); - assert( !gl_exiting ); - if( 0 < port && 0xffff > port ) - { - gl_port = port; - tr_sessionSetPublicPort( gl_handle, port ); - savestate(); - } -} - -int -torrent_get_port( void ) -{ - return gl_port; -} - -void -torrent_set_pex( int pex ) -{ - assert( NULL != gl_handle ); - assert( !gl_exiting ); - - if( gl_pex != pex ) - { - gl_pex = pex; - - tr_sessionSetPexEnabled( gl_handle, gl_pex ); - - savestate( ); - } -} - -int -torrent_get_pex( void ) -{ - return gl_pex; -} - -void -torrent_enable_port_mapping( int automap ) -{ - assert( NULL != gl_handle ); - assert( !gl_exiting ); - gl_mapping = ( automap ? 1 : 0 ); - tr_sessionSetPortForwardingEnabled( gl_handle, gl_mapping ); - savestate(); -} - -int -torrent_get_port_mapping( void ) -{ - return gl_mapping; -} - -void -torrent_set_uplimit( int uplimit ) -{ - assert( NULL != gl_handle ); - assert( !gl_exiting ); - gl_uplimit = uplimit; - tr_sessionSetSpeedLimit( gl_handle, TR_UP, uplimit ); - tr_sessionSetSpeedLimitEnabled( gl_handle, TR_UP, uplimit > 0 ); - savestate(); -} - -int -torrent_get_uplimit( void ) -{ - return gl_uplimit; -} - -void -torrent_set_downlimit( int downlimit ) -{ - assert( NULL != gl_handle ); - assert( !gl_exiting ); - gl_downlimit = downlimit; - tr_sessionSetSpeedLimit( gl_handle, TR_DOWN, downlimit ); - tr_sessionSetSpeedLimitEnabled( gl_handle, TR_DOWN, downlimit > 0 ); - savestate(); -} - -int -torrent_get_downlimit( void ) -{ - return gl_downlimit; -} - -void -torrent_set_directory( const char * path ) -{ - assert( NULL != gl_handle ); - assert( !gl_exiting ); - - absolutify( gl_dir, sizeof gl_dir, path ); - savestate(); -} - -const char * -torrent_get_directory( void ) -{ - return gl_dir; -} - -void -torrent_set_encryption(tr_encryption_mode mode) -{ - tr_sessionSetEncryption( gl_handle, mode ); - gl_crypto = mode; - savestate(); -} - -tr_encryption_mode -torrent_get_encryption(void) -{ - return tr_sessionGetEncryption( gl_handle ); -} - -struct tor * -opentor( const char * path, - const char * hash, - uint8_t * data, - size_t size, - const char * dir, - int start ) -{ - struct tor * tor, * found; - int errcode; - const tr_info * inf; - tr_ctor * ctor; - - assert( (path?1:0) + (hash?1:0) + (data?1:0) == 1 ); - - /* XXX should probably wrap around back to 1 and avoid duplicates */ - if( INT_MAX == gl_lastid ) - { - errmsg( "Congratulations, you're the %ith torrent! Your prize the " - "inability to load any more torrents, enjoy!", INT_MAX ); - return NULL; - } - - tor = calloc( 1, sizeof *tor ); - if( NULL == tor ) - { - mallocmsg( sizeof *tor ); - return NULL; - } - - if( dir == NULL ) - dir = gl_dir; - - ctor = tr_ctorNew( gl_handle ); - tr_ctorSetPaused( ctor, TR_FORCE, !start ); - tr_ctorSetDestination( ctor, TR_FORCE, dir ); - if( path != NULL ) - tr_ctorSetMetainfoFromFile( ctor, path ); - else if( hash != NULL ) - tr_ctorSetMetainfoFromHash( ctor, hash ); - else - tr_ctorSetMetainfo( ctor, data, size ); - tor->tor = tr_torrentNew( gl_handle, ctor, &errcode ); - tr_ctorFree( ctor ); - - if( NULL == tor->tor ) - { - found = NULL; - switch( errcode ) - { - case TR_EINVALID: - if( NULL == path ) - { - errmsg( "invalid torrent file" ); - } - else - { - errmsg( "invalid torrent file: %s", path ); - } - break; - case TR_EDUPLICATE: - /* XXX not yet - found = hashlookup( tor->hash, 1 ); - assert( NULL != found ); - found->deleting = 0; - */ - errmsg( "XXX loaded duplicate torrent" ); - break; - default: - if( NULL == path ) - { - errmsg( "torrent file failed to load" ); - } - else - { - errmsg( "torrent file failed to load: %s", path ); - } - break; - } - free( tor ); - return found; - } - gl_lastid++; - tor->id = gl_lastid; - - assert( sizeof( inf->hash ) == sizeof( tor->hash ) ); - inf = tr_torrentInfo( tor->tor ); - memcpy( tor->hash, inf->hash, sizeof tor->hash ); - - found = RB_INSERT( tortree, &gl_tree, tor ); - assert( NULL == found ); - found = RB_INSERT( hashtree, &gl_hashes, tor ); - assert( NULL == found ); - - return tor; -} - -static void -freetor( struct tor * tor ) -{ - tr_torrentClose( tor->tor ); - RB_REMOVE( tortree, &gl_tree, tor ); - RB_REMOVE( hashtree, &gl_hashes, tor ); - free( tor ); -} - -void -closetor( struct tor * tor, int calltimer ) -{ - if( NULL != tor ) - { - freetor( tor ); - - starttimer( calltimer ); - } -} - -void -starttimer( int callnow ) -{ - if( !evtimer_initialized( &gl_event ) ) - { - evtimer_set( &gl_event, timerfunc, NULL ); - event_base_set( gl_base, &gl_event ); - } - - if( callnow ) - { - timerfunc( -1, EV_TIMEOUT, NULL ); - } -} - -static void -timerfunc( int fd UNUSED, short event UNUSED, void * arg UNUSED ) -{ - struct tor * tor, * next; - const tr_handle_status * hs; - int stillmore; - struct timeval tv; - - /* true if we've still got live torrents... */ - stillmore = tr_torrentCount( gl_handle ) != 0; - - if( gl_exiting ) - { - if( !stillmore ) - { - hs = tr_handleStatus( gl_handle ); - if( TR_NAT_TRAVERSAL_UNMAPPED != hs->natTraversalStatus ) - { - stillmore = 1; - } - } - - if( !stillmore || EXIT_TIMEOUT <= time( NULL ) - gl_exiting ) - { - if( stillmore ) - { - errmsg( "timing out trackers and/or port mapping on exit" ); - } - for( tor = RB_MIN( tortree, &gl_tree ); NULL != tor; tor = next ) - { - next = RB_NEXT( tortree, &gl_tree, tor ); - freetor( tor ); - } - tr_close( gl_handle ); - exit( gl_exitval ); - } - } - - if( stillmore ) - { - memset( &tv, 0, sizeof tv ); - tv.tv_sec = TIMER_SECS; - tv.tv_usec = TIMER_USECS; - evtimer_add( &gl_event, &tv ); - } -} - -int -savestate( void ) -{ - benc_val_t top, * list; - struct tor * ii; - int torrentCount; - - torrentCount = 0; - RB_FOREACH( ii, tortree, &gl_tree ) - ++torrentCount; - - tr_bencInitDict( &top, 9 ); - tr_bencDictAddInt( &top, "autostart", gl_autostart ); - tr_bencDictAddInt( &top, "port", gl_port ); - tr_bencDictAddInt( &top, "default-pex", gl_pex ); - tr_bencDictAddInt( &top, "port-mapping", gl_mapping ); - tr_bencDictAddInt( &top, "upload-limit", gl_uplimit ); - tr_bencDictAddInt( &top, "download-limit", gl_downlimit ); - tr_bencDictAddStr( &top, "default-directory", gl_dir ); - tr_bencDictAddStr( &top, "encryption-mode", TR_ENCRYPTION_REQUIRED == gl_crypto - ? "required" : "preferred" ); - list = tr_bencDictAddList( &top, "torrents", torrentCount ); - - RB_FOREACH( ii, tortree, &gl_tree ) - { - const tr_info * inf = tr_torrentInfo( ii->tor ); - const tr_stat * st = tr_torrentStat( ii->tor ); - tr_benc * tor = tr_bencListAddDict( list, 3 ); - tr_bencDictAddStr( tor, "hash", inf->hashString ); - tr_bencDictAddInt( tor, "paused", !TR_STATUS_IS_ACTIVE( st->status ) ); - tr_bencDictAddStr( tor, "directory", tr_torrentGetFolder( ii->tor ) ); - } - - if( tr_bencSaveFile( gl_newstate, &top ) ) - { - errnomsg( "failed to save state: failed to write to %s", gl_newstate ); - return -1; - } - - if( 0 > rename( gl_newstate, gl_state ) ) - { - errnomsg( "failed to save state: failed to rename %s to %s", - gl_newstate, CONF_FILE_STATE ); - return -1; - } - - return 0; -} diff --git a/daemon/torrents.h b/daemon/torrents.h deleted file mode 100644 index d2136f9bc..000000000 --- a/daemon/torrents.h +++ /dev/null @@ -1,65 +0,0 @@ -/****************************************************************************** - * $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. - *****************************************************************************/ - -#ifndef TR_DAEMON_TORRENTS_H -#define TR_DAEMON_TORRENTS_H - -#include - -#include - -struct event_base; - -void torrent_init ( const char * configDir, struct event_base * ); -int torrent_add_file ( const char *, const char *, int ); -int torrent_add_data ( uint8_t *, size_t, const char *, int ); -void torrent_start ( int ); -void torrent_stop ( int ); -void torrent_verify ( int ); -void torrent_remove ( int ); -tr_torrent * torrent_handle ( int ); -const tr_info * torrent_info ( int ); -const tr_stat * torrent_stat ( int ); -int torrent_lookup ( const uint8_t * ); -void * torrent_iter ( void *, int * ); - -void torrent_exit ( int ); -void torrent_set_autostart ( int ); -int torrent_get_autostart ( void ); -void torrent_set_port ( int ); -int torrent_get_port ( void ); -void torrent_set_pex ( int ); -int torrent_get_pex ( void ); -void torrent_enable_port_mapping ( int ); -int torrent_get_port_mapping ( void ); -void torrent_set_uplimit ( int ); -int torrent_get_uplimit ( void ); -void torrent_set_downlimit ( int ); -int torrent_get_downlimit ( void ); -void torrent_set_directory ( const char * ); -const char * torrent_get_directory ( void ); -void torrent_set_encryption ( tr_encryption_mode ); -tr_encryption_mode torrent_get_encryption ( void ); - -#endif /* TR_DAEMON_TORRENTS_H */ diff --git a/daemon/transmission-daemon.1 b/daemon/transmission-daemon.1 index 3c368e7b9..8f674f1f8 100644 --- a/daemon/transmission-daemon.1 +++ b/daemon/transmission-daemon.1 @@ -69,15 +69,14 @@ instead of the platform-dependent default location. The .Nm program was written by -.An Josh Elsasser Aq josh@elsasser.org , -.An Eric Petit Aq titer@m0k.org , -.An Charles Kerr Aq charles@rebelbase.com , +.An Charles Kerr , +.An Josh Elsasser , +.An Eric Petit , and -.An Mitchell Livingston Aq livings124@gmail.com . +.An Mitchell Livingston . .Sh SEE ALSO .Xr transmissioncli 1 , .Xr transmission 1 , -.Xr transmission-proxy 1 , .Xr transmission-remote 1 .Pp http://www.transmissionbt.com/ diff --git a/daemon/transmission-proxy.1 b/daemon/transmission-proxy.1 deleted file mode 100644 index d0fb8446b..000000000 --- a/daemon/transmission-proxy.1 +++ /dev/null @@ -1,66 +0,0 @@ -.\" $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. - -.Dd March 30, 2007 -.Dt TRANSMISSION-PROXY 1 -.Os -.Sh NAME -.Nm transmission-proxy -.Nd a bittorrent client -.Sh SYNOPSIS -.Nm transmission-proxy -.Fl h -.Nm -.Op Fl t -.Sh DESCRIPTION -An ipc proxy to make remote daemon connections easier. -.Pp -The options are as follows: -.Bl -tag -width Ds -.It Fl t Fl -type Ar daemon | gtk -Connect to either -.Xr transmission-daemon 1 -or -.Xr transmission 1 . -.It Fl g, Fl -config-dir Ar directory -Where to look for configuration files. -.It Fl h Fl -help -Print command-line option descriptions. -.El -.Sh FILES -.Pa ~/.transmission -.Sh AUTHORS -.An -nosplit -The -.Nm -program was written by -.An Josh Elsasser Aq josh@elsasser.org , -.An Eric Petit Aq titer@m0k.org , -.An Charles Kerr Aq charles@rebelbase.com , -and -.An Mitchell Livingston Aq livings124@gmail.com . -.Sh SEE ALSO -.Xr transmissioncli 1 , -.Xr transmission 1 , -.Xr transmission-remote 1 -.Pp -http://www.transmissionbt.com/ diff --git a/daemon/transmission-remote.1 b/daemon/transmission-remote.1 index 9da2793c0..17c707c24 100644 --- a/daemon/transmission-remote.1 +++ b/daemon/transmission-remote.1 @@ -197,15 +197,14 @@ Show the info hashes of all the torrents on jade: The .Nm program was written by -.An Josh Elsasser Aq josh@elsasser.org , -.An Eric Petit Aq titer@m0k.org , -.An Charles Kerr Aq charles@rebelbase.com , +.An Charles Kerr , +.An Josh Elsasser , +.An Eric Petit , and -.An Mitchell Livingston Aq livings124@gmail.com . +.An Mitchell Livingston . .Sh SEE ALSO .Xr transmissioncli 1 , .Xr transmission-daemon 1 , -.Xr transmission 1 , -.Xr transmission-proxy 1 +.Xr transmission 1 .Pp http://www.transmissionbt.com/ diff --git a/doc/ipcproto.txt b/doc/ipcproto.txt deleted file mode 100644 index e2130da06..000000000 --- a/doc/ipcproto.txt +++ /dev/null @@ -1,619 +0,0 @@ -It is assumed the reader is familiar with bencoding, as described in -the BitTorrent protocol specification at -http://www.bittorrent.org/protocol.html - -Dictionary keys used below will be enclosed in quotation marks, these -are used for clarity and are not part of the actual key. - -The IPC protocol is used to allow processes to control or retrieve -information from Transmission frontend, such as transmission-daemon, -or the GTK+ or Mac OS X frontends. This communication is done -over a unix-domain socket file such as -~/.transmission/daemon/socket. In this document the Transmission -frontend will be referred to as the server and the process connecting -to it as the client. - -Once a client connects to the server's socket, messages may be -exchanged until either end closes the connection. Messages contain an -32-bit payload length, encoded as 8 bytes of ASCII hexadecimal, -followed by the payload. Upper, lower, or mixed case for the length -are all acceptable and must be handled correctly. Payload lengths -greater than 2^31 - 8 (ie: 2147483640 decimal, 7FFFFFF8 hex) are not -allowed. - -Bencoded messages will additionally be shown in the following format -for better readability: -str - "this is a string" -num - 38795 -list - ("wheeee", 435, "writing docs is boring") -dict - {"name": "Josh", "beverage": "coffee", "quantity": "too damn much" } - -For version 1, the message payload is a bencoded dictionary, the -valid keys and value formats for which are described below. Multiple -keys may be used in one message. - -An example version 1 message: - -0000000Ed4:porti51413ee - {"port": 51413} - -For version 2 the message payload is a bencoded list containing a -message id string followed by a bencoded value, the format of which is -the same for version 1. The value may be followed by an optional -bencoded integer, this is a message tag and is described in more -detail below. - -An example version 2 message: - -0000001El12:get-info-alll4:hashee - ("get-info-all", ("hash")) - -The same message with a tag: - -00000021l12:get-info-alll4:hashei5ee - ("get-info-all", ("hash"), 5) - -Once the connection is made both the client and server must send a -version 1 style message (ie: the payload is a dictionary and may not -contain a tag) with the dictionary key "version" and a value formatted -as described below. The version should be the first but not -necessarily only key in the dictionary. Any other keys should be -ignored and not processed as messages. Neither the client nor the -server should wait to receive a version message before sending one, it -must be sent immediately. No other messages should be sent until the -version is received. - -The version value should be a bencoded dictionary containing two keys, -"max" and "min". These are the minimum and maximum supported protocol -versions, respectively. Communication will take place using the -highest protocol version supported by both the client and the server, -and is not possible at all if there is no common version. A client may -receive a version value that is an integer instead of a dictionary -with "min" and "max" keys. This deprecated version format indicates -the only version supported by the server. - -The version dictionary may optionally contain a key "label". This is a -human-readable name for the software, it is not machine-readable and -neither servers nor clients should attempt to parse it. - -An example message containing minimum and maximum versions 1 and 2: - -0000001Dd7:versiond3:mini1e3:maxi2eee - {"version": {"min": 1, "max": 2}} - -Tagged messages, only supported in version 2, allow a client to tag a -message with a positive non-zero integer. The server is then required -to send a response to the message even if none would normally be -required, and to tag the response with the same integer. When the -server receives a tagged message it must send exactly one message back -with the same tag. The client is allowed to use the same tag for -multiple messages, even if a response to the first is not received -before the second it sent. If a tagged message does not normally -require a response then a "succeeded", "failed", "not-supported" or -"bad-format" message will be sent back. - -An example tagged message and response: - -00000010l5:startli8eei15ee - ("start", (8), 15) -00000011l8:succeeded0:i15ee - ("succeeded", "", 15) - -Some example sessions, including version handshake, are found at the -end of this file. - -Dictionary keys are encoded in UTF-8 and case sensitive. Any character -except a NUL (0x00) is valid. A server or client need not support -every possible key and should silently ignore any that it does not -understand. - -If a reference to a boolean is seen, it should be taken to mean an -integer with a value of 0 representing false, 1 representing true, and -any other value undefined. - -Individual torrents are identified by a unique integer. This integer -is only valid for the current connection and may be invalid or refer -to another torrent in a future connection. If a torrent is closed it's -ID will never be reused to refer to another torrent for at least the -duration of the connection. Negative integers or 0 are not valid IDs. - -A list of keys and the format of their values follows. Also listed is -the minimum protocol version that the key may be used with. - - -Key: "addfiles" -Version: 1 -Format: list of strings -Replies: "succeeded", "failed", "not-supported", "bad-format", "info" -Example: 8:addfilesl21:/torrents/foo.torrent20:/home/me/bar.torrente - "addfiles", ("/torrents/foo.torrent", /home/me/bar.torrent") -Details: Each string is the absolute path to a torrent metainfo file - for the server to add. Note that whether or not the torrent - metainfo file is copied (allowing the original to be moved or - deleted safely) is implementation dependent and may not - currently be known or changed with this protocol. - -Key: "addfile-detailed" -Version: 2 -Format: dict -Replies: "succeeded", "failed", "not-supported", "bad-format", "info" -Example: 16:addfile-detailedd4:file19:/tor/wooble.torrente - "addfile-detailed", {"file": "/tor/wooble.torrent"} -Details: Dictionary containing information about a torrent for the - server to add. Valid keys include: - "file" string, filename of torrent metainfo file - "data" string, contents of a torrent metainfo file - "directory" string, directory for data files for the torrent - "autostart" boolean, start the torrent automatically - Either "file" or "data" is required, but both are not allowed. - -Key: "automap" -Version: 2 -Format: boolean -Replies: "succeeded", "failed", "not-supported", "bad-format" -Example: 7:automapi1e - "automap", 1 -Details: Enable (1) or disable (0) automatic port mapping on the server. - -Key: "autostart" -Version: 2 -Format: boolean -Replies: "succeeded", "failed", "not-supported", "bad-format" -Example: 9:autostarti0e - "autostart", 0 -Details: Enable (1) or disable (0) automatic starting of new torrents - added via "addfiles" or "addfiles-detailed" messages. - -Key: "bad-format" -Version: 2 -Format: value is ignored -Replies: N/A -Example: 10:bad-format0: - "bad-format", "" -Details: Sent in response to a tagged message which was structured - incorrectly. For example, an "autostart" message with a - string value might cause this message to be returned. - -Key: "directory" -Version: 2 -Format: string -Replies: "succeeded", "failed", "not-supported", "bad-format" -Example: 9:directory21:/home/roger/downloads - "directory", "/home/roger/downloads" -Details: Set the default directory used for any torrents added in the future. - -Key: "downlimit" -Version: 2 -Format: int -Replies: "succeeded", "failed", "not-supported", "bad-format" -Example: 9:downlimiti100e - "downlimit", 100 -Details: Set the server's download limit in kilobytes per second. - Negative values are interpreted as no limit. - -Key: "encryption" -Version: 2 -Format: string -Replies: "succeeded", "failed", "not-supported", "bad-format" -Example: 10:encryption8:required - "encryption", "required" -Details: Set the encryption mode for peer connections. Valid values - are "required", "preferred" and "plaintext". - -Key: "failed" -Version: 2 -Format: string -Replies: N/A -Example: 6:failed17:permission denied - "failed", "permission denied" -Details: Sent in response to a tagged message to indicate failure. - -Key: "get-automap" -Version: 2 -Format: value is ignored -Replies: "failed", "not-supported", "bad-format", "automap" -Example: 11:get-automap0: - "get-automap", "" -Details: Requests that an "automap" message be sent back. - -Key: "get-autostart" -Version: 2 -Format: value is ignored -Replies: "failed", "not-supported", "bad-format", "autostart" -Example: 13:get-autostart0: - "get-autostart", "" -Details: Requests that an "autostart" message be sent back. - -Key: "get-directory" -Version: 2 -Format: value is ignored -Replies: "failed", "not-supported", "bad-format", "directory" -Example: 13:get-directory0: - "get-directory", "" -Details: Requests that an "directory" message be sent back. - -Key: "get-downlimit" -Version: 2 -Format: value is ignored -Replies: "failed", "not-supported", "bad-format", "downlimit" -Example: 13:get-downlimit0: - "get-downlimit", "" -Details: Requests that a "downlimit" message be sent back. - -Key: "get-encryption" -Version: 2 -Format: value is ignored -Replies: "failed", "not-supported", "bad-format", "encryption" -Example: 14:get-encryption0: - "get-encryption", "" -Details: Requests that an "encryption" message be sent back. - -Key: "get-info" -Version: 2 -Format: dict with keys "id" and "type" for lists of ints and strings -Replies: "failed", "not-supported", "bad-format", "info" -Example: 8:get-infod2:idli4ei7ei2ee4:typel4:hash4:nameee - "get-info", {"id": (4, 7, 2), "type": ("hash", "name")} -Details: Requests that the server send back an "info" message with - info on all the torrent IDs in "id". The "type" key requests - what info will be returned. See below for valid values to use - here. Since the torrent ID is always included in an "info" - message an empty or missing "type" key will cause only the ID - to be returned. An "info" message will always be sent back, - even if it is empty. - -Key: "get-info-all" -Version: 2 -Format: list of strings -Replies: "failed", "not-supported", "bad-format", "info" -Example: 12:get-info-alll4:hash4:namee - "get-info-all", ("hash", "name") -Details: Same as "get-info" message with all torrent IDs specified. - -Key: "get-pex" -Version: 2 -Format: value is ignored -Replies: "failed", "not-supported", "bad-format", "pex" -Example: 7:get-pex0: - "get-pex", "" -Details: Requests that a "pex" message be sent back. - -Key: "get-port" -Version: 2 -Format: value is ignored -Replies: "failed", "not-supported", "bad-format", "port" -Example: 8:get-port0: - "get-port", "" -Details: Requests that a "port" message be sent back. - -Key: "get-status" -Version: 2 -Format: dict with keys "id" and "type" for lists of ints and strings -Replies: "failed", "not-supported", "bad-format", "status" -Example: 10:get-statusd2:idli4ei7ei2ee4:typel5:state9:completedee - "get-status", {"id": (4, 7, 4), "type": ("state", "completed")} -Details: Same as "get-info" message except status type strings are used - instead and the server sends back a "status" message. - -Key: "get-status-all" -Version: 2 -Format: list of strings -Replies: "failed", "not-supported", "bad-format", "status" -Example: 14:get-status-alll5:state9:completede - "get-status-all", ("state", "completed") -Details: Same as "get-status" message with all torrent IDs specified. - -Key: "get-supported" -Version: 2 -Format: list of strings -Replies: "failed", "not-supported", "bad-format", "supported" -Example: 13:get-supportedl6:lookup8:get-port16:addfile-detailede - "get-supported", ("lookup", "get-port", "addfile-detailed") -Details: Request that a "supported" message be returned with whichever - of the given message keys are supported. - -Key: "get-uplimit" -Version: 2 -Format: value is ignored -Replies: "failed", "not-supported", "bad-format", "uplimit" -Example: 11:get-uplimit0: - "get-uplimit", "" -Details: Requests that an "uplimit" message be sent back. - -Key: "lookup" -Version: 2 -Format: list of strings -Replies: "failed", "not-supported", "bad-format", "info" -Example: 6:lookupl40:0f16ea6965ee5133ea4dbb1e7f516e9fcf3d899ee - "lookup", ("0f16ea6965ee5133ea4dbb1e7f516e9fcf3d899e") -Details: Request that the server send back an "info" message with "id" - and "hash" keys for any torrents with the given hashes. - -Key: "info" -Version: 2 -Format: list of dictionaries -Replies: N/A -Example: 4:infold2:idi3e4:name3:fooed2:idi9e4:name3:baree - "info", ({"id": 4, "name": "foo"}, {"id": 9, "name": "bar"}) -Details: A list containing information for several torrents. The - dictionaries always contain at least an "id" key with the - integer ID for the torrent, other possible values are listed - below. - -Key: "noop" -Version: 2 -Format: value is ignored -Replies: "succeeded", "failed", "not-supported", "bad-format" -Example: 4:noop0: - "noop", "" -Details: This does nothing but keep the connection alive. With a tag - it may be used as a ping. - -Key: "not-supported" -Version: 2 -Format: value is ignored -Replies: N/A -Example: 13:not-supported0: - "not-supported", "" -Details: Sent in response to a tagged message to indicated that the - message was not supported. - -Key: "pex" -Version: 2 -Format: boolean -Replies: "succeeded", "failed", "not-supported", "bad-format" -Example: 3:pexi0e - "pex", 0 -Details: Enables or disables peer exchange. - -Key: "port" -Version: 2 -Format: int between 0 and 65535 -Replies: "succeeded", "failed", "not-supported", "bad-format" -Example: 4:porti51413e - "port", 51413 -Details: Change the port the server uses to listen for incoming peer - connections. - -Key: "encryption" -Version: 2 -Format: string -Example: 10:encryption9:preferred - "encryption", "preferred" -Details: Returns thestring is one of: - "required", "preferred", "tolerated" - -Key: "quit" -Version: 1 -Format: value is ignored -Replies: "succeeded", "failed", "not-supported", "bad-format" -Example: 4:quit0: - "quit", "" -Details: Cause the server to quit. Note that the connection might be - closed without a response being sent. - -Key: "remove" -Version: 2 -Format: list of torrent ID ints -Replies: "succeeded", "failed", "not-supported", "bad-format" -Example: 5:removeli3ei8ei6ee - "remove", (3, 8, 6) -Details: Stop and remove the specified torrents. Note that whether or - not the downloaded data or the original torrent files will be - removed is implementation dependent and may not currently be - known or changed with this protocol. If a saved copy of the - torrent file has been made then it will always be deleted. - -Key: "remove-all" -Version: 2 -Format: value is ignored -Replies: "succeeded", "failed", "not-supported", "bad-format" -Example: 10:remove-all0: - "remove-all", "" -Details: Like "remove" with all torrent IDs specified. - -Key: "start" -Version: 2 -Format: list of torrent ID ints -Replies: "succeeded", "failed", "not-supported", "bad-format" -Example: 5:startli3ei8ei6ee - "start", (3, 8, 6) -Details: List of torrent IDs to start. - -Key: "start-all" -Version: 2 -Format: value is ignored -Replies: "succeeded", "failed", "not-supported", "bad-format" -Example: 9:start-all0: - "start-all", "" -Details: Start all torrents. - -Key: "status" -Version: 2 -Format: list of dictionaries -Replies: N/A -Example: 4:infold2:idi3e5:state7:seedinged2:idi9e5:state6:pausedee - "info", ({"id": 3, "state": "seeding"}, {"id": 9, "state" : "paused"}) -Details: Same as "info" message except status type keys are used. - -Key: "stop" -Version: 2 -Format: list of torrent ID ints -Replies: "succeeded", "failed", "not-supported", "bad-format" -Example: 4:stopli3ei8ei6ee - "stop", (3, 8, 6) -Details: List of torrent IDs to stop. - -Key: "stop-all" -Version: 2 -Format: value is ignored -Replies: "succeeded", "failed", "not-supported", "bad-format" -Example: 8:stop-all0: - "stop-all", "" -Details: Stop all torrents. - -Key: "succeeded" -Version: 2 -Format: value is ignored -Replies: N/A -Example: 8:succeeded0: - "succeeded", "" -Details: This is used to indicate that a tagged message was processed - successfully. - -Key: "supported" -Version: 2 -Format: list of strings -Replies: N/A -Example: 9:supportedl8:get-port6:lookupe - "supported", ("get-port", "lookup") -Details: Sent in response to a "get-supported" message, indicates that - the given messages ate supported. - -Key: "uplimit" -Version: 2 -Format: int -Replies: "succeeded", "failed", "not-supported", "bad-format" -Example: 7:uplimiti20e - "uplimit", 20 -Details: Set the server's upload limit in kilobytes per second. - Negative values are interpreted as no limit. - -Key: "verify" -Version: 2 -Format: list of torrent ID ints -Replies: "succeeded", "failed", "not-supported", "bad-format" -Example: 6:verifyli3ei8ei6ee - "verify", (3, 8, 6) -Details: List of torrent IDs to stop. - - - -Info types for "get-info" and "info" messages. The only type for which -support is mandatory is "id". - -"id" integer, torrent's ID for this connection -"hash" SHA-1 info hash as a 40-char hex string -"name" string, torrent name -"path" string, path to .torrent file -"saved" boolean, true if a copy of this torrent was saved -"private" boolean, true if the torrent is private -"trackers" a list of lists of dictionaries containing tracker info: - "address" string, hostname or ip address of tracker - "port" integer, port for tracker - "announce" string, announce url on tracker - "scrape" string, scrape url on tracker, may be absent -"comment" string, comment from torrent file -"creator" string, creator of torrent file -"date" integer, date of torrent creation (unix time_t format) -"size" integer, total size of all files in bytes -"files" list of dictionaries for the files in this torrent: - "name" string, name of file - "size" integer, size of file in bytes - - -Status types for "get-status" and "status" messages. The only type for -which support is mandatory is "id". - -"completed" integer, bytes of data downloaded and verified -"download-speed" integer, download speed in bytes per second -"download-total" integer, total bytes downloaded so far -"error" string, one of the following: - "assert" something happened that shouldn't - "io-parent" missing parent directory - "io-permissions" filesystem permission error - "io-space" not enough free space in filesystem - "io-resource" insufficient resources - "io-other" other filesystem i/o error - "tracker-error" tracker returned error message - "tracker-warning" tracker returned warning message - "other" other error - zero-length or missing string indicates no error -"error-message" string, printable error message -"eta" integer, estimated seconds until downloading is finished -"id" integer, torrent's ID for this connection -"peers-downloading" integer, peers downloading from us -"peers-from" dict with the following int keys, peer connection sources: - "incoming" peers connected to our listening port - "tracker" peers discovered from tracker - "cache" peers retrieved from on-disk cache - "pex" peers discovered via peer exchange -"peers-total" integer, total connected peers -"peers-uploading" integer, peers uploading to us -"running" boolean, false if torrent is stopped or stopping -"state" string, one of the following: - "checking" performing hash check on file data - "downloading" downloading file data - "seeding" seeding file data to peers - "stopping" contacting tracker to send 'stopped' event - "paused" torrent is not active - "waiting to checking" - torrent is queued for hash check. - yes, this it really is called that. -"swarm-speed" integer, swarm speed in bytes per second -"tracker" dict with the following keys, current active tracker - "address" string, hostname or ip address of tracker - "port" integer, port for tracker - "announce" string, tracker announce url - "scrape" string, tracker scrape url, may be absent -"scrape-completed" integer, total completed peers as reported by tracker -"scrape-leechers" integer, current leechers as reported by tracker -"scrape-seeders" integer, current, seeders as reported by tracker -"upload-speed" integer, upload speed in bytes per second -"upload-total" integer, total bytes uploaded so far - - -Examples: - -Data from the client to the server is prefixed with >>> and from -server to client with <<<. These prefixes and newlines are added for -clarity, they are not actually sent over the socket. - -Quit the server. Note that this is a version 1 client and so version 1 -messages are used. ->>> 0000001Dd7:versiond3:mini1e3:maxi1eee - {"version", {"min": 1, "max": 1"}} -<<< 0000001Dd7:versiond3:mini1e3:maxi2eee - {"version", {"min": 1, "max": 2"}} ->>> 0000000Bd4:quit0:e - {"quit": ""} - -Pause all torrents and disable automapping. Note the server happens to -have sent it's version before the client. The value for the stop-all -message here is 5:fnord instead of 0: as used above, since the value -is unused anything is allowed. -<<< 0000001Dd7:versiond3:mini1e3:maxi2eee - {"version", {"min": 1, "max": 2"}} ->>> 0000001Dd7:versiond3:mini1e3:maxi2eee - {"version", {"min": 1, "max": 2"}} ->>> 0000000El8:stop-all5:fnorde - ("stop-all", "fnord") - -Change upload and download limits with tagged responses. ->>> 0000001Dd7:versiond3:mini1e3:maxi2eee - {"version", {"min": 1, "max": 2"}} -<<< 0000001Dd7:versiond3:mini1e3:maxi2eee - {"version", {"min": 1, "max": 2"}} ->>> 00000017l9:downlimiti100ei47el - ("downlimit", 100, 47) ->>> 00000017l9:uplimiti20ei48ee - ("uplimit", 20, 48) -<<< 00000014l8:succeeded0:i47ee - ("succeeded", "", 47) -<<< 00000014l8:succeeded0:i48ee - (succeeded"", "", 48) - -Retrieve the upload and download limits. Note that the server has -returned the responses in a different order than the requests were -sent. The server is allowed to do this, a client should use tags if -this is a concern. ->>> 0000001Dd7:versiond3:mini1e3:maxi2eee - {"version", {"min": 1, "max": 2"}} -<<< 0000001Dd7:versiond3:mini1e3:maxi2eee - {"version", {"min": 1, "max": 2"}} ->>> 00000015l13:get-downlimiti0ee00000013l13:get-uplimiti0ee - ("get-downlimit", 0) - ("get-uplimit", 0) -<<< 0000000Fl9:uplimiti20ee00000012l9:downlimiti100ee - ("uplimit", 20) - ("downlimit", 100) diff --git a/doc/rpc-json-spec.txt b/doc/rpc-spec.txt similarity index 82% rename from doc/rpc-json-spec.txt rename to doc/rpc-spec.txt index 0059fd4fc..37dde920c 100644 --- a/doc/rpc-json-spec.txt +++ b/doc/rpc-spec.txt @@ -86,19 +86,22 @@ Method name: "torrent-info". - Request arguments: an "id" number specifying a torrent id number + Request arguments: 3.1's optional "ids" argument. - Response arguments: "info", an array of objects based on libtransmission's - tr_info struct but different in the following ways: + Response arguments: "torrent-info", an array of objects based on + libtransmission's tr_info struct but different in the following ways: + (1) the torrent's "id" field is added. + (2) tr_info's "hash" field is omitted. + (3) tr_info's "pieces" field is omitted. + (4) tr_file's "firstPiece", "lastPiece", and "offset" fields are omitted. - (1) tr_info's "hash" field is omitted. - (2) tr_info's "pieces" field is omitted. - (3) tr_file's "firstPiece", "lastPiece", and "offset" fields are omitted. + Note that this is a fairly high-bandwidth request and that its results + don't change. You should try to cache its results instead of re-calling it. Example Request: { - "arguments": { "id": 7 } + "arguments": { "ids": [ 7, 10 ] } "method": "torrent-info", "tag": 39693 } @@ -109,14 +112,24 @@ "tag": 39693 "result": "success", "arguments": { - "info": { - "id": 7, - "totalSize": 9803930483, - "pieceCount": 1209233, - "pieceSize": 4096, - "name": "Ubuntu x86_64 DVD", - ... - } + "torrent-info": [ + { + "id": 7, + "name": "Ubuntu x86_64 DVD", + "pieceCount": 1209233, + "pieceSize": 4096, + "totalSize": 9803930483, + ... + }, + { + "id": 10, + "name": "Ubuntu i386 DVD", + "pieceCount": 83943, + "pieceSize": 12345, + "totalSize": 2398480394, + ... + } + ] } } @@ -124,15 +137,16 @@ Method name: "torrent-status" - Request arguments: an "id" int specifying a torrent id number + Request arguments: 3.1's optional "ids" argument. - Response arguments: "status", an object based on libtransmission's - tr_stat struct but differerent in the following ways: + Response arguments: "torrent-status", an array of objects based + on libtransmission's tr_stat struct but differerent in the + following ways: - (1) tr_stat's "tracker" field is omitted. - (2) a new string, "announce-url", is added. - (3) a new string, "scrape-url", is added. - (4) tr_info's "name" field is added. + (1) the torrent's "id" field is added. + (2) tr_info's "name" field is added. + (3) tr_stat's "tracker" field is omitted, and is instead + replaced with two strings: "announce-url" and scrape-url" 3.5. Adding a Torrent diff --git a/gtk/Makefile.am b/gtk/Makefile.am index d46d41889..33dc8af5a 100644 --- a/gtk/Makefile.am +++ b/gtk/Makefile.am @@ -22,7 +22,6 @@ noinst_HEADERS = \ details.h \ dialogs.h \ hig.h \ - ipc.h \ file-list.h \ lock.h \ logo.h \ @@ -51,7 +50,6 @@ transmission_SOURCES = \ dialogs.c \ file-list.c \ hig.c \ - ipc.c \ main.c \ makemeta-ui.c \ msgwin.c \ @@ -74,6 +72,7 @@ transmission_LDADD = \ $(top_builddir)/third-party/libevent/libevent_core.la \ $(top_builddir)/third-party/miniupnp/libminiupnp.a \ $(top_builddir)/third-party/libnatpmp/libnatpmp.a \ + $(top_builddir)/third-party/shttpd/libshttpd.a \ $(GTK_LIBS) \ $(GIO_LIBS) \ $(LIBNOTIFY_LIBS) \ diff --git a/gtk/add-dialog.c b/gtk/add-dialog.c index 70c66eeee..54cae1fde 100644 --- a/gtk/add-dialog.c +++ b/gtk/add-dialog.c @@ -25,7 +25,7 @@ struct AddData GtkWidget * run_check; GtkWidget * trash_check; char * filename; - char * destination; + char * downloadDir; TrTorrent * gtor; tr_ctor * ctor; }; @@ -63,7 +63,7 @@ addResponseCB( GtkDialog * dialog, gint response, gpointer gdata ) tr_ctorFree( data->ctor ); g_free( data->filename ); - g_free( data->destination ); + g_free( data->downloadDir ); g_free( data ); gtk_widget_destroy( GTK_WIDGET( dialog ) ); } @@ -72,7 +72,7 @@ static void updateTorrent( struct AddData * o ) { if( o->gtor ) - tr_torrentSetFolder( tr_torrent_handle( o->gtor ), o->destination ); + tr_torrentSetDownloadDir( tr_torrent_handle( o->gtor ), o->downloadDir ); file_list_set_torrent( o->list, o->gtor ); } @@ -93,7 +93,7 @@ sourceChanged( GtkFileChooserButton * b, gpointer gdata ) tr_torrent * torrent; tr_handle * handle = tr_core_handle( data->core ); tr_ctorSetMetainfoFromFile( data->ctor, data->filename ); - tr_ctorSetDestination( data->ctor, TR_FORCE, data->destination ); + tr_ctorSetDownloadDir( data->ctor, TR_FORCE, data->downloadDir ); tr_ctorSetPaused( data->ctor, TR_FORCE, TRUE ); tr_ctorSetDeleteSource( data->ctor, FALSE ); if(( torrent = tr_torrentNew( handle, data->ctor, &err ))) @@ -112,14 +112,14 @@ verifyRequested( GtkButton * button UNUSED, gpointer gdata ) } static void -destinationChanged( GtkFileChooserButton * b, gpointer gdata ) +downloadDirChanged( GtkFileChooserButton * b, gpointer gdata ) { char * fname = gtk_file_chooser_get_filename( GTK_FILE_CHOOSER( b ) ); if( fname ) { struct AddData * data = gdata; - g_free( data->destination ); - data->destination = fname; + g_free( data->downloadDir ); + data->downloadDir = fname; updateTorrent( data ); verifyRequested( NULL, data ); @@ -174,7 +174,7 @@ addSingleTorrentDialog( GtkWindow * parent, GTK_RESPONSE_CANCEL, -1 ); - if( tr_ctorGetDestination( ctor, TR_FORCE, &str ) ) + if( tr_ctorGetDownloadDir( ctor, TR_FORCE, &str ) ) g_assert_not_reached( ); g_assert( str ); @@ -182,7 +182,7 @@ addSingleTorrentDialog( GtkWindow * parent, data->core = core; data->ctor = ctor; data->filename = g_strdup( tr_ctorGetSourceFile( ctor ) ); - data->destination = g_strdup( str ); + data->downloadDir = g_strdup( str ); data->list = file_list_new( NULL ); data->trash_check = gtk_check_button_new_with_mnemonic( _( "Mo_ve source file to Trash" ) ); data->run_check = gtk_check_button_new_with_mnemonic( _( "_Start when added" ) ); @@ -218,11 +218,11 @@ addSingleTorrentDialog( GtkWindow * parent, gtk_table_attach( GTK_TABLE( t ), l, col, col+1, row, row+1, GTK_FILL, 0, 0, 0 ); ++col; w = gtk_file_chooser_button_new( _( "Select Destination Folder" ), GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER ); - if( !gtk_file_chooser_set_filename( GTK_FILE_CHOOSER( w ), data->destination ) ) - g_warning( "couldn't select '%s'", data->destination ); + if( !gtk_file_chooser_set_filename( GTK_FILE_CHOOSER( w ), data->downloadDir ) ) + g_warning( "couldn't select '%s'", data->downloadDir ); gtk_table_attach( GTK_TABLE( t ), w, col, col+1, row, row+1, ~0, 0, 0, 0 ); gtk_label_set_mnemonic_widget( GTK_LABEL( l ), w ); - g_signal_connect( w, "selection-changed", G_CALLBACK( destinationChanged ), data ); + g_signal_connect( w, "selection-changed", G_CALLBACK( downloadDirChanged ), data ); ++row; col = 0; diff --git a/gtk/details.c b/gtk/details.c index 55b64d945..e8afd24ec 100644 --- a/gtk/details.c +++ b/gtk/details.c @@ -779,7 +779,7 @@ info_page_new (tr_torrent * tor) hig_workarea_add_section_divider (t, &row); hig_workarea_add_section_title (t, &row, _("Location")); - l = g_object_new( GTK_TYPE_LABEL, "label", tr_torrentGetFolder( tor ), "selectable", TRUE, + l = g_object_new( GTK_TYPE_LABEL, "label", tr_torrentGetDownloadDir( tor ), "selectable", TRUE, "ellipsize", PANGO_ELLIPSIZE_END, NULL ); hig_workarea_add_row (t, &row, _( "Destination folder:" ), l, NULL); @@ -857,7 +857,7 @@ refresh_activity (GtkWidget * top) tr_strlratio( buf, stat->ratio, sizeof( buf ) ); gtk_label_set_text( GTK_LABEL( a->ratio_lb ), buf ); - tr_strlspeed( buf, stat->swarmspeed, sizeof(buf) ); + tr_strlspeed( buf, stat->swarmSpeed, sizeof(buf) ); gtk_label_set_text (GTK_LABEL(a->swarm_lb), buf ); gtk_label_set_text (GTK_LABEL(a->err_lb), @@ -1213,29 +1213,29 @@ refresh_tracker( GtkWidget * w ) const tr_stat * torStat = tr_torrent_stat( page->gtor ); l = page->last_scrape_time_lb; - t = torStat->tracker_stat.lastScrapeTime; + t = torStat->trackerStat.lastScrapeTime; refresh_time_lb( l, t ); l = page->last_scrape_response_lb; - gtk_label_set_text( GTK_LABEL( l ), torStat->tracker_stat.scrapeResponse ); + gtk_label_set_text( GTK_LABEL( l ), torStat->trackerStat.scrapeResponse ); l = page->next_scrape_countdown_lb; - t = torStat->tracker_stat.nextScrapeTime; + t = torStat->trackerStat.nextScrapeTime; refresh_countdown_lb( l, t ); l = page->last_announce_time_lb; - t = torStat->tracker_stat.lastAnnounceTime; + t = torStat->trackerStat.lastAnnounceTime; refresh_time_lb( l, t ); l = page->last_announce_response_lb; - gtk_label_set_text( GTK_LABEL( l ), torStat->tracker_stat.announceResponse ); + gtk_label_set_text( GTK_LABEL( l ), torStat->trackerStat.announceResponse ); l = page->next_announce_countdown_lb; - t = torStat->tracker_stat.nextAnnounceTime; + t = torStat->trackerStat.nextAnnounceTime; refresh_countdown_lb( l, t ); l = page->manual_announce_countdown_lb; - t = torStat->tracker_stat.nextManualAnnounceTime; + t = torStat->trackerStat.nextManualAnnounceTime; refresh_countdown_lb( l, t ); } diff --git a/gtk/ipc.c b/gtk/ipc.c deleted file mode 100644 index 90454e7ff..000000000 --- a/gtk/ipc.c +++ /dev/null @@ -1,1180 +0,0 @@ -/****************************************************************************** - * $Id$ - * - * Copyright (c) 2006-2008 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 -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include /* g_unlink */ - -#include - -#include -#include -#include - -#include "conf.h" -#include "ipc.h" -#include "tr-core.h" -#include "tr-io.h" -#include "tr-prefs.h" -#include "tr-torrent.h" -#include "util.h" - -#ifndef AF_LOCAL -#define AF_LOCAL AF_UNIX -#endif - -#ifndef SUN_LEN -#define SUN_LEN( sun ) \ - ( sizeof( *(sun) ) - sizeof( (sun)->sun_path ) + strlen( (sun)->sun_path ) ) -#endif - -/* XXX error handling throughout this file is pretty bogus */ - -enum contype { CON_SERV, CON_CLIENT }; - -struct constate_serv -{ - GtkWindow * wind; - gpointer core; -}; - -struct constate_client -{ - GMainLoop * loop; - enum ipc_msg msg; - GSList * files; - gboolean * succeeded; - unsigned int msgid; -}; - -struct constate -{ - GSource * source; - int fd; - enum contype type; - struct ipc_funcs * msgs; - struct ipc_info * ipc; - union - { - struct constate_serv serv; - struct constate_client client; - } u; -}; - -/* this is only used on the server */ -static char *gl_sockpath = NULL; - -static gboolean -simpleresp( struct constate * con, int64_t tag, enum ipc_msg id ) -{ - uint8_t * buf; - size_t size; - - buf = ipc_mkempty( con->ipc, &size, id, tag ); - if( NULL == buf ) - { - return FALSE; - } - - io_send_keepdata( con->source, buf, size ); - - return TRUE; -} - -static void -all_default( enum ipc_msg id, tr_benc * val UNUSED, int64_t tag, void * arg ) -{ - switch( id ) - { - case IPC_MSG_FAIL: - case IPC_MSG_NOTSUP: - case IPC_MSG_BAD: - case IPC_MSG_OK: - break; - default: - simpleresp( arg, tag, IPC_MSG_NOTSUP ); - break; - } -} - -static void -destroycon(struct constate *con) { - con->source = NULL; - - if(0 <= con->fd) - EVUTIL_CLOSESOCKET(con->fd); - con->fd = -1; - ipc_freecon( con->ipc ); - - switch(con->type) { - case CON_SERV: - break; - case CON_CLIENT: - ipc_freemsgs( con->msgs ); - freestrlist(con->u.client.files); - g_main_loop_quit(con->u.client.loop); - break; - } -} - -static void -cli_io_sent( GSource * source UNUSED, size_t id, void *vdata ) -{ - struct constate_client *cli = &((struct constate*)vdata)->u.client; - - if(0 < id && cli->msgid == id) { - *(cli->succeeded) = TRUE; - destroycon(vdata); - } -} - -static void -client_sendmsg( struct constate * con ) -{ - struct constate_client * cli = &con->u.client; - GSList * ii; - uint8_t * buf; - size_t size; - tr_benc packet, * val; - int saved; - - switch( cli->msg ) - { - case IPC_MSG_ADDMANYFILES: - val = ipc_initval( con->ipc, cli->msg, -1, &packet, TYPE_LIST ); - if( NULL == val || - tr_bencListReserve( val, g_slist_length( cli->files ) ) ) - { - perror( "malloc" ); - destroycon( con ); - return; - } - for( ii = cli->files; NULL != ii; ii = ii->next ) - { - tr_bencInitStr( tr_bencListAdd( val ), ii->data, -1, 0 ); - } - buf = ipc_serialize( &packet, &size ); - saved = errno; - tr_bencFree( &packet ); - g_slist_free( cli->files ); - cli->files = NULL; - break; - case IPC_MSG_QUIT: - buf = ipc_mkempty( con->ipc, &size, cli->msg, -1 ); - saved = errno; - break; - default: - g_assert_not_reached(); - return; - } - - if( NULL == buf ) - { - errno = saved; - perror( "malloc" ); - destroycon( con ); - return; - } - - cli->msgid = io_send_keepdata( con->source, buf, size ); -} - -static size_t -cli_io_received( GSource * source UNUSED, void * data, size_t len, - void * vdata ) -{ - struct constate * con = vdata; - struct constate_client * cli = &con->u.client; - ssize_t res; - - if( IPC_MIN_MSG_LEN > len ) - { - return 0; - } - - res = ipc_handleMessages( con->ipc, data, len, con ); - - if( 0 > res ) - { - switch( errno ) - { - case EPERM: - g_message( _("Bad IPC protocol version") ); - break; - default: - g_message( _("IPC parse error: %s"), g_strerror( errno ) ); - break; - } - destroycon( con ); - return 0; - } - - if( ipc_hasvers( con->ipc ) && 0 == cli->msgid ) - { - client_sendmsg( con ); - } - - return res; -} - -static void -all_io_closed(GSource *source UNUSED, void *vdata) { - struct constate *con = vdata; - - destroycon(con); -} - -static gboolean -client_connect(char *path, struct constate *con) { - struct sockaddr_un addr; - uint8_t * buf; - size_t size; - - if(0 > (con->fd = socket(AF_UNIX, SOCK_STREAM, 0))) { - g_message( _("Couldn't create socket: %s"), g_strerror(errno)); - return FALSE; - } - - memset(&addr, 0, sizeof(addr)); - addr.sun_family = AF_UNIX; - strncpy(addr.sun_path, path, sizeof(addr.sun_path) - 1); - - if(0 > connect(con->fd, (struct sockaddr*)&addr, SUN_LEN(&addr))) { - g_message( _("Couldn't connect to \"%1$s\": %2$s"), path, g_strerror(errno)); - return FALSE; - } - - con->source = io_new(con->fd, cli_io_sent, cli_io_received, - all_io_closed, con); - if( NULL == con->source ) - { - EVUTIL_CLOSESOCKET( con->fd ); - return FALSE; - } - - buf = ipc_mkvers( &size, "Transmission GTK+ " LONG_VERSION_STRING ); - if( NULL == buf ) - { - EVUTIL_CLOSESOCKET( con->fd ); - return FALSE; - } - - io_send_keepdata( con->source, buf, size ); - - return TRUE; -} - -static gboolean -blocking_client( enum ipc_msg msgid, GSList * files ) -{ - - struct constate *con; - char *path; - gboolean ret = FALSE; - - con = g_new0(struct constate, 1); - con->source = NULL; - con->fd = -1; - con->type = CON_CLIENT; - - con->msgs = ipc_initmsgs(); - if( NULL == con->msgs ) - { - g_message( _("Couldn't set up IPC: %s"), g_strerror( errno ) ); - g_free( con ); - return FALSE; - } - - con->ipc = ipc_newcon( con->msgs ); - if( NULL == con->ipc ) - { - ipc_freemsgs( con->msgs ); - g_free( con ); - return FALSE; - } - - ipc_setdefmsg( con->msgs, all_default ); - - con->u.client.loop = g_main_loop_new(NULL, TRUE); - con->u.client.msg = msgid; - con->u.client.files = files; - con->u.client.succeeded = &ret; - con->u.client.msgid = 0; - - path = cf_sockname(); - if(!client_connect(path, con)) { - g_free(path); - destroycon(con); - return FALSE; - } - - g_main_loop_run(con->u.client.loop); - - sleep( 1 ); /* http://trac.transmissionbt.com/ticket/826#comment:6 */ - return ret; -} - -gboolean -ipc_sendfiles_blocking( GSList * files ) -{ - return blocking_client( IPC_MSG_ADDMANYFILES, files ); -} - -gboolean -ipc_sendquit_blocking( void ) -{ - return blocking_client( IPC_MSG_QUIT, NULL ); -} - -static void -rmsock(void) { - if( gl_sockpath) { - g_unlink(gl_sockpath); - g_free(gl_sockpath); - } -} - -static size_t -srv_io_received( GSource * source UNUSED, void * data, size_t len, - void * vdata ) -{ - struct constate * con = vdata; - struct constate_serv * srv = &con->u.serv; - ssize_t res; - - if( IPC_MIN_MSG_LEN > len ) - { - return 0; - } - - if( NULL == srv->core ) - { - destroycon( con ); - } - - res = ipc_handleMessages( con->ipc, data, len, con ); - - if( 0 > res ) - { - switch( errno ) - { - case EPERM: - errmsg( con->u.serv.wind, _("Bad IPC protocol version") ); - break; - default: - errmsg( con->u.serv.wind, _("IPC parse error: %s"), g_strerror( errno ) ); - break; - } - destroycon( con ); - return 0; - } - - return res; -} - -static void -srv_io_accept(GSource *source UNUSED, int fd, struct sockaddr *sa UNUSED, - socklen_t len UNUSED, void *vdata) { - struct constate *con = vdata; - struct constate *newcon; - uint8_t * buf; - size_t size; - - newcon = g_new(struct constate, 1); - memcpy(newcon, con, sizeof(*newcon)); - newcon->fd = fd; - - newcon->ipc = ipc_newcon( con->msgs ); - if( NULL == newcon->ipc ) - { - g_free( newcon ); - EVUTIL_CLOSESOCKET( fd ); - return; - } - - newcon->source = io_new(fd, NULL, srv_io_received, all_io_closed, newcon); - if( NULL == newcon->source ) - { - ipc_freecon( newcon->ipc ); - g_free( newcon ); - EVUTIL_CLOSESOCKET( fd ); - return; - } - - buf = ipc_mkvers( &size, "Transmission GTK+ " LONG_VERSION_STRING ); - if( NULL == buf ) - { - ipc_freecon( newcon->ipc ); - g_free( newcon ); - EVUTIL_CLOSESOCKET( fd ); - return; - } - - io_send_keepdata( newcon->source, buf, size ); -} - -/* open a local socket for clients connections */ -static void -serv_bind(struct constate *con) { - struct sockaddr_un sa; - - rmsock(); - gl_sockpath = cf_sockname(); - - if(0 > (con->fd = socket(AF_LOCAL, SOCK_STREAM, 0))) - goto fail; - - memset(&sa, 0, sizeof(sa)); - sa.sun_family = AF_LOCAL; - strncpy(sa.sun_path, gl_sockpath, sizeof(sa.sun_path) - 1); - - /* unlink any existing socket file before trying to create ours */ - g_unlink(gl_sockpath); - if(0 > bind(con->fd, (struct sockaddr *)&sa, SUN_LEN(&sa))) { - /* bind may fail if there was already a socket, so try twice */ - g_unlink(gl_sockpath); - if(0 > bind(con->fd, (struct sockaddr *)&sa, SUN_LEN(&sa))) - goto fail; - } - - if(0 > listen(con->fd, 5)) - goto fail; - - con->source = io_new_listening(con->fd, sizeof(struct sockaddr_un), - srv_io_accept, all_io_closed, con); - - g_atexit(rmsock); - - return; - - fail: - errmsg(con->u.serv.wind, _("Couldn't create socket: %s"), - g_strerror(errno)); - if(0 <= con->fd) - EVUTIL_CLOSESOCKET(con->fd); - con->fd = -1; - rmsock(); -} - -static void -smsg_add( enum ipc_msg id UNUSED, tr_benc * val, int64_t tag, void * arg ) -{ - struct constate * con = arg; - struct constate_serv * srv = &con->u.serv; - tr_benc * path; - int ii; - GSList * list = NULL; - - if( !tr_bencIsList( val ) ) - { - simpleresp( con, tag, IPC_MSG_BAD ); - return; - } - - for( ii = 0; ii < val->val.l.count; ii++ ) - { - path = val->val.l.vals + ii; - if( TYPE_STR == path->type && - /* XXX somehow escape invalid utf-8 */ - g_utf8_validate( path->val.s.s, path->val.s.i, NULL ) ) - { - list = g_slist_prepend( list, g_strndup( path->val.s.s, path->val.s.i ) ); - } - } - - if( list ) { - list = g_slist_reverse( list ); - tr_core_add_list_defaults( srv->core, list ); - tr_core_torrents_added( TR_CORE( srv->core ) ); - } - - /* XXX should send info response back with torrent ids */ - simpleresp( con, tag, IPC_MSG_OK ); -} - -static void -smsg_addone( enum ipc_msg id UNUSED, tr_benc * val, int64_t tag, - void * arg ) -{ - struct constate * con = arg; - struct constate_serv * srv = &con->u.serv; - tr_benc * file, * data, * dir, * start; - tr_ctor * ctor; - - if( !tr_bencIsDict( val ) ) - { - simpleresp( con, tag, IPC_MSG_BAD ); - return; - } - - file = tr_bencDictFind( val, "file" ); - data = tr_bencDictFind( val, "data" ); - dir = tr_bencDictFind( val, "directory" ); - start = tr_bencDictFind( val, "autostart" ); - - if( ( file && !tr_bencIsString( file ) ) || - ( data && !tr_bencIsString( data ) ) || - ( dir && !tr_bencIsString( dir ) ) || - ( start && !tr_bencIsInt( start ) ) ) - { - simpleresp( con, tag, IPC_MSG_BAD ); - return; - } - - ctor = tr_ctorNew( tr_core_handle( srv->core ) ); - if( dir ) - tr_ctorSetDestination( ctor, TR_FORCE, dir->val.s.s ); - if( file ) - tr_ctorSetMetainfoFromFile( ctor, file->val.s.s ); - if( data ) - tr_ctorSetMetainfo( ctor, (uint8_t*)data->val.s.s, data->val.s.i ); - if( start ) - tr_ctorSetPaused( ctor, TR_FORCE, !start->val.i ); - - tr_core_add_ctor( TR_CORE( srv->core ), ctor ); - - tr_core_torrents_added( TR_CORE( srv->core ) ); - - /* XXX should send info response back with torrent ids */ - simpleresp( con, tag, IPC_MSG_OK ); -} - -static void -smsg_quit( enum ipc_msg id UNUSED, tr_benc * val UNUSED, int64_t tag UNUSED, - void * arg UNUSED ) -{ - struct constate * con = arg; - struct constate_serv * srv = &con->u.serv; - - tr_core_quit( srv->core ); -} - -static void -smsg_noop( enum ipc_msg id UNUSED, tr_benc * val UNUSED, int64_t tag, - void * arg ) -{ - simpleresp( arg, tag, IPC_MSG_OK ); -} - -static TrTorrent * -findtorid( TrCore * core, int id, GtkTreeIter * iter ) -{ - GtkTreeModel * model; - GtkTreeIter myiter; - int rowid; - TrTorrent * tor; - - if( NULL == iter ) - { - iter = &myiter; - } - - model = tr_core_model( core ); - if( gtk_tree_model_get_iter_first( model, iter ) ) - { - do - { - gtk_tree_model_get( model, iter, MC_ID, &rowid, -1 ); - if( rowid == id ) - { - gtk_tree_model_get( model, iter, MC_TORRENT, &tor, -1 ); - g_object_unref( tor ); - return tor; - } - } - while( gtk_tree_model_iter_next( model, iter ) ); - } - - return NULL; -} - -static int -addinfo( TrTorrent * gtor, enum ipc_msg msgid, int torid, int types, - tr_benc * val ) -{ - tr_torrent * tor = tr_torrent_handle( gtor ); - - if( IPC_MSG_INFO == msgid ) - return ipc_addinfo( val, torid, tor, types ); - else - return ipc_addstat( val, torid, tor, types ); -} - -static void -smsg_info( enum ipc_msg id, tr_benc * val, int64_t tag, void * arg ) -{ - struct constate * con = arg; - struct constate_serv * srv = &con->u.serv; - enum ipc_msg respid; - tr_benc * ids, * types, * idval, packet, * pkval; - int typeflags, ii; - TrTorrent * tor; - uint8_t * buf; - size_t size; - - if( !tr_bencIsDict( val ) ) - { - simpleresp( con, tag, IPC_MSG_BAD ); - return; - } - - respid = ( IPC_MSG_GETINFO == id ? IPC_MSG_INFO : IPC_MSG_STAT ); - ids = tr_bencDictFind( val, "id" ); - types = tr_bencDictFind( val, "types" ); - if( !tr_bencIsList(ids) || !tr_bencIsList(types) ) - { - simpleresp( con, tag, IPC_MSG_BAD ); - return; - } - typeflags = ipc_infotypes( respid, types ); - - pkval = ipc_initval( con->ipc, respid, tag, &packet, TYPE_LIST ); - if( NULL == pkval ) - { - simpleresp( con, tag, IPC_MSG_FAIL ); - return; - } - for( ii = 0; ids->val.l.count > ii; ii++ ) - { - idval = &ids->val.l.vals[ii]; - if( TYPE_INT != idval->type || !TORRENT_ID_VALID( idval->val.i ) || - NULL == ( tor = findtorid( srv->core, idval->val.i, NULL ) ) ) - { - continue; - } - if( 0 > addinfo( tor, respid, idval->val.i, typeflags, pkval ) ) - { - tr_bencFree( &packet ); - simpleresp( con, tag, IPC_MSG_FAIL ); - return; - } - } - - buf = ipc_serialize( &packet, &size ); - tr_bencFree( &packet ); - if( NULL == buf ) - { - simpleresp( con, tag, IPC_MSG_FAIL ); - } - else - { - io_send_keepdata( con->source, buf, size ); - } -} - -static void -smsg_infoall( enum ipc_msg id, tr_benc * val, int64_t tag, void * arg ) -{ - struct constate * con = arg; - struct constate_serv * srv = &con->u.serv; - enum ipc_msg respid; - tr_benc packet, * pkval; - int typeflags; - GtkTreeModel * model; - GtkTreeIter iter; - int rowid; - TrTorrent * tor; - uint8_t * buf; - size_t size; - - if( !tr_bencIsList( val ) ) - { - simpleresp( con, tag, IPC_MSG_BAD ); - return; - } - - respid = ( IPC_MSG_GETINFOALL == id ? IPC_MSG_INFO : IPC_MSG_STAT ); - typeflags = ipc_infotypes( respid, val ); - - pkval = ipc_initval( con->ipc, respid, tag, &packet, TYPE_LIST ); - if( NULL == pkval ) - { - simpleresp( con, tag, IPC_MSG_FAIL ); - return; - } - - model = tr_core_model( srv->core ); - if( gtk_tree_model_get_iter_first( model, &iter ) ) - { - do - { - gtk_tree_model_get( model, &iter, MC_ID, &rowid, - MC_TORRENT, &tor, -1 ); - g_object_unref( tor ); - if( 0 > addinfo( tor, respid, rowid, typeflags, pkval ) ) - { - tr_bencFree( &packet ); - simpleresp( con, tag, IPC_MSG_FAIL ); - return; - } - } - while( gtk_tree_model_iter_next( model, &iter ) ); - } - - buf = ipc_serialize( &packet, &size ); - tr_bencFree( &packet ); - if( NULL == buf ) - { - simpleresp( con, tag, IPC_MSG_FAIL ); - } - else - { - io_send_keepdata( con->source, buf, size ); - } -} - -static TrTorrent * -findtorhash( TrCore * core, const char * hash, int * torid ) -{ - GtkTreeModel * model; - GtkTreeIter iter; - char * rowhash; - TrTorrent * tor; - - model = tr_core_model( core ); - if( gtk_tree_model_get_iter_first( model, &iter ) ) - { - do - { - gtk_tree_model_get( model, &iter, MC_HASH, &rowhash, -1 ); - if( 0 == strcmp( hash, rowhash ) ) - { - gtk_tree_model_get( model, &iter, MC_ID, torid, - MC_TORRENT, &tor, -1 ); - g_object_unref( tor ); - return tor; - } - } - while( gtk_tree_model_iter_next( model, &iter ) ); - } - - return NULL; -} - -static void -smsg_look( enum ipc_msg id UNUSED, tr_benc * val, int64_t tag, - void * arg ) -{ - struct constate * con = arg; - struct constate_serv * srv = &con->u.serv; - tr_benc packet, * pkval, * hash; - int ii, torid; - TrTorrent * tor; - uint8_t * buf; - size_t size; - - if( !tr_bencIsList( val ) ) - { - simpleresp( con, tag, IPC_MSG_BAD ); - return; - } - - pkval = ipc_initval( con->ipc, IPC_MSG_INFO, tag, &packet, TYPE_LIST ); - if( NULL == pkval ) - { - simpleresp( con, tag, IPC_MSG_FAIL ); - return; - } - - for( ii = 0; val->val.l.count > ii; ii++ ) - { - hash = &val->val.l.vals[ii]; - if( NULL == hash || TYPE_STR != hash->type || - SHA_DIGEST_LENGTH * 2 != hash->val.s.i || - NULL == ( tor = findtorhash( srv->core, hash->val.s.s, &torid ) ) ) - { - continue; - } - if( 0 > ipc_addinfo( pkval, torid, tr_torrent_handle( tor ), IPC_INF_HASH ) ) - { - tr_bencFree( &packet ); - simpleresp( con, tag, IPC_MSG_FAIL ); - return; - } - } - - buf = ipc_serialize( &packet, &size ); - tr_bencFree( &packet ); - if( NULL == buf ) - { - simpleresp( con, tag, IPC_MSG_FAIL ); - } - else - { - io_send_keepdata( con->source, buf, size ); - } -} - -static void -smsg_tor( enum ipc_msg id, tr_benc * val, int64_t tag, void * arg ) -{ - struct constate * con = arg; - struct constate_serv * srv = &con->u.serv; - tr_benc * idval; - TrTorrent * tor; - GtkTreeIter iter; - int ii; - - if( !tr_bencIsList( val ) ) - { - simpleresp( con, 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 ) || - NULL == ( tor = findtorid( srv->core, idval->val.i, &iter ) ) ) - { - continue; - } - switch( id ) - { - case IPC_MSG_REMOVE: - tr_core_delete_torrent( srv->core, &iter ); - break; - case IPC_MSG_START: - tr_torrent_start( tor ); - break; - case IPC_MSG_STOP: - tr_torrent_stop( tor ); - break; - case IPC_MSG_VERIFY: - tr_torrentVerify( tr_torrent_handle( tor ) ); - break; - default: - g_assert_not_reached(); - break; - } - } - - tr_core_update( srv->core ); - - /* XXX this is a lie */ - simpleresp( con, tag, IPC_MSG_OK ); -} - -static void -smsg_torall( enum ipc_msg id, tr_benc * val UNUSED, int64_t tag, - void * arg ) -{ - struct constate * con = arg; - struct constate_serv * srv = &con->u.serv; - TrTorrent * tor; - GtkTreeModel * model; - GtkTreeIter iter; - - model = tr_core_model( srv->core ); - if( gtk_tree_model_get_iter_first( model, &iter ) ) - { - do - { - gtk_tree_model_get( model, &iter, MC_TORRENT, &tor, -1 ); - switch( id ) - { - case IPC_MSG_REMOVEALL: - tr_core_delete_torrent( srv->core, &iter ); - break; - case IPC_MSG_STARTALL: - tr_torrent_start( tor ); - break; - case IPC_MSG_STOPALL: - tr_torrent_stop( tor ); - break; - default: - g_assert_not_reached(); - break; - } - g_object_unref( tor ); - } - while( gtk_tree_model_iter_next( model, &iter ) ); - } - - tr_core_update( srv->core ); - - /* XXX this is a lie */ - simpleresp( con, tag, IPC_MSG_OK ); -} - -#define TR_NAT_TRAVERSAL_IS_DISABLED( st ) \ - ( TR_NAT_TRAVERSAL_UNMAPPED == (st) || TR_NAT_TRAVERSAL_UNMAPPING == (st) ) - -static void -smsg_pref( enum ipc_msg id, tr_benc * val UNUSED, int64_t tag, void * arg ) -{ - struct constate * con = arg; - struct constate_serv * srv = &con->u.serv; - uint8_t * buf; - size_t size; - const tr_handle_status * hstat; - const char * pref; - int num; - - switch( id ) - { - case IPC_MSG_GETAUTOMAP: - hstat = tr_handleStatus( tr_core_handle( srv->core ) ); - buf = ipc_mkint( con->ipc, &size, IPC_MSG_AUTOMAP, tag, - !TR_NAT_TRAVERSAL_IS_DISABLED( - hstat->natTraversalStatus ) ); - break; - case IPC_MSG_GETAUTOSTART: - buf = ipc_mkint( con->ipc, &size, IPC_MSG_AUTOSTART, tag, 1 ); - break; - case IPC_MSG_GETDIR: - /* XXX sending back "" when we're prompting is kind of bogus */ - pref = pref_flag_get( PREF_KEY_OPTIONS_PROMPT ) ? "" : getdownloaddir(); - buf = ipc_mkstr( con->ipc, &size, IPC_MSG_DIR, tag, pref ); - break; - case IPC_MSG_GETDOWNLIMIT: - num = pref_flag_get( PREF_KEY_DL_LIMIT_ENABLED ) - ? pref_int_get( PREF_KEY_DL_LIMIT ) - : -1; - buf = ipc_mkint( con->ipc, &size, IPC_MSG_DOWNLIMIT, tag, num ); - break; - case IPC_MSG_GETPEX: - buf = ipc_mkint( con->ipc, &size, IPC_MSG_PEX, tag, - pref_flag_get( PREF_KEY_PEX ) ); - break; - case IPC_MSG_GETPORT: - buf = ipc_mkint( con->ipc, &size, IPC_MSG_PORT, tag, - pref_flag_get( PREF_KEY_PORT ) ); - break; - case IPC_MSG_GETUPLIMIT: - num = pref_flag_get( PREF_KEY_UL_LIMIT_ENABLED ) - ? pref_int_get( PREF_KEY_UL_LIMIT ) - : -1; - buf = ipc_mkint( con->ipc, &size, IPC_MSG_UPLIMIT, tag, num ); - break; - default: - g_assert_not_reached(); - return; - } - - if( NULL != buf ) - { - io_send_keepdata( con->source, buf, size ); - } -} - -static void -smsg_int( enum ipc_msg id, tr_benc * val, int64_t tag, void * arg ) -{ - struct constate * con = arg; - struct constate_serv * srv = &con->u.serv; - - if( !tr_bencIsInt( val ) || INT_MAX < val->val.i ) - { - simpleresp( con, tag, IPC_MSG_BAD ); - return; - } - - switch( id ) - { - case IPC_MSG_AUTOMAP: - tr_core_set_pref_bool( srv->core, PREF_KEY_NAT, val->val.i ); - break; - case IPC_MSG_AUTOSTART: - simpleresp( con, tag, IPC_MSG_BAD ); - return; - case IPC_MSG_DOWNLIMIT: - if( 0 > val->val.i ) - { - tr_core_set_pref_bool( srv->core, PREF_KEY_DL_LIMIT_ENABLED, 0 ); - } - else - { - tr_core_set_pref_int( srv->core, PREF_KEY_DL_LIMIT, val->val.i ); - tr_core_set_pref_bool( srv->core, PREF_KEY_DL_LIMIT_ENABLED, 1 ); - } - break; - case IPC_MSG_PEX: - tr_core_set_pref_bool( srv->core, PREF_KEY_PEX, val->val.i ); - break; - case IPC_MSG_PORT: - tr_core_set_pref_int( srv->core, PREF_KEY_PORT, val->val.i ); - break; - case IPC_MSG_UPLIMIT: - if( 0 > val->val.i ) - { - tr_core_set_pref_bool( srv->core, PREF_KEY_UL_LIMIT_ENABLED, 0 ); - } - else - { - tr_core_set_pref_int( srv->core, PREF_KEY_UL_LIMIT, val->val.i ); - tr_core_set_pref_bool( srv->core, PREF_KEY_UL_LIMIT_ENABLED, 1 ); - } - break; - default: - g_assert_not_reached(); - return; - } -} - -static void -smsg_str( enum ipc_msg id, tr_benc * val, int64_t tag, void * arg ) -{ - struct constate * con = arg; - struct constate_serv * srv = &con->u.serv; - - if( !tr_bencIsString( val ) ) - { - simpleresp( con, tag, IPC_MSG_BAD ); - return; - } - - switch( id ) - { - case IPC_MSG_DIR: - tr_core_set_pref( srv->core, PREF_KEY_DIR_DEFAULT, val->val.s.s ); - break; - default: - g_assert_not_reached(); - return; - } -} - -static void -smsg_sup( enum ipc_msg id UNUSED, tr_benc * val, int64_t tag, void * arg ) -{ - struct constate * con = arg; - tr_benc packet, * pkval; - int ii; - enum ipc_msg found; - uint8_t * buf; - size_t size; - - if( !tr_bencIsList( val ) ) - { - simpleresp( con, tag, IPC_MSG_BAD ); - return; - } - - pkval = ipc_initval( con->ipc, IPC_MSG_SUP, tag, &packet, TYPE_LIST ); - if( NULL == pkval ) - { - simpleresp( con, tag, IPC_MSG_FAIL ); - return; - } - if( tr_bencListReserve( pkval, val->val.l.count ) ) - { - tr_bencFree( &packet ); - simpleresp( con, tag, IPC_MSG_FAIL ); - return; - } - - for( ii = 0; val->val.l.count > ii; ii++ ) - { - tr_benc * name = &val->val.l.vals[ii]; - - if( !tr_bencIsString( name ) ) - continue; - - found = ipc_msgid( con->ipc, name->val.s.s ); - if( IPC__MSG_COUNT == found || !ipc_ishandled( con->ipc, found ) ) - continue; - - tr_bencInitStr( tr_bencListAdd( pkval ), - name->val.s.s, name->val.s.i, 1 ); - } - - buf = ipc_serialize( &packet, &size ); - tr_bencFree( &packet ); - - if( !buf ) - simpleresp( con, tag, IPC_MSG_FAIL ); - else - io_send_keepdata( con->source, buf, size ); -} - -void -ipc_socket_setup( GtkWindow * parent, TrCore * core ) -{ - struct constate *con; - - con = g_new0(struct constate, 1); - con->source = NULL; - con->fd = -1; - con->type = CON_SERV; - - con->msgs = ipc_initmsgs(); - if( NULL == con->msgs ) - { - errmsg( con->u.serv.wind, _("Couldn't set up IPC: %s"), - g_strerror( errno ) ); - g_free( con ); - return; - } - - ipc_addmsg( con->msgs, IPC_MSG_ADDMANYFILES, smsg_add ); - ipc_addmsg( con->msgs, IPC_MSG_ADDONEFILE, smsg_addone ); - ipc_addmsg( con->msgs, IPC_MSG_AUTOMAP, smsg_int ); - ipc_addmsg( con->msgs, IPC_MSG_AUTOSTART, smsg_int ); - ipc_addmsg( con->msgs, IPC_MSG_DIR, smsg_str ); - ipc_addmsg( con->msgs, IPC_MSG_DOWNLIMIT, smsg_int ); - ipc_addmsg( con->msgs, IPC_MSG_GETAUTOMAP, smsg_pref ); - ipc_addmsg( con->msgs, IPC_MSG_GETAUTOSTART, smsg_pref ); - ipc_addmsg( con->msgs, IPC_MSG_GETDIR, smsg_pref ); - ipc_addmsg( con->msgs, IPC_MSG_GETDOWNLIMIT, smsg_pref ); - ipc_addmsg( con->msgs, IPC_MSG_GETINFO, smsg_info ); - ipc_addmsg( con->msgs, IPC_MSG_GETINFOALL, smsg_infoall ); - ipc_addmsg( con->msgs, IPC_MSG_GETPEX, smsg_pref ); - ipc_addmsg( con->msgs, IPC_MSG_GETPORT, smsg_pref ); - ipc_addmsg( con->msgs, IPC_MSG_GETSTAT, smsg_info ); - ipc_addmsg( con->msgs, IPC_MSG_GETSTATALL, smsg_infoall ); - ipc_addmsg( con->msgs, IPC_MSG_GETUPLIMIT, smsg_pref ); - ipc_addmsg( con->msgs, IPC_MSG_LOOKUP, smsg_look ); - ipc_addmsg( con->msgs, IPC_MSG_NOOP, smsg_noop ); - ipc_addmsg( con->msgs, IPC_MSG_PEX, smsg_int ); - ipc_addmsg( con->msgs, IPC_MSG_PORT, smsg_int ); - ipc_addmsg( con->msgs, IPC_MSG_QUIT, smsg_quit ); - ipc_addmsg( con->msgs, IPC_MSG_REMOVE, smsg_tor ); - ipc_addmsg( con->msgs, IPC_MSG_REMOVEALL, smsg_torall ); - ipc_addmsg( con->msgs, IPC_MSG_START, smsg_tor ); - ipc_addmsg( con->msgs, IPC_MSG_STARTALL, smsg_torall ); - ipc_addmsg( con->msgs, IPC_MSG_STOP, smsg_tor ); - ipc_addmsg( con->msgs, IPC_MSG_STOPALL, smsg_torall ); - ipc_addmsg( con->msgs, IPC_MSG_SUP, smsg_sup ); - ipc_addmsg( con->msgs, IPC_MSG_UPLIMIT, smsg_int ); - ipc_addmsg( con->msgs, IPC_MSG_VERIFY, smsg_tor ); - - ipc_setdefmsg( con->msgs, all_default ); - - con->u.serv.wind = parent; - con->u.serv.core = core; - - g_object_add_weak_pointer( G_OBJECT( core ), &con->u.serv.core ); - - serv_bind(con); -} diff --git a/gtk/ipc.h b/gtk/ipc.h deleted file mode 100644 index 0558a31e7..000000000 --- a/gtk/ipc.h +++ /dev/null @@ -1,41 +0,0 @@ -/****************************************************************************** - * $Id$ - * - * Copyright (c) 2006-2008 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. - *****************************************************************************/ - -#ifndef TG_IPC_H -#define TG_IPC_H - -#include - -#include "tr-core.h" - -void -ipc_socket_setup( GtkWindow * wind, TrCore * core ); - -gboolean -ipc_sendfiles_blocking( GSList * files ); - -gboolean -ipc_sendquit_blocking( void ); - -#endif /* TG_IPC_H */ diff --git a/gtk/main.c b/gtk/main.c index 678c5343e..22af872be 100644 --- a/gtk/main.c +++ b/gtk/main.c @@ -48,7 +48,6 @@ #include "details.h" #include "dialogs.h" #include "hig.h" -#include "ipc.h" #include "makemeta-ui.h" #include "msgwin.h" #include "notify.h" @@ -123,8 +122,6 @@ struct cbdata static GtkUIManager * myUIManager = NULL; -static gboolean -sendremote( GSList * files, gboolean sendquit ); static void appsetup( TrWindow * wind, GSList * args, struct cbdata *, @@ -282,18 +279,67 @@ setupsighandlers( void ) signal( sigs[i], fatalsig ); } +struct rpc_data +{ + int type; + int torrentId; + tr_torrent * tor; + struct cbdata * cbdata; +}; + +static int +onRPCIdle( void * vdata ) +{ + struct rpc_data * data = vdata; + switch( data->type ) + { + case TR_RPC_TORRENT_ADDED: + tr_core_add_torrent( data->cbdata->core, tr_torrent_new_preexisting( data->tor ) ); + break; + case TR_RPC_TORRENT_STARTED: + /* this should be automatic */ + break; + case TR_RPC_TORRENT_STOPPED: + /* this should be automatic */ + break; + case TR_RPC_TORRENT_CLOSING: + /* FIXME */ + break; + case TR_RPC_TORRENT_CHANGED: + case TR_RPC_SESSION_CHANGED: + /* nothing interesting to do here */ + break; + } + g_free( data ); + return FALSE; +} + +static void +onRPCChanged( tr_handle * handle UNUSED, + tr_rpc_callback_type type, + struct tr_torrent * tor, + void * cbdata ) +{ + /* this callback is being invoked from the libtransmission thread, + * so let's push the information over to the gtk+ thread where + * it's safe to update the gui */ + struct rpc_data * data = g_new0( struct rpc_data, 1 ); + data->type = type; + data->torrentId = tor ? tr_torrentId( tor ) : -1; + data->tor = type == TR_RPC_TORRENT_CLOSING ? NULL : tor; + data->cbdata = cbdata; + g_idle_add( onRPCIdle, data ); +} + int main( int argc, char ** argv ) { - gboolean do_inhibit; - guint inhibit_cookie; char * err = NULL; struct cbdata * cbdata; GSList * argfiles; GError * gerr; gboolean didinit = FALSE; gboolean didlock = FALSE; - gboolean sendquit = FALSE; gboolean startpaused = FALSE; gboolean startminimized = FALSE; char * domain = "transmission"; @@ -302,8 +348,6 @@ main( int argc, char ** argv ) GOptionEntry entries[] = { { "paused", 'p', 0, G_OPTION_ARG_NONE, &startpaused, _("Start with all torrents paused"), NULL }, - { "quit", 'q', 0, G_OPTION_ARG_NONE, &sendquit, - _( "Ask the running instance to quit"), NULL }, #ifdef STATUS_ICON_SUPPORTED { "minimized", 'm', 0, G_OPTION_ARG_NONE, &startminimized, _( "Start minimized in system tray"), NULL }, @@ -331,7 +375,7 @@ main( int argc, char ** argv ) g_clear_error( &gerr ); return 0; } - if( !configDir ) + if( configDir == NULL ) configDir = (char*) tr_getDefaultConfigDir( ); tr_notify_init( ); @@ -344,30 +388,50 @@ main( int argc, char ** argv ) gtk_ui_manager_ensure_update (myUIManager); gtk_window_set_default_icon_name ( "transmission" ); - argfiles = checkfilenames( argc-1, argv+1 ); - didlock = didinit && sendremote( argfiles, sendquit ); setupsighandlers( ); /* set up handlers for fatal signals */ - if( ( didinit || cf_init( configDir, &err ) ) && - ( didlock || cf_lock( &err ) ) ) + /* either get a lockfile s.t. this is the one instance of + * transmission that's running, OR if there are files to + * be added, delegate that to the running instance via dbus */ + didlock = cf_lock( &err ); + argfiles = checkfilenames( argc-1, argv+1 ); + if( !didlock && argfiles ) + { + GSList * l; + gboolean delegated = FALSE; + for( l=argfiles; l; l=l->next ) + delegated |= gtr_dbus_add_torrent( l->data ); + if( delegated ) + err = NULL; + } + + if( didlock && ( didinit || cf_init( configDir, &err ) ) ) { - tr_handle * h = tr_initFull( configDir, - "gtk", - pref_flag_get( PREF_KEY_PEX ), - pref_flag_get( PREF_KEY_NAT ), - pref_int_get( PREF_KEY_PORT ), - pref_flag_get( PREF_KEY_ENCRYPTED_ONLY ) - ? TR_ENCRYPTION_REQUIRED - : TR_ENCRYPTION_PREFERRED, - pref_flag_get( PREF_KEY_UL_LIMIT_ENABLED ), - pref_int_get( PREF_KEY_UL_LIMIT ), - pref_flag_get( PREF_KEY_DL_LIMIT_ENABLED ), - pref_int_get( PREF_KEY_DL_LIMIT ), - pref_int_get( PREF_KEY_MAX_PEERS_GLOBAL ), - pref_int_get( PREF_KEY_MSGLEVEL ), - TRUE, /* message queueing */ - pref_flag_get( PREF_KEY_BLOCKLIST_ENABLED ), - pref_int_get( PREF_KEY_PEER_SOCKET_TOS ) ); + gboolean do_inhibit = FALSE; + guint inhibit_cookie = 0; + + tr_handle * h = tr_sessionInitFull( + configDir, + "gtk", + pref_string_get( PREF_KEY_DOWNLOAD_DIR ), + pref_flag_get( PREF_KEY_PEX ), + pref_flag_get( PREF_KEY_NAT ), + pref_int_get( PREF_KEY_PORT ), + pref_flag_get( PREF_KEY_ENCRYPTED_ONLY ) + ? TR_ENCRYPTION_REQUIRED + : TR_ENCRYPTION_PREFERRED, + pref_flag_get( PREF_KEY_UL_LIMIT_ENABLED ), + pref_int_get( PREF_KEY_UL_LIMIT ), + pref_flag_get( PREF_KEY_DL_LIMIT_ENABLED ), + pref_int_get( PREF_KEY_DL_LIMIT ), + pref_int_get( PREF_KEY_MAX_PEERS_GLOBAL ), + pref_int_get( PREF_KEY_MSGLEVEL ), + TRUE, /* message queueing */ + pref_flag_get( PREF_KEY_BLOCKLIST_ENABLED ), + pref_int_get( PREF_KEY_PEER_SOCKET_TOS ), + pref_flag_get( PREF_KEY_RPC_ENABLED ), + pref_int_get( PREF_KEY_RPC_PORT ), + pref_string_get( PREF_KEY_RPC_ACL ) ); cbdata->core = tr_core_new( h ); /* create main window now to be a parent to any error dialogs */ @@ -376,42 +440,27 @@ main( int argc, char ** argv ) g_signal_connect( win, "size-allocate", G_CALLBACK(onMainWindowSizeAllocated), cbdata ); appsetup( win, argfiles, cbdata, startpaused, startminimized ); + tr_sessionSetRPCCallback( h, onRPCChanged, cbdata ); + + if(( do_inhibit = pref_flag_get( PREF_KEY_INHIBIT_HIBERNATION ))) + inhibit_cookie = gtr_inhibit_hibernation( ); + + gtk_main(); + + if( do_inhibit && inhibit_cookie ) + gtr_uninhibit_hibernation( inhibit_cookie ); } - else + else if( err ) { gtk_widget_show( errmsg_full( NULL, (callbackfunc_t)gtk_main_quit, NULL, "%s", err ) ); g_free( err ); + gtk_main(); } - do_inhibit = pref_flag_get( PREF_KEY_INHIBIT_HIBERNATION ); - if( do_inhibit ) - inhibit_cookie = gtr_inhibit_hibernation( ); - - gtk_main(); - - if( do_inhibit && inhibit_cookie ) - gtr_uninhibit_hibernation( inhibit_cookie ); - return 0; } -static gboolean -sendremote( GSList * files, gboolean sendquit ) -{ - const gboolean didlock = cf_lock( NULL ); - - /* send files if there's another instance, otherwise start normally */ - if( !didlock && files ) - exit( ipc_sendfiles_blocking( files ) ? 0 : 1 ); - - /* either send a quit message or exit if no other instance */ - if( sendquit ) - exit( didlock ? 0 : !ipc_sendquit_blocking() ); - - return didlock; -} - static void appsetup( TrWindow * wind, GSList * torrentFiles, struct cbdata * cbdata, @@ -447,9 +496,6 @@ appsetup( TrWindow * wind, GSList * torrentFiles, torrentFiles = NULL; tr_core_torrents_added( cbdata->core ); - /* set up the ipc socket */ - ipc_socket_setup( GTK_WINDOW( wind ), cbdata->core ); - /* set up main window */ winsetup( cbdata, wind ); @@ -889,7 +935,19 @@ prefschanged( TrCore * core UNUSED, const char * key, gpointer data ) else if( !strcmp( key, PREF_KEY_PEX ) ) { const gboolean b = pref_flag_get( key ); - tr_sessionSetPortForwardingEnabled( tr_core_handle( cbdata->core ), b ); + tr_sessionSetPortForwardingEnabled( tr, b ); + } + else if( !strcmp( key, PREF_KEY_RPC_ENABLED ) ) + { + tr_sessonSetRPCEnabled( tr, pref_flag_get( key ) ); + } + else if( !strcmp( key, PREF_KEY_RPC_PORT ) ) + { + tr_sessonSetRPCPort( tr, pref_int_get( key ) ); + } + else if( !strcmp( key, PREF_KEY_RPC_ENABLED ) ) + { + g_message( "preferences option not recognized: %s", key ); } } diff --git a/gtk/notify.c b/gtk/notify.c index efe35f20d..5ee98b649 100644 --- a/gtk/notify.c +++ b/gtk/notify.c @@ -46,7 +46,7 @@ notifyCallback( NotifyNotification * n UNUSED, { tr_torrent * tor = tr_torrent_handle( gtor ); const tr_info * info = tr_torrent_info( gtor ); - char * path = g_build_filename( tr_torrentGetFolder(tor), info->files[0].name, NULL ); + char * path = g_build_filename( tr_torrentGetDownloadDir(tor), info->files[0].name, NULL ); gtr_open_file( path ); g_free( path ); } diff --git a/gtk/tr-core-dbus.h b/gtk/tr-core-dbus.h new file mode 100644 index 000000000..69124c2b3 --- /dev/null +++ b/gtk/tr-core-dbus.h @@ -0,0 +1,122 @@ +/* Generated by dbus-binding-tool; do not edit! */ + + +#ifndef __dbus_glib_marshal_tr_core_MARSHAL_H__ +#define __dbus_glib_marshal_tr_core_MARSHAL_H__ + +#include + +G_BEGIN_DECLS + +#ifdef G_ENABLE_DEBUG +#define g_marshal_value_peek_boolean(v) g_value_get_boolean (v) +#define g_marshal_value_peek_char(v) g_value_get_char (v) +#define g_marshal_value_peek_uchar(v) g_value_get_uchar (v) +#define g_marshal_value_peek_int(v) g_value_get_int (v) +#define g_marshal_value_peek_uint(v) g_value_get_uint (v) +#define g_marshal_value_peek_long(v) g_value_get_long (v) +#define g_marshal_value_peek_ulong(v) g_value_get_ulong (v) +#define g_marshal_value_peek_int64(v) g_value_get_int64 (v) +#define g_marshal_value_peek_uint64(v) g_value_get_uint64 (v) +#define g_marshal_value_peek_enum(v) g_value_get_enum (v) +#define g_marshal_value_peek_flags(v) g_value_get_flags (v) +#define g_marshal_value_peek_float(v) g_value_get_float (v) +#define g_marshal_value_peek_double(v) g_value_get_double (v) +#define g_marshal_value_peek_string(v) (char*) g_value_get_string (v) +#define g_marshal_value_peek_param(v) g_value_get_param (v) +#define g_marshal_value_peek_boxed(v) g_value_get_boxed (v) +#define g_marshal_value_peek_pointer(v) g_value_get_pointer (v) +#define g_marshal_value_peek_object(v) g_value_get_object (v) +#else /* !G_ENABLE_DEBUG */ +/* WARNING: This code accesses GValues directly, which is UNSUPPORTED API. + * Do not access GValues directly in your code. Instead, use the + * g_value_get_*() functions + */ +#define g_marshal_value_peek_boolean(v) (v)->data[0].v_int +#define g_marshal_value_peek_char(v) (v)->data[0].v_int +#define g_marshal_value_peek_uchar(v) (v)->data[0].v_uint +#define g_marshal_value_peek_int(v) (v)->data[0].v_int +#define g_marshal_value_peek_uint(v) (v)->data[0].v_uint +#define g_marshal_value_peek_long(v) (v)->data[0].v_long +#define g_marshal_value_peek_ulong(v) (v)->data[0].v_ulong +#define g_marshal_value_peek_int64(v) (v)->data[0].v_int64 +#define g_marshal_value_peek_uint64(v) (v)->data[0].v_uint64 +#define g_marshal_value_peek_enum(v) (v)->data[0].v_long +#define g_marshal_value_peek_flags(v) (v)->data[0].v_ulong +#define g_marshal_value_peek_float(v) (v)->data[0].v_float +#define g_marshal_value_peek_double(v) (v)->data[0].v_double +#define g_marshal_value_peek_string(v) (v)->data[0].v_pointer +#define g_marshal_value_peek_param(v) (v)->data[0].v_pointer +#define g_marshal_value_peek_boxed(v) (v)->data[0].v_pointer +#define g_marshal_value_peek_pointer(v) (v)->data[0].v_pointer +#define g_marshal_value_peek_object(v) (v)->data[0].v_pointer +#endif /* !G_ENABLE_DEBUG */ + + +/* BOOLEAN:STRING,POINTER,POINTER (/tmp/dbus-binding-tool-c-marshallers.0AFVAU:1) */ +extern void dbus_glib_marshal_tr_core_BOOLEAN__STRING_POINTER_POINTER (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); +void +dbus_glib_marshal_tr_core_BOOLEAN__STRING_POINTER_POINTER (GClosure *closure, + GValue *return_value G_GNUC_UNUSED, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint G_GNUC_UNUSED, + gpointer marshal_data) +{ + typedef gboolean (*GMarshalFunc_BOOLEAN__STRING_POINTER_POINTER) (gpointer data1, + gpointer arg_1, + gpointer arg_2, + gpointer arg_3, + gpointer data2); + register GMarshalFunc_BOOLEAN__STRING_POINTER_POINTER callback; + register GCClosure *cc = (GCClosure*) closure; + register gpointer data1, data2; + gboolean v_return; + + g_return_if_fail (return_value != NULL); + g_return_if_fail (n_param_values == 4); + + if (G_CCLOSURE_SWAP_DATA (closure)) + { + data1 = closure->data; + data2 = g_value_peek_pointer (param_values + 0); + } + else + { + data1 = g_value_peek_pointer (param_values + 0); + data2 = closure->data; + } + callback = (GMarshalFunc_BOOLEAN__STRING_POINTER_POINTER) (marshal_data ? marshal_data : cc->callback); + + v_return = callback (data1, + g_marshal_value_peek_string (param_values + 1), + g_marshal_value_peek_pointer (param_values + 2), + g_marshal_value_peek_pointer (param_values + 3), + data2); + + g_value_set_boolean (return_value, v_return); +} + +G_END_DECLS + +#endif /* __dbus_glib_marshal_tr_core_MARSHAL_H__ */ + +#include +static const DBusGMethodInfo dbus_glib_tr_core_methods[] = { + { (GCallback) tr_core_add_file, dbus_glib_marshal_tr_core_BOOLEAN__STRING_POINTER_POINTER, 0 }, +}; + +const DBusGObjectInfo dbus_glib_tr_core_object_info = { + 0, + dbus_glib_tr_core_methods, + 1, +"com.transmissionbt.Transmission\0AddFile\0S\0filename\0I\0s\0handled\0O\0F\0N\0b\0\0\0", +"\0", +"\0" +}; + diff --git a/gtk/tr-core-dbus.xml b/gtk/tr-core-dbus.xml new file mode 100644 index 000000000..4135b456a --- /dev/null +++ b/gtk/tr-core-dbus.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/gtk/tr-core.c b/gtk/tr-core.c index 2d866ea8c..3df2ddae6 100644 --- a/gtk/tr-core.c +++ b/gtk/tr-core.c @@ -29,12 +29,18 @@ #ifdef HAVE_GIO #include #endif +#ifdef HAVE_DBUS_GLIB +#include +#endif #include #include /* tr_free */ #include "conf.h" #include "tr-core.h" +#ifdef HAVE_DBUS_GLIB +#include "tr-core-dbus.h" +#endif #include "tr-prefs.h" #include "tr-torrent.h" #include "util.h" @@ -50,7 +56,6 @@ struct TrCorePrivate #endif GtkTreeModel * model; tr_handle * handle; - int nextid; }; static void @@ -152,6 +157,29 @@ tr_core_class_init( gpointer g_class, gpointer g_class_data UNUSED ) G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_VOID__STRING, G_TYPE_NONE, 1, G_TYPE_STRING ); + +#ifdef HAVE_DBUS_GLIB + { + DBusGConnection * bus = dbus_g_bus_get( DBUS_BUS_SESSION, NULL ); + DBusGProxy * bus_proxy = NULL; + if( bus ) + bus_proxy = dbus_g_proxy_new_for_name( bus, "org.freedesktop.DBus", + "/org/freedesktop/DBus", + "org.freedesktop.DBus" ); + if( bus_proxy ) { + int result = 0; + dbus_g_proxy_call( bus_proxy, "RequestName", NULL, + G_TYPE_STRING, "com.transmissionbt.Transmission", + G_TYPE_UINT, 0, + G_TYPE_INVALID, + G_TYPE_UINT, &result, + G_TYPE_INVALID ); + if( result == 1 ) + dbus_g_object_type_install_info( TR_CORE_TYPE, + &dbus_glib_tr_core_object_info ); + } + } +#endif } /*** @@ -327,9 +355,9 @@ tr_core_apply_defaults( tr_ctor * ctor ) tr_ctorSetPeerLimit( ctor, TR_FORCE, pref_int_get( PREF_KEY_MAX_PEERS_PER_TORRENT ) ); - if( tr_ctorGetDestination( ctor, TR_FORCE, NULL ) ) { - char * path = pref_string_get( PREF_KEY_DIR_DEFAULT ); - tr_ctorSetDestination( ctor, TR_FORCE, path ); + if( tr_ctorGetDownloadDir( ctor, TR_FORCE, NULL ) ) { + char * path = pref_string_get( PREF_KEY_DOWNLOAD_DIR ); + tr_ctorSetDownloadDir( ctor, TR_FORCE, path ); g_free( path ); } } @@ -470,7 +498,7 @@ tr_core_init( GTypeInstance * instance, gpointer g_class UNUSED ) TR_TORRENT_TYPE, /* TrTorrent object */ G_TYPE_POINTER, /* tr_torrent* */ G_TYPE_INT, /* tr_stat()->status */ - G_TYPE_INT /* ID for IPC */ + G_TYPE_INT /* tr_torrentId() */ }; p = self->priv = G_TYPE_INSTANCE_GET_PRIVATE( self, @@ -482,7 +510,17 @@ tr_core_init( GTypeInstance * instance, gpointer g_class UNUSED ) store = gtk_list_store_newv( MC_ROW_COUNT, types ); p->model = GTK_TREE_MODEL( store ); - p->nextid = 1; + +#ifdef HAVE_DBUS_GLIB + { + DBusGConnection * bus = dbus_g_bus_get( DBUS_BUS_SESSION, NULL ); + if( bus ) + dbus_g_connection_register_g_object( bus, + "/com/transmissionbt/Transmission", + G_OBJECT( self )); + } +#endif + } GType @@ -538,7 +576,7 @@ tr_core_close( TrCore * core ) if( handle ) { core->priv->handle = NULL; - tr_close( handle ); + tr_sessionClose( handle ); } } @@ -619,10 +657,11 @@ doCollate( const char * in ) } void -tr_core_add_torrent( TrCore * self, TrTorrent * tor ) +tr_core_add_torrent( TrCore * self, TrTorrent * gtor ) { - const tr_info * inf = tr_torrent_info( tor ); - const tr_stat * torStat = tr_torrent_stat( tor ); + const tr_info * inf = tr_torrent_info( gtor ); + const tr_stat * torStat = tr_torrent_stat( gtor ); + tr_torrent * tor = tr_torrent_handle( gtor ); char * collated = doCollate( inf->name ); GtkListStore * store = GTK_LIST_STORE( tr_core_model( self ) ); GtkTreeIter unused; @@ -631,15 +670,14 @@ tr_core_add_torrent( TrCore * self, TrTorrent * tor ) MC_NAME, inf->name, MC_NAME_COLLATED, collated, MC_HASH, inf->hashString, - MC_TORRENT, tor, - MC_TORRENT_RAW, tr_torrent_handle( tor ), + MC_TORRENT, gtor, + MC_TORRENT_RAW, tor, MC_STATUS, torStat->status, - MC_ID, self->priv->nextid, + MC_ID, tr_torrentId( tor ), -1); - ++self->priv->nextid; /* cleanup */ - g_object_unref( G_OBJECT( tor ) ); + g_object_unref( G_OBJECT( gtor ) ); g_free( collated ); } @@ -649,25 +687,20 @@ tr_core_load( TrCore * self, gboolean forcePaused ) int i; int count = 0; tr_torrent ** torrents; - char * path; tr_ctor * ctor; - path = getdownloaddir( ); - ctor = tr_ctorNew( tr_core_handle( self ) ); if( forcePaused ) tr_ctorSetPaused( ctor, TR_FORCE, TRUE ); - tr_ctorSetDestination( ctor, TR_FALLBACK, path ); tr_ctorSetPeerLimit( ctor, TR_FALLBACK, pref_int_get( PREF_KEY_MAX_PEERS_PER_TORRENT ) ); - torrents = tr_loadTorrents ( tr_core_handle( self ), ctor, &count ); + torrents = tr_sessionLoadTorrents ( tr_core_handle( self ), ctor, &count ); for( i=0; ipromptsig, 0, ctor ); + else + tr_core_add_ctor( core, ctor ); + } +} + +gboolean +tr_core_add_file( TrCore * core, + const char * filename, + gboolean * success, + GError ** err UNUSED ) +{ + add_filename( core, filename, + pref_flag_get( PREF_KEY_START ), + pref_flag_get( PREF_KEY_OPTIONS_PROMPT ) ); + *success = TRUE; + return TRUE; +} + void tr_core_add_list( TrCore * core, GSList * torrentFiles, @@ -704,29 +774,10 @@ tr_core_add_list( TrCore * core, pref_flag_t prompt ) { const gboolean doStart = pref_flag_eval( start, PREF_KEY_START ); - const gboolean doPrompt = pref_flag_eval( prompt, PREF_KEY_OPTIONS_PROMPT ); - - if( torrentFiles && !isDisposed( core ) ) - { - GSList * l; - tr_handle * handle = core->priv->handle; - - for( l=torrentFiles; l!=NULL; l=l->next ) - { - tr_ctor * ctor = tr_ctorNew( handle ); - tr_core_apply_defaults( ctor ); - tr_ctorSetPaused( ctor, TR_FORCE, !doStart ); - if( tr_ctorSetMetainfoFromFile( ctor, l->data ) ) - tr_ctorFree( ctor ); - else if( tr_torrentParse( handle, ctor, NULL ) ) - tr_ctorFree( ctor ); - else if( doPrompt ) - g_signal_emit( core, TR_CORE_GET_CLASS(core)->promptsig, 0, ctor ); - else - tr_core_add_ctor( core, ctor ); - } - } - + const gboolean doPrompt = pref_flag_eval( prompt,PREF_KEY_OPTIONS_PROMPT ); + GSList * l; + for( l=torrentFiles; l!=NULL; l=l->next ) + add_filename( core, l->data, doStart, doPrompt ); freestrlist( torrentFiles ); } @@ -737,19 +788,6 @@ tr_core_torrents_added( TrCore * self ) tr_core_errsig( self, TR_CORE_ERR_NO_MORE_TORRENTS, NULL ); } -void -tr_core_delete_torrent( TrCore * self, GtkTreeIter * iter ) -{ - TrTorrent * tor; - GtkTreeModel * model = tr_core_model( self ); - - gtk_tree_model_get( model, iter, MC_TORRENT, &tor, -1 ); - gtk_list_store_remove( GTK_LIST_STORE( model ), iter ); - tr_torrentRemoveSaved( tr_torrent_handle( tor ) ); - - g_object_unref( G_OBJECT( tor ) ); -} - static gboolean findTorrentInModel( TrCore * core, const TrTorrent * gtor, GtkTreeIter * setme ) { diff --git a/gtk/tr-core.h b/gtk/tr-core.h index 3a7a502cd..d18c3fd19 100644 --- a/gtk/tr-core.h +++ b/gtk/tr-core.h @@ -134,9 +134,11 @@ void tr_core_add_list( TrCore * self, #define tr_core_add_list_defaults(c,l) \ tr_core_add_list(c,l,PREF_FLAG_DEFAULT,PREF_FLAG_DEFAULT) -/** - * Add a torrent. - */ + +/** Add a torrent. */ +gboolean tr_core_add_file( TrCore*, const char * filename, gboolean * setme_success, GError ** err ); + +/** Add a torrent. */ void tr_core_add_torrent( TrCore*, TrTorrent* ); /** @@ -149,8 +151,6 @@ void tr_core_torrents_added( TrCore * self ); ******* ******/ -void tr_core_delete_torrent( TrCore * self, GtkTreeIter * iter ); - void tr_core_remove_torrent( TrCore * self, TrTorrent * gtor, int deleteFiles ); /* update the model with current torrent status */ diff --git a/gtk/tr-prefs.c b/gtk/tr-prefs.c index 8b2a22b88..f21d4aa13 100644 --- a/gtk/tr-prefs.c +++ b/gtk/tr-prefs.c @@ -45,7 +45,7 @@ tr_prefs_init_global( void ) pref_int_set_default ( PREF_KEY_PEER_SOCKET_TOS, TR_DEFAULT_PEER_SOCKET_TOS ); pref_flag_set_default ( PREF_KEY_INHIBIT_HIBERNATION, TRUE ); - pref_flag_set_default ( PREF_KEY_BLOCKLIST_ENABLED, FALSE ); + pref_flag_set_default ( PREF_KEY_BLOCKLIST_ENABLED, TR_DEFAULT_BLOCKLIST_ENABLED ); pref_string_set_default ( PREF_KEY_OPEN_DIALOG_FOLDER, g_get_home_dir( ) ); @@ -73,7 +73,7 @@ tr_prefs_init_global( void ) if( !str ) str = g_get_user_special_dir( G_USER_DIRECTORY_DOWNLOAD ); #endif if( !str ) str = g_get_home_dir( ); - pref_string_set_default ( PREF_KEY_DIR_DEFAULT, str ); + pref_string_set_default ( PREF_KEY_DOWNLOAD_DIR, str ); pref_int_set_default ( PREF_KEY_PORT, TR_DEFAULT_PORT ); @@ -91,6 +91,10 @@ tr_prefs_init_global( void ) pref_flag_set_default ( PREF_KEY_START, TRUE ); pref_flag_set_default ( PREF_KEY_TRASH_ORIGINAL, FALSE ); + pref_flag_set_default ( PREF_KEY_RPC_ENABLED, TR_DEFAULT_RPC_ENABLED ); + pref_int_set_default ( PREF_KEY_RPC_PORT, TR_DEFAULT_RPC_PORT ); + pref_string_set_default ( PREF_KEY_RPC_ACL, TR_DEFAULT_RPC_ACL ); + pref_save( NULL ); } @@ -268,7 +272,7 @@ torrentPage( GObject * core ) w = new_check_button( s, PREF_KEY_TRASH_ORIGINAL, core ); hig_workarea_add_wide_control( t, &row, w ); - w = new_path_chooser_button( PREF_KEY_DIR_DEFAULT, core ); + w = new_path_chooser_button( PREF_KEY_DOWNLOAD_DIR, core ); hig_workarea_add_row( t, &row, _( "_Destination folder:" ), w, NULL ); hig_workarea_finish( t, &row ); @@ -524,7 +528,7 @@ networkPage( GObject * core, gpointer alive ) g_signal_connect( w, "toggled", G_CALLBACK(target_cb), w2 ); hig_workarea_add_row_w( t, &row, w, w2, NULL ); - hig_workarea_add_section_title (t, &row, _("Ports")); + hig_workarea_add_section_title (t, &row, _( "Ports" ) ); s = _("_Forward port from router" ); w = new_check_button( s, PREF_KEY_NAT, core ); @@ -546,6 +550,13 @@ networkPage( GObject * core, gpointer alive ) g_signal_connect( w, "toggled", G_CALLBACK(testing_port_cb), l ); g_signal_connect( w2, "value-changed", G_CALLBACK(testing_port_cb), l ); + s = _( "Listen for _RPC requests on port:" ); + w = new_check_button( s, PREF_KEY_RPC_ENABLED, core ); + w2 = new_spin_button( PREF_KEY_RPC_PORT, core, 0, 65535, 1 ); + gtk_widget_set_sensitive( GTK_WIDGET(w2), pref_flag_get( PREF_KEY_RPC_ENABLED ) ); + g_signal_connect( w, "toggled", G_CALLBACK(target_cb), w2 ); + hig_workarea_add_row_w( t, &row, w, w2, NULL ); + hig_workarea_finish( t, &row ); return t; } diff --git a/gtk/tr-prefs.h b/gtk/tr-prefs.h index 8b7f43024..234d39bf9 100644 --- a/gtk/tr-prefs.h +++ b/gtk/tr-prefs.h @@ -25,7 +25,7 @@ GtkWidget * tr_prefs_dialog_new( GObject * core, GtkWindow * parent ); #define PREF_KEY_UL_LIMIT_ENABLED "upload-limit-enabled" #define PREF_KEY_UL_LIMIT "upload-limit" #define PREF_KEY_OPTIONS_PROMPT "show-options-window" -#define PREF_KEY_DIR_DEFAULT "default-download-directory" +#define PREF_KEY_DOWNLOAD_DIR "default-download-directory" #define PREF_KEY_OPEN_DIALOG_FOLDER "open-dialog-folder" #define PREF_KEY_INHIBIT_HIBERNATION "inhibit-hibernation" #define PREF_KEY_DIR_WATCH "watch-folder" @@ -53,6 +53,9 @@ GtkWidget * tr_prefs_dialog_new( GObject * core, GtkWindow * parent ); #define PREF_KEY_MAIN_WINDOW_WIDTH "main-window-width" #define PREF_KEY_MAIN_WINDOW_X "main-window-x" #define PREF_KEY_MAIN_WINDOW_Y "main-window-y" +#define PREF_KEY_RPC_PORT "rpc-port" +#define PREF_KEY_RPC_ENABLED "rpc-enabled" +#define PREF_KEY_RPC_ACL "rpc-access-control-list" void tr_prefs_init_global( void ); diff --git a/gtk/tr-torrent.c b/gtk/tr-torrent.c index fec20dc98..b34cab8f2 100644 --- a/gtk/tr-torrent.c +++ b/gtk/tr-torrent.c @@ -329,7 +329,7 @@ tr_torrent_delete_files( TrTorrent * gtor ) { tr_file_index_t i; const tr_info * info = tr_torrent_info( gtor ); - const char * stop = tr_torrentGetFolder( tr_torrent_handle( gtor ) ); + const char * stop = tr_torrentGetDownloadDir( tr_torrent_handle( gtor ) ); for( i=0; info && ifileCount; ++i ) { @@ -351,8 +351,8 @@ tr_torrent_open_folder( TrTorrent * gtor ) tr_torrent * tor = tr_torrent_handle( gtor ); const tr_info * info = tr_torrent_info( gtor ); char * path = info->fileCount == 1 - ? g_build_filename( tr_torrentGetFolder(tor), NULL ) - : g_build_filename( tr_torrentGetFolder(tor), info->name, NULL ); + ? g_build_filename( tr_torrentGetDownloadDir(tor), NULL ) + : g_build_filename( tr_torrentGetDownloadDir(tor), info->name, NULL ); gtr_open_file( path ); g_free( path ); } diff --git a/gtk/util.c b/gtk/util.c index b4a70535c..f9483ace3 100644 --- a/gtk/util.c +++ b/gtk/util.c @@ -271,19 +271,6 @@ checkfilenames( int argc, char **argv ) return g_slist_reverse( ret ); } -char * -getdownloaddir( void ) -{ - static char * wd = NULL; - char * dir = pref_string_get( PREF_KEY_DIR_DEFAULT ); - if ( dir == NULL ) { - if( wd == NULL ) - wd = g_get_current_dir(); - dir = g_strdup( wd ); - } - return dir; -} - static void onErrorResponse(GtkWidget * dialog, int resp UNUSED, gpointer glist) { @@ -454,6 +441,7 @@ gtr_open_file( const char * path ) } #ifdef HAVE_DBUS_GLIB + static DBusGProxy* get_hibernation_inhibit_proxy( void ) { @@ -526,3 +514,33 @@ gtr_uninhibit_hibernation( guint inhibit_cookie ) } #endif } + +#define VALUE_SERVICE_NAME "com.transmissionbt.Transmission" +#define VALUE_SERVICE_OBJECT_PATH "/com/transmissionbt/Transmission" +#define VALUE_SERVICE_INTERFACE "com.transmissionbt.Transmission" + +gboolean +gtr_dbus_add_torrent( const char * filename ) +{ + static gboolean success = FALSE; +#ifdef HAVE_DBUS_GLIB + DBusGProxy * proxy = NULL; + GError * err = NULL; + DBusGConnection * conn; + if(( conn = dbus_g_bus_get( DBUS_BUS_SESSION, &err ))) + proxy = dbus_g_proxy_new_for_name (conn, VALUE_SERVICE_NAME, + VALUE_SERVICE_OBJECT_PATH, + VALUE_SERVICE_INTERFACE ); + else if( err ) + g_message( "err: %s", err->message ); + if( proxy ) + dbus_g_proxy_call( proxy, "AddFile", &err, + G_TYPE_STRING, filename, + G_TYPE_INVALID, + G_TYPE_BOOLEAN, &success, + G_TYPE_INVALID ); + if( err ) + g_message( "err: %s", err->message ); +#endif + return success; +} diff --git a/gtk/util.h b/gtk/util.h index c7cd03829..851c11698 100644 --- a/gtk/util.h +++ b/gtk/util.h @@ -29,7 +29,9 @@ #include /* macro to shut up "unused parameter" warnings */ +#ifndef UNUSED #define UNUSED G_GNUC_UNUSED +#endif /* NULL-safe version of strcmp */ int tr_strcmp( const char*, const char * ); @@ -79,16 +81,15 @@ decode_uri( const char * uri ); GSList * checkfilenames( int argc, char ** argv ); -/* retrieve the global download directory */ -char * -getdownloaddir( void ); - void gtr_open_file( const char * path ); guint gtr_inhibit_hibernation( void ); void gtr_uninhibit_hibernation( guint ); +gboolean gtr_dbus_add_torrent( const char * filename ); + + #ifdef GTK_MAJOR_VERSION /* create an error dialog, if wind is NULL or mapped then show dialog now, diff --git a/libtransmission/Makefile.am b/libtransmission/Makefile.am index 93bc52fa6..8670aa540 100644 --- a/libtransmission/Makefile.am +++ b/libtransmission/Makefile.am @@ -16,7 +16,6 @@ libtransmission_a_SOURCES = \ ggets.c \ handshake.c \ inout.c \ - ipcparse.c \ json.c \ list.c \ makemeta.c \ @@ -33,6 +32,7 @@ libtransmission_a_SOURCES = \ ratecontrol.c \ resume.c \ rpc.c \ + rpc-server.c \ session.c \ stats.c \ torrent.c \ @@ -57,7 +57,6 @@ noinst_HEADERS = \ ggets.h \ handshake.h \ inout.h \ - ipcparse.h \ json.h \ list.h \ makemeta.h \ @@ -75,12 +74,12 @@ noinst_HEADERS = \ ratecontrol.h \ resume.h \ rpc.h \ + rpc-server.h \ session.h \ stats.h \ torrent.h \ tracker.h \ transmission.h \ - trcompat.h \ trevent.h \ upnp.h \ utils.h \ @@ -99,6 +98,7 @@ noinst_PROGRAMS = $(TESTS) APPS_LDADD = \ ./libtransmission.a \ + $(top_builddir)/third-party/shttpd/libshttpd.a \ $(top_builddir)/third-party/miniupnp/libminiupnp.a \ $(top_builddir)/third-party/libnatpmp/libnatpmp.a \ $(top_builddir)/third-party/libevent/libevent.la \ diff --git a/libtransmission/bencode-test.c b/libtransmission/bencode-test.c index d1c80ce2f..ac71682b0 100644 --- a/libtransmission/bencode-test.c +++ b/libtransmission/bencode-test.c @@ -275,8 +275,10 @@ static int testRISONSnippet( const char * rison_str, const char * expected ) { char * serialized = tr_rison2json( rison_str, -1 ); +#if 0 fprintf( stderr, " expected: [%s]\n", expected ); fprintf( stderr, " got: [%s]\n", serialized ); +#endif check( !strcmp( serialized, expected ) ); tr_free( serialized ); return 0; diff --git a/libtransmission/bencode.c b/libtransmission/bencode.c index 266c22339..64db437a5 100644 --- a/libtransmission/bencode.c +++ b/libtransmission/bencode.c @@ -1093,7 +1093,13 @@ jsonStringFunc( const tr_benc * val, void * vdata ) case '\r': evbuffer_add_printf( data->out, "\\n" ); break; case '\t': evbuffer_add_printf( data->out, "\\n" ); break; case '\\': evbuffer_add_printf( data->out, "\\\\" ); break; - default: evbuffer_add_printf( data->out, "%c", *it ); break; + default: { + if( isascii( *it ) ) + evbuffer_add_printf( data->out, "%c", *it ); + else + evbuffer_add_printf( data->out, "\\u%0x", (unsigned int)*it ); + break; + } } } evbuffer_add_printf( data->out, "\"" ); diff --git a/libtransmission/bencode.h b/libtransmission/bencode.h index 7affbdeeb..7fbf84e0f 100644 --- a/libtransmission/bencode.h +++ b/libtransmission/bencode.h @@ -56,11 +56,6 @@ typedef struct tr_benc /* backwards compatability */ typedef tr_benc benc_val_t; -int tr_jsonParse( const void * buf, - const void * bufend, - tr_benc * setme_benc, - const uint8_t ** setme_end ); - int tr_bencParse( const void * buf, const void * bufend, tr_benc * setme_benc, diff --git a/libtransmission/blocklist.c b/libtransmission/blocklist.c index 07664e071..19b0e9b99 100644 --- a/libtransmission/blocklist.c +++ b/libtransmission/blocklist.c @@ -89,7 +89,7 @@ blocklistLoad( tr_blocklist * b ) b->byteCount = st.st_size; b->ruleCount = st.st_size / sizeof( struct tr_ip_range ); b->fd = fd; - tr_inf( _( "Blocklist contains %'d entries" ), b->ruleCount ); + tr_inf( _( "Blocklist contains %'u entries" ), (unsigned int)b->ruleCount ); } static void diff --git a/libtransmission/clients.c b/libtransmission/clients.c index cc6186e08..d30407c8d 100644 --- a/libtransmission/clients.c +++ b/libtransmission/clients.c @@ -30,7 +30,6 @@ #include #include "transmission.h" -#include "trcompat.h" /* strlcpy */ #include "utils.h" static int @@ -90,7 +89,7 @@ two_major_two_minor( char * buf, size_t buflen, const char * name, const uint8_t static void no_version( char * buf, size_t buflen, const char * name ) { - strlcpy( buf, name, buflen ); + tr_strlcpy( buf, name, buflen ); } static void diff --git a/libtransmission/fastresume.c b/libtransmission/fastresume.c index 66f853e71..f7a00141e 100644 --- a/libtransmission/fastresume.c +++ b/libtransmission/fastresume.c @@ -100,8 +100,8 @@ enum /* IPs and ports of connectable peers */ FR_ID_PEERS = 11, - /* destination of the torrent: zero-terminated string */ - FR_ID_DESTINATION = 12, + /* download dir of the torrent: zero-terminated string */ + FR_ID_DOWNLOAD_DIR = 12, /* pex flag * 't' if pex is enabled, 'f' if disabled */ @@ -152,7 +152,7 @@ getMTimes( const tr_torrent * tor, int * setme_n ) char fname[MAX_PATH_LENGTH]; struct stat sb; tr_buildPath( fname, sizeof(fname), - tor->destination, tor->info.files[i].name, NULL ); + tor->downloadDir, tor->info.files[i].name, NULL ); if ( !stat( fname, &sb ) && S_ISREG( sb.st_mode ) ) { #ifdef SYS_DARWIN m[i] = sb.st_mtimespec.tv_sec; @@ -199,11 +199,11 @@ tr_fastResumeSave( const tr_torrent * tor ) /* Write format version */ fwrite( &version, 4, 1, file ); - if( TRUE ) /* FR_ID_DESTINATION */ + if( TRUE ) /* FR_ID_DOWNLOAD_DIR */ { - const char * d = tor->destination ? tor->destination : ""; + const char * d = tor->downloadDir ? tor->downloadDir : ""; const int byteCount = strlen( d ) + 1; - fastResumeWriteData( FR_ID_DESTINATION, d, 1, byteCount, file ); + fastResumeWriteData( FR_ID_DOWNLOAD_DIR, d, 1, byteCount, file ); } /* Write progress data */ @@ -349,7 +349,7 @@ internalIdToPublicBitfield( uint8_t id ) case FR_ID_RUN: ret = TR_FR_RUN; break; case FR_ID_CORRUPT: ret = TR_FR_CORRUPT; break; case FR_ID_PEERS: ret = TR_FR_PEERS; break; - case FR_ID_DESTINATION: ret = TR_FR_DESTINATION; break; + case FR_ID_DOWNLOAD_DIR: ret = TR_FR_DOWNLOAD_DIR; break; case FR_ID_MAX_PEERS: ret = TR_FR_MAX_PEERS; break; } @@ -559,14 +559,14 @@ parsePeers( tr_torrent * tor, const uint8_t * buf, uint32_t len ) } static uint64_t -parseDestination( tr_torrent * tor, const uint8_t * buf, uint32_t len ) +parseDownloadDir( tr_torrent * tor, const uint8_t * buf, uint32_t len ) { uint64_t ret = 0; if( buf && *buf && len ) { - tr_free( tor->destination ); - tor->destination = tr_strndup( (char*)buf, len ); - ret = TR_FR_DESTINATION; + tr_free( tor->downloadDir ); + tor->downloadDir = tr_strndup( (char*)buf, len ); + ret = TR_FR_DOWNLOAD_DIR; } return ret; @@ -600,7 +600,7 @@ parseVersion1( tr_torrent * tor, const uint8_t * buf, const uint8_t * end, case FR_ID_CORRUPT: ret |= parseCorrupt( tor, buf, len ); break; case FR_ID_PEERS: ret |= parsePeers( tor, buf, len ); break; case FR_ID_MAX_PEERS: ret |= parseConnections( tor, buf, len ); break; - case FR_ID_DESTINATION: ret |= parseDestination( tor, buf, len ); break; + case FR_ID_DOWNLOAD_DIR: ret |= parseDownloadDir( tor, buf, len ); break; default: tr_tordbg( tor, "Skipping unknown resume code %d", (int)id ); break; } diff --git a/libtransmission/fdlimit.c b/libtransmission/fdlimit.c index 3f797f76a..4dccdd438 100644 --- a/libtransmission/fdlimit.c +++ b/libtransmission/fdlimit.c @@ -50,7 +50,6 @@ #include "list.h" #include "net.h" #include "platform.h" /* tr_lock */ -#include "trcompat.h" /* strlcpy */ #include "utils.h" #if SIZEOF_VOIDP==8 @@ -264,7 +263,7 @@ tr_fdFileCheckout( const char * folder, } dbgmsg( "opened '%s' in slot %d, write %c", filename, winner, write?'y':'n' ); - strlcpy( o->filename, filename, sizeof( o->filename ) ); + tr_strlcpy( o->filename, filename, sizeof( o->filename ) ); o->isWritable = write; } diff --git a/libtransmission/inout.c b/libtransmission/inout.c index 4eaed511d..e962ebff7 100644 --- a/libtransmission/inout.c +++ b/libtransmission/inout.c @@ -60,7 +60,7 @@ readOrWriteBytes( const tr_torrent * tor, assert( !file->length || (fileOffset < file->length)); assert( fileOffset + buflen <= file->length ); - tr_buildPath ( path, sizeof(path), tor->destination, file->name, NULL ); + tr_buildPath ( path, sizeof(path), tor->downloadDir, file->name, NULL ); fileExists = !stat( path, &sb ); if( !file->length ) @@ -68,7 +68,7 @@ readOrWriteBytes( const tr_torrent * tor, if ((ioMode==TR_IO_READ) && !fileExists ) /* does file exist? */ err = tr_ioErrorFromErrno( errno ); - else if ((fd = tr_fdFileCheckout ( tor->destination, + else if ((fd = tr_fdFileCheckout ( tor->downloadDir, file->name, ioMode==TR_IO_WRITE )) < 0) err = fd; @@ -135,7 +135,7 @@ ensureMinimumFileSize( const tr_torrent * tor, assert( 0<=fileIndex && fileIndexinfo.fileCount ); assert( minBytes <= file->length ); - fd = tr_fdFileCheckout( tor->destination, file->name, TRUE ); + fd = tr_fdFileCheckout( tor->downloadDir, file->name, TRUE ); if( fd < 0 ) /* bad fd */ err = fd; else if (fstat (fd, &sb) ) /* how big is the file? */ diff --git a/libtransmission/ipcparse.c b/libtransmission/ipcparse.c deleted file mode 100644 index 0287d7d90..000000000 --- a/libtransmission/ipcparse.c +++ /dev/null @@ -1,1101 +0,0 @@ -/****************************************************************************** - * $Id$ - * - * Copyright (c) 2007-2008 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 -#include -#include -#include -#include -#include - -#include "transmission.h" -#include "bencode.h" -#include "torrent.h" -#include "utils.h" - -#include "ipcparse.h" - -#define TR_N_ELEMENTS( ary ) ( sizeof( ary ) / sizeof( (ary)[0] ) ) - -#define SAFEFREE( ptr ) \ - do \ - { \ - const int saved = errno; \ - free( ptr ); \ - errno = saved; \ - } \ - while( 0 ) - -#define SAFEBENCFREE( val ) \ - do \ - { \ - int saved = errno; \ - tr_bencFree( val ); \ - errno = saved; \ - } \ - while( 0 ) - -/* IPC protocol version */ -#define PROTO_VERS_MIN ( 1 ) -#define PROTO_VERS_MAX ( 2 ) - -#define MSGVALID( id ) ( IPC__MSG_COUNT > (id) ) -#define MSGNAME( id ) ( gl_msgs[(id)].name ) -#define DICTPAYLOAD( info ) ( 2 > (info)->vers ) - -struct ipc_funcs -{ - trd_msgfunc msgs[IPC__MSG_COUNT]; - trd_msgfunc def; -}; - -struct ipc_info -{ - struct ipc_funcs * funcs; - int vers; -}; - -int -ipc_hasvers( const struct ipc_info * inf ) -{ - return inf && ( inf->vers > 0 ); -} - -struct msg -{ - const char * name; - const int minvers; - const enum ipc_msg id; -}; - -/* these names must be sorted for strcmp() */ -static const struct msg gl_msgs[] = -{ - { "addfile-detailed", 2, IPC_MSG_ADDONEFILE }, - { "addfiles", 1, IPC_MSG_ADDMANYFILES }, - { "automap", 2, IPC_MSG_AUTOMAP }, - { "autostart", 2, IPC_MSG_AUTOSTART }, - { "bad-format", 2, IPC_MSG_BAD }, - { "directory", 2, IPC_MSG_DIR }, - { "downlimit", 2, IPC_MSG_DOWNLIMIT }, - { "encryption", 2, IPC_MSG_CRYPTO }, - { "failed", 2, IPC_MSG_FAIL }, - { "get-automap", 2, IPC_MSG_GETAUTOMAP }, - { "get-autostart", 2, IPC_MSG_GETAUTOSTART }, - { "get-directory", 2, IPC_MSG_GETDIR }, - { "get-downlimit", 2, IPC_MSG_GETDOWNLIMIT }, - { "get-encryption", 2, IPC_MSG_GETCRYPTO }, - { "get-info", 2, IPC_MSG_GETINFO }, - { "get-info-all", 2, IPC_MSG_GETINFOALL }, - { "get-pex", 2, IPC_MSG_GETPEX }, - { "get-port", 2, IPC_MSG_GETPORT }, - { "get-status", 2, IPC_MSG_GETSTAT }, - { "get-status-all", 2, IPC_MSG_GETSTATALL }, - { "get-supported", 2, IPC_MSG_GETSUP }, - { "get-uplimit", 2, IPC_MSG_GETUPLIMIT }, - { "info", 2, IPC_MSG_INFO }, - { "lookup", 2, IPC_MSG_LOOKUP }, - { "noop", 2, IPC_MSG_NOOP }, - { "not-supported", 2, IPC_MSG_NOTSUP }, - { "peer-max", 2, IPC_MSG_PEERMAX }, - { "pex", 2, IPC_MSG_PEX }, - { "port", 2, IPC_MSG_PORT }, - { "quit", 1, IPC_MSG_QUIT }, - { "remove", 2, IPC_MSG_REMOVE }, - { "remove-all", 2, IPC_MSG_REMOVEALL }, - { "start", 2, IPC_MSG_START }, - { "start-all", 2, IPC_MSG_STARTALL }, - { "status", 2, IPC_MSG_STAT }, - { "stop", 2, IPC_MSG_STOP }, - { "stop-all", 2, IPC_MSG_STOPALL }, - { "succeeded", 2, IPC_MSG_OK }, - { "supported", 2, IPC_MSG_SUP }, - { "uplimit", 2, IPC_MSG_UPLIMIT }, - { "verify", 2, IPC_MSG_VERIFY }, - { "version", 1, IPC_MSG_VERSION } -}; - -struct inf -{ - const char * name; - const int type; -}; - -/* these names must be sorted for strcmp() */ -static const struct inf gl_inf[] = -{ - { "comment", IPC_INF_COMMENT }, - { "creator", IPC_INF_CREATOR }, - { "date", IPC_INF_DATE }, - { "files", IPC_INF_FILES }, - { "hash", IPC_INF_HASH }, - { "id", IPC_INF_ID }, - { "name", IPC_INF_NAME }, - { "path", IPC_INF_PATH }, - { "private", IPC_INF_PRIVATE }, - { "size", IPC_INF_SIZE }, - { "trackers", IPC_INF_TRACKERS } -}; - -/* these names must be sorted for strcmp() */ -static const struct inf gl_stat[] = -{ - { "completed", IPC_ST_COMPLETED }, - { "download-speed", IPC_ST_DOWNSPEED }, - { "download-total", IPC_ST_DOWNTOTAL }, - { "download-valid", IPC_ST_DOWNVALID }, - { "error", IPC_ST_ERROR }, - { "error-message", IPC_ST_ERRMSG }, - { "eta", IPC_ST_ETA }, - { "id", IPC_ST_ID }, - { "peers-downloading", IPC_ST_PEERDOWN }, - { "peers-from", IPC_ST_PEERFROM }, - { "peers-max", IPC_ST_PEERMAX }, - { "peers-total", IPC_ST_PEERTOTAL }, - { "peers-uploading", IPC_ST_PEERUP }, - { "running", IPC_ST_RUNNING }, - { "scrape-completed", IPC_ST_TKDONE }, - { "scrape-leechers", IPC_ST_TKLEECH }, - { "scrape-seeders", IPC_ST_TKSEED }, - { "state", IPC_ST_STATE }, - { "swarm-speed", IPC_ST_SWARM }, - { "tracker", IPC_ST_TRACKER }, - { "upload-speed", IPC_ST_UPSPEED }, - { "upload-total", IPC_ST_UPTOTAL } -}; - -#define ASSERT_SORTED(A) \ - do { \ - int i, n; \ - for( i=0, n=TR_N_ELEMENTS(A)-1; imsgs[msg_id] = func; -} - -void -ipc_setdefmsg( struct ipc_funcs * funcs, trd_msgfunc func ) -{ - funcs->def = func; -} - -void -ipc_freemsgs( struct ipc_funcs * funcs ) -{ - tr_free( funcs ); -} - -struct ipc_info * -ipc_newcon( struct ipc_funcs * funcs ) -{ - struct ipc_info * info = tr_new0( struct ipc_info, 1 ); - info->funcs = funcs; - info->vers = -1; - return info; -} - -void -ipc_freecon( struct ipc_info * session ) -{ - tr_free( session ); -} - -int -ipc_ishandled( const struct ipc_info * session, enum ipc_msg msg_id ) -{ - assert( MSGVALID( msg_id ) ); - - return session->funcs->msgs[msg_id] != NULL; -} - -int -ipc_havetags( const struct ipc_info * session ) -{ - return !DICTPAYLOAD( session ); -} - -static int -sessionSupportsTags( const struct ipc_info * session ) -{ - return session->vers >= 2; -} - -static int -sessionSupportsMessage( const struct ipc_info * session, enum ipc_msg id ) -{ - assert( MSGVALID( id ) ); - assert( ipc_hasvers( session ) ); - - return gl_msgs[id].minvers <= session->vers; -} - -/** - * Creates the benc metainfo structure for a message - * and returns its child where payload should be set. - * - * In protocol version 1, the metainfo is a single-entry - * dictionary with a string from gl_msgs as the key - * and the return tr_benc pointer as the value. - * - * In protocol version 2, the metainfo is a list - * holding a string from gl_msgs, the return benc pointer, - * and (optionally) the integer tag. - */ -tr_benc * -ipc_initval( const struct ipc_info * session, - enum ipc_msg msg_id, - int64_t tag, - tr_benc * pk, - int benc_type ) -{ - tr_benc * ret; - - assert( MSGVALID( msg_id ) ); - - if( !sessionSupportsMessage( session, msg_id ) - || ( (tag>0) && !sessionSupportsTags( session ) ) ) - { - errno = EPERM; - return NULL; - } - - if( DICTPAYLOAD( session ) ) - { - tr_bencInitDict( pk, 1 ); - ret = tr_bencDictAdd( pk, MSGNAME( msg_id ) ); - } - else - { - tr_bencInitList( pk, 3 ); - tr_bencListAddStr( pk, MSGNAME( msg_id ) ); - ret = tr_bencListAdd( pk ); - if( 0 < tag ) - tr_bencListAddInt( pk, tag ); - } - - tr_bencInit( ret, benc_type ); - return ret; -} - -/** - * Serialize a benc message into a string appended to a - * printf()'ed string IPC_MIN_MSG_LEN bytes long that - * gives the length of the string. - */ -uint8_t * -ipc_serialize( const tr_benc * benc, size_t * setmeSize ) -{ - uint8_t * ret = NULL; - int len = 0; - char * str = tr_bencSave( benc, &len ); - - if( len > IPC_MAX_MSG_LEN ) - errno = EFBIG; - else { - const size_t size = IPC_MIN_MSG_LEN + len; - ret = tr_new( uint8_t, size ); - snprintf( (char*)ret, size, "%0*X", IPC_MIN_MSG_LEN, len ); - memcpy( ret + IPC_MIN_MSG_LEN, str, len ); - *setmeSize = size; - } - - tr_free( str ); - return ret; -} - -/** - * Create a serialized message whose payload is a NULL string - */ -uint8_t * -ipc_mkempty( const struct ipc_info * session, - size_t * setmeSize, - enum ipc_msg msg_id, - int64_t tag ) -{ - return ipc_mkstr( session, setmeSize, msg_id, tag, NULL ); -} - -/** - * Create a serialized message whose payload is an integer - */ -uint8_t * -ipc_mkint( const struct ipc_info * session, - size_t * setmeSize, - enum ipc_msg msg_id, - int64_t tag, - int64_t num ) -{ - tr_benc pk, * val; - uint8_t * ret = NULL; - - if(( val = ipc_initval( session, msg_id, tag, &pk, TYPE_INT ))) - { - tr_bencInitInt( val, num ); - ret = ipc_serialize( &pk, setmeSize ); - SAFEBENCFREE( &pk ); - } - - return ret; -} - -/** - * Create a serialized message whose payload is a string - */ -uint8_t * -ipc_mkstr( const struct ipc_info * session, - size_t * setmeSize, - enum ipc_msg msg_id, - int64_t tag, - const char * str ) -{ - tr_benc pk, * val; - uint8_t * ret = NULL; - - if(( val = ipc_initval( session, msg_id, tag, &pk, TYPE_STR ))) - { - tr_bencInitStrDup( val, str ); - ret = ipc_serialize( &pk, setmeSize ); - SAFEBENCFREE( &pk ); - } - - return ret; -} - -/** - * Create a serialized message whose payload is a dictionary - * giving the minimum and maximum protocol version we support, - * and (optionally) the label passed in. - * - * Note that this message is just the dictionary payload. - * It doesn't contain metainfo as the other ipc_mk*() functions do. - * That's because the metainfo is dependent on the protocol version, - * and this is a handshake message to negotiate protocol versions. - * - * @see handlevers() - */ -uint8_t * -ipc_mkvers( size_t * len, const char * label ) -{ - tr_benc pk, * dict; - uint8_t * ret; - - tr_bencInitDict( &pk, 1 ); - dict = tr_bencDictAddDict( &pk, MSGNAME( IPC_MSG_VERSION ), 3 ); - tr_bencDictAddInt( dict, "min", PROTO_VERS_MIN ); - tr_bencDictAddInt( dict, "max", PROTO_VERS_MAX ); - if( label ) - tr_bencDictAddStr( dict, "label", label ); - - ret = ipc_serialize( &pk, len ); - SAFEBENCFREE( &pk ); - - return ret; -} - -/** - * Create a serialized message that is used to request - * torrent information or statistics. - * - * msg_id must be one of: - * IPC_MSG_GETINFO - * IPC_MSG_GETINFOALL - * IPC_MSG_GETSTAT - * IPC_MSG_GETSTATALL - * - * "ids" is an optional array of torrent IDs. - * The array, if included, must be terminated by a 0 torrent id. - * - * "types" is a bitwise-and'ed set of fields from either - * the IPC_INF_* or IPC_ST_* enums in ipc-parse.h. - * Which enums are used is dependent on the value of msg_id. - * - * If torrent ids are specified in the "ids" array, - * the payload is a dictionary of two lists, "id" and "type". - * The "id" list holds the torrent IDs, and - * the "type" list holds string keys from either - * gl_inf or gl_stat, depending on the value of msg_id - * - * If no torrent ids are specified, the payload is - * a single list identical to the "type" list described above. - */ -uint8_t * -ipc_createInfoRequest( const struct ipc_info * session, - size_t * setmeSize, - enum ipc_msg msg_id, - int64_t tag, - int types, - const int * ids ) -{ - tr_benc pk; - tr_benc * typelist; - size_t i, typecount; - const struct inf * typearray; - uint8_t * ret; - - /* no ID list, send an -all message */ - if( !ids ) { - typelist = ipc_initval( session, msg_id, tag, &pk, TYPE_LIST ); - if( !typelist ) - return NULL; - } - else - { - tr_benc * top; - tr_benc * idlist; - - top = ipc_initval( session, msg_id, tag, &pk, TYPE_DICT ); - if( !top ) - return NULL; - - /* add the requested IDs */ - tr_bencDictReserve( top, 2 ); - typelist = tr_bencDictAdd( top, "type" ); - tr_bencInit( typelist, TYPE_LIST ); - for( i = 0; TORRENT_ID_VALID( ids[i] ); i++ ) { } - idlist = tr_bencDictAddList( top, "id", i ); - for( i = 0; TORRENT_ID_VALID( ids[i] ); i++ ) - tr_bencListAddInt( idlist, ids[i] ); - } - - /* get the type name array */ - switch( msg_id ) - { - case IPC_MSG_GETINFO: - case IPC_MSG_GETINFOALL: - typecount = TR_N_ELEMENTS( gl_inf ); - typearray = gl_inf; - break; - case IPC_MSG_GETSTAT: - case IPC_MSG_GETSTATALL: - typecount = TR_N_ELEMENTS( gl_stat ); - typearray = gl_stat; - break; - default: - assert( 0 ); - break; - } - - tr_bencListReserve( typelist, typecount ); - for( i=0; iannounce ); - if( tk->scrape ) - tr_bencDictAddStr( val, "scrape", tk->scrape ); -} - -/** - * append to "list" a dictionary whose keys are - * the string keys from gl_inf and whose values are - * torrent info set from "torrent_id" and "inf". - * - * "types" is a bitwise-and'ed set of fields - * from the IPC_INF_* enum in ipcparse.h. - * It specifies what to put in the dictionary. - */ -int -ipc_addinfo( tr_benc * list, - int torrent_id, - tr_torrent * tor, - int types ) -{ - const tr_info * inf = tr_torrentInfo( tor ); - const struct inf * it; - const struct inf * end; - tr_benc * dict; - - /* always send torrent id */ - types |= IPC_INF_ID; - - /* create a dict to hold the info */ - dict = tr_bencListAddDict( list, TR_N_ELEMENTS( gl_inf ) ); - - /* populate the dict */ - for( it=gl_inf, end=it+TR_N_ELEMENTS(gl_inf); it!=end; ++it ) - { - if( types & it->type ) - { - tr_benc * val = tr_bencDictAdd( dict, it->name ); - - switch( it->type ) - { - case IPC_INF_COMMENT: - tr_bencInitStrDup( val, inf->comment ? inf->comment : "" ); - break; - case IPC_INF_CREATOR: - tr_bencInitStrDup( val, inf->creator ? inf->creator : "" ); - break; - case IPC_INF_DATE: - tr_bencInitInt( val, inf->dateCreated ); - break; - case IPC_INF_FILES: { - tr_file_index_t f; - tr_bencInitList( val, inf->fileCount ); - for( f=0; ffileCount; ++f ) { - tr_benc * file = tr_bencListAddDict( val, 2 ); - tr_bencDictAddStr( file, "name", inf->files[f].name ); - tr_bencDictAddInt( file, "size", inf->files[f].length ); - } - break; - } - case IPC_INF_HASH: - tr_bencInitStr( val, inf->hashString, -1, 1 ); - break; - case IPC_INF_ID: - tr_bencInitInt( val, torrent_id ); - break; - case IPC_INF_NAME: - tr_bencInitStrDup( val, inf->name ); - break; - case IPC_INF_PATH: - tr_bencInitStrDup( val, inf->torrent ); - break; - case IPC_INF_PRIVATE: - tr_bencInitInt( val, inf->isPrivate ? 1 : 0 ); - break; - case IPC_INF_SIZE: - tr_bencInitInt( val, inf->totalSize ); - break; - case IPC_INF_TRACKERS: { - int j; - int prevTier = -1; - tr_benc * tier = NULL; - tr_bencInitList( val, 0 ); - for( j=0; jtrackerCount; ++j ) { - if( prevTier != inf->trackers[j].tier ) { - prevTier = inf->trackers[j].tier; - tier = tr_bencListAddList( val, 0 ); - } - filltracker( tr_bencListAdd( tier ), &inf->trackers[j] ); - } - break; - } - default: - assert( 0 ); - break; - } - } - } - - return 0; -} - -/** - * append to "list" a dictionary whose keys are - * the string keys from gl_stat and whose values - * are torrent statistics set from "st". - * - * "types" is a bitwise-and'ed set of fields - * from the IPC_INF_* enum in ipcparse.h. - * It specifies what to put in the dictionary. - */ -int -ipc_addstat( tr_benc * list, - int torrent_id, - tr_torrent * tor, - int types ) -{ - const tr_stat * st = tr_torrentStatCached( tor ); - const struct inf * it; - const struct inf * end; - tr_benc * dict; - - /* always send torrent id */ - types |= IPC_ST_ID; - - /* add the dictionary child */ - dict = tr_bencListAddDict( list, TR_N_ELEMENTS( gl_stat ) ); - - /* populate the dict */ - for( it=gl_stat, end=it+TR_N_ELEMENTS(gl_stat); it!=end; ++it ) - { - if( types & it->type ) - { - tr_benc * val = tr_bencDictAdd( dict, it->name ); - - switch( it->type ) - { - case IPC_ST_COMPLETED: - case IPC_ST_DOWNVALID: - tr_bencInitInt( val, st->haveValid ); - break; - case IPC_ST_DOWNSPEED: - tr_bencInitInt( val, st->rateDownload * 1024 ); - break; - case IPC_ST_DOWNTOTAL: - tr_bencInitInt( val, st->downloadedEver ); - break; - case IPC_ST_ERROR: { - const char * s; - const tr_errno e = st->error; - if( !e ) s = ""; - else if( e == TR_ERROR_ASSERT ) s = "assert"; - else if( e == TR_ERROR_IO_PERMISSIONS ) s = "io-permissions"; - else if( e == TR_ERROR_IO_SPACE ) s = "io-space"; - else if( e == TR_ERROR_IO_FILE_TOO_BIG ) s = "io-file-too-big"; - else if( e == TR_ERROR_IO_OPEN_FILES ) s = "io-open-files"; - else if( TR_ERROR_IS_IO( e ) ) s = "io-other"; - else if( e == TR_ERROR_TC_ERROR ) s = "tracker-error"; - else if( e == TR_ERROR_TC_WARNING ) s = "tracker-warning"; - else if( TR_ERROR_IS_TC( e ) ) s = "tracker-other"; - else s = "other"; - tr_bencInitStrDup( val, s ); - break; - } - case IPC_ST_ERRMSG: { - const char * s; - if( !st->error ) s = ""; - else if( !*st->errorString ) s = "other"; - else s = st->errorString; - tr_bencInitStrDup( val, s ); - break; - } - case IPC_ST_ETA: - tr_bencInitInt( val, st->eta ); - break; - case IPC_ST_ID: - tr_bencInitInt( val, torrent_id ); - break; - case IPC_ST_PEERDOWN: - tr_bencInitInt( val, st->peersSendingToUs ); - break; - case IPC_ST_PEERFROM: { - const int * c = st->peersFrom; - tr_bencInitDict( val, 4 ); - tr_bencDictAddInt( val, "cache", c[TR_PEER_FROM_CACHE] ); - tr_bencDictAddInt( val, "incoming", c[TR_PEER_FROM_INCOMING] ); - tr_bencDictAddInt( val, "pex", c[TR_PEER_FROM_PEX] ); - tr_bencDictAddInt( val, "tracker", c[TR_PEER_FROM_TRACKER] ); - break; - } - case IPC_ST_PEERMAX: - tr_bencInitInt( val, tor->maxConnectedPeers ); - break; - case IPC_ST_PEERTOTAL: - tr_bencInitInt( val, st->peersConnected ); - break; - case IPC_ST_PEERUP: - tr_bencInitInt( val, st->peersGettingFromUs ); - break; - case IPC_ST_RUNNING: - tr_bencInitInt( val, TR_STATUS_IS_ACTIVE(st->status) ); - break; - case IPC_ST_STATE: { - const char * s; - if( TR_STATUS_CHECK_WAIT & st->status ) s = "waiting to check"; - else if( TR_STATUS_CHECK & st->status ) s = "checking"; - else if( TR_STATUS_DOWNLOAD & st->status ) s = "downloading"; - else if( TR_STATUS_SEED & st->status ) s = "seeding"; - else if( TR_STATUS_STOPPED & st->status ) s = "paused"; - else s = "error"; - tr_bencInitStr( val, s, -1, 1 ); - break; - } - case IPC_ST_SWARM: - tr_bencInitInt( val, st->swarmspeed * 1024 ); - break; - case IPC_ST_TRACKER: - filltracker( val, st->tracker ); - break; - case IPC_ST_TKDONE: - tr_bencInitInt( val, st->completedFromTracker ); - break; - case IPC_ST_TKLEECH: - tr_bencInitInt( val, st->leechers ); - break; - case IPC_ST_TKSEED: - tr_bencInitInt( val, st->seeders ); - break; - case IPC_ST_UPSPEED: - tr_bencInitInt( val, st->rateUpload * 1024 ); - break; - case IPC_ST_UPTOTAL: - tr_bencInitInt( val, st->uploadedEver ); - break; - default: - assert( 0 ); - break; - } - } - } - - return 0; -} - -/** - * This reads a handshake message from the client to decide - * which IPC protocol version to use. - * Returns 0 on success; otherwise, returns -1 and sets errno. - * - * @see ipc_handleMessages() - * @see ipc_mkvers() - */ -static int -handlevers( struct ipc_info * info, tr_benc * dict ) -{ - tr_benc * vers; - int64_t min, max; - - if( !tr_bencIsDict( dict ) ) - { - errno = EINVAL; - return -1; - } - - vers = tr_bencDictFind( dict, MSGNAME( IPC_MSG_VERSION ) ); - if( NULL == vers ) - { - errno = EINVAL; - return -1; - } - - switch( vers->type ) - { - case TYPE_INT: - min = max = vers->val.i; - break; - case TYPE_DICT: - min = max = -1; - tr_bencDictFindInt( vers, "min", &min ); - tr_bencDictFindInt( vers, "max", &max ); - break; - default: - min = max = -1; - break; - } - - if( 0 >= min || 0 >= max || INT_MAX < min || INT_MAX < max ) - { - errno = EINVAL; - return -1; - } - - assert( PROTO_VERS_MIN <= PROTO_VERS_MAX ); - if( min > max ) - { - errno = EINVAL; - return -1; - } - if( PROTO_VERS_MAX < min || PROTO_VERS_MIN > max ) - { - errno = EPERM; - return -1; - } - - info->vers = MIN( PROTO_VERS_MAX, max ); - - return 0; -} - -static int -compareNameToMsg( const void * a, const void * b ) -{ - const struct msg * msg = b; - return strcmp( a, msg->name ); -} - -static const struct msg * -msglookup( const char * name ) -{ - return bsearch( name, - gl_msgs, TR_N_ELEMENTS( gl_msgs ), sizeof( struct msg ), - compareNameToMsg ); -} - -enum ipc_msg -ipc_msgid( const struct ipc_info * info, const char * name ) -{ - const struct msg * msg = msglookup( name ); - - return msg && sessionSupportsMessage( info, msg->id ) - ? msg->id - : IPC__MSG_COUNT; -} - -/** - * Invokes the trd_msgfunc for the message passed in. - * Returns 0 on success; otherwise, returns -1 and sets errno. - */ -static int -callmsgfunc( const struct ipc_info * info, - tr_benc * name, - tr_benc * val, - tr_benc * tagval, - void * user_data ) -{ - const struct msg * msg; - int64_t tag; - - /* extract tag from tagval */ - if( !tagval ) - tag = -1; - else if( tr_bencIsInt( tagval ) ) - tag = tagval->val.i; - else { - errno = EINVAL; - return -1; - } - - /* find the msg corresponding to `name' */ - if( !tr_bencIsString( name ) ) { - errno = EINVAL; - return -1; - } - msg = msglookup( name->val.s.s ); - - if( msg && msg->minvers <= info->vers ) - { - if( info->funcs->msgs[msg->id] != NULL ) - { - (*info->funcs->msgs[msg->id])( msg->id, val, tag, user_data ); - } - else if( info->funcs->def ) - { - info->funcs->def( msg->id, val, tag, user_data ); - } - } - else if( NULL != info->funcs->def ) - info->funcs->def( IPC__MSG_UNKNOWN, NULL, tag, user_data ); - - return 0; -} - -static int -handlemsgs( const struct ipc_info * session, - tr_benc * message, - void * user_data ) -{ - tr_benc * name, * val, * tag; - - assert( ipc_hasvers( session ) ); - - if( DICTPAYLOAD( session ) ) - { - int ii; - - if( TYPE_DICT != message->type || message->val.l.count % 2 ) - { - errno = EINVAL; - return -1; - } - - for( ii = 0; ii < message->val.l.count; ii += 2 ) - { - assert( ii + 1 < message->val.l.count ); - name = &message->val.l.vals[ii]; - val = &message->val.l.vals[ii+1]; - if( 0 > callmsgfunc( session, name, val, NULL, user_data ) ) - return -1; - } - } - else - { - if( TYPE_LIST != message->type || 2 > message->val.l.count ) - { - errno = EINVAL; - return -1; - } - - name = &message->val.l.vals[0]; - val = &message->val.l.vals[1]; - tag = ( 2 == message->val.l.count ? NULL : &message->val.l.vals[2] ); - if( 0 > callmsgfunc( session, name, val, tag, user_data ) ) - return -1; - } - - return 0; -} - -ssize_t -ipc_handleMessages( struct ipc_info * info, - const uint8_t * msgs, - ssize_t msgslen, - void * user_data ) -{ - char hex[IPC_MIN_MSG_LEN+1], * end; - ssize_t off, len; - tr_benc benc; - - for( off = 0; off + IPC_MIN_MSG_LEN < msgslen; off += IPC_MIN_MSG_LEN + len ) - { - memcpy( hex, msgs + off, IPC_MIN_MSG_LEN ); - hex[IPC_MIN_MSG_LEN] = '\0'; - end = NULL; - len = strtol( hex, &end, 16 ); - if( hex + IPC_MIN_MSG_LEN != end || - 0 > len || IPC_MAX_MSG_LEN < len ) - { - errno = EINVAL; - return -1; - } - if( off + IPC_MIN_MSG_LEN + len > msgslen ) - { - break; - } - errno = 0; - if( tr_bencLoad( msgs + off + IPC_MIN_MSG_LEN, len, &benc, NULL ) ) - { - if( 0 == errno ) - { - errno = EINVAL; - } - return -1; - } - if( 0 > ( ipc_hasvers( info ) ? handlemsgs( info, &benc, user_data ) : - handlevers( info, &benc ) ) ) - { - SAFEBENCFREE( &benc ); - return -1; - } - tr_bencFree( &benc ); - } - - return off; -} - -static int -compareNameToInf( const void * a, const void * b ) -{ - const struct inf * inf = b; - return strcmp( a, inf->name ); -} - -/** - * Convert a benc list of string keys from gl_inf or gl_stat - * into a bitwise-or'ed int representation. - * msg_id must be either IPC_MSG_INFO or IPC_MSG_STAT. - * @see ipc_infoname() - */ -int -ipc_infotypes( enum ipc_msg id, const tr_benc * list ) -{ - const struct inf * array; - size_t len; - int i; - int ret; - - switch( id ) - { - case IPC_MSG_INFO: - array = gl_inf; - len = TR_N_ELEMENTS( gl_inf ); - break; - case IPC_MSG_STAT: - array = gl_stat; - len = TR_N_ELEMENTS( gl_stat ); - break; - default: - assert( 0 ); - break; - } - - ret = IPC_INF_ID; - - if( !tr_bencIsList( list ) ) - return ret; - - for( i=0; ival.l.count; ++i ) - { - const tr_benc * name = &list->val.l.vals[i]; - const struct inf * inf; - - if( !tr_bencIsString( name ) ) - continue; - - inf = bsearch( name->val.s.s, - array, len, sizeof( struct inf ), - compareNameToInf ); - if( inf ) - ret |= inf->type; - } - - return ret; -} - -/** - * This function is the reverse of ipc_infotypes: - * it returns the string key that corresponds to the type passed in. - * Type is one of the IPC_INF_* or IPC_ST_* enums from ipcparse.h. - * msg_id must be either IPC_MSG_INFO or IPC_MSG_STAT. - * @see ipc_infotypes() - */ -const char * -ipc_infoname( enum ipc_msg id, int type ) -{ - const struct inf * it; - const struct inf * end; - - switch( id ) - { - case IPC_MSG_INFO: - it = gl_inf; - end = it + TR_N_ELEMENTS( gl_inf ); - break; - case IPC_MSG_STAT: - it = gl_stat; - end = it + TR_N_ELEMENTS( gl_stat ); - break; - default: - assert( 0 ); - break; - } - - for( ; it!=end; ++it ) - if( it->type == type ) - return it->name; - - assert( 0 ); - return NULL; -} diff --git a/libtransmission/ipcparse.h b/libtransmission/ipcparse.h deleted file mode 100644 index 5b3cc4b34..000000000 --- a/libtransmission/ipcparse.h +++ /dev/null @@ -1,199 +0,0 @@ -/****************************************************************************** - * $Id$ - * - * Copyright (c) 2007-2008 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. - *****************************************************************************/ - -#ifndef TR_DAEMON_IPC_H -#define TR_DAEMON_IPC_H - -#include -#include /* for ssize_t */ - -#define IPC_MIN_MSG_LEN ( 8 ) -#define IPC_MAX_MSG_LEN ( 0x7fffffff - IPC_MIN_MSG_LEN ) - -/* These need to be in the same order as ipcparse.c's gl_msgs array. */ -enum ipc_msg -{ - IPC_MSG_ADDONEFILE = 0, - IPC_MSG_ADDMANYFILES, - IPC_MSG_AUTOMAP, - IPC_MSG_AUTOSTART, - IPC_MSG_BAD, - IPC_MSG_DIR, - IPC_MSG_DOWNLIMIT, - IPC_MSG_CRYPTO, - IPC_MSG_FAIL, - IPC_MSG_GETAUTOMAP, - IPC_MSG_GETAUTOSTART, - IPC_MSG_GETDIR, - IPC_MSG_GETDOWNLIMIT, - IPC_MSG_GETCRYPTO, - IPC_MSG_GETINFO, - IPC_MSG_GETINFOALL, - IPC_MSG_GETPEX, - IPC_MSG_GETPORT, - IPC_MSG_GETSTAT, - IPC_MSG_GETSTATALL, - IPC_MSG_GETSUP, - IPC_MSG_GETUPLIMIT, - IPC_MSG_INFO, - IPC_MSG_LOOKUP, - IPC_MSG_NOOP, - IPC_MSG_NOTSUP, - IPC_MSG_PEERMAX, - IPC_MSG_PEX, - IPC_MSG_PORT, - IPC_MSG_QUIT, - IPC_MSG_REMOVE, - IPC_MSG_REMOVEALL, - IPC_MSG_START, - IPC_MSG_STARTALL, - IPC_MSG_STAT, - IPC_MSG_STOP, - IPC_MSG_STOPALL, - IPC_MSG_OK, - IPC_MSG_SUP, - IPC_MSG_UPLIMIT, - IPC_MSG_VERIFY, - IPC_MSG_VERSION, - IPC__MSG_COUNT, - IPC__MSG_UNKNOWN -}; - -/* If you add or delete a constant here, to renumber the ones after it. - * They need to be in ascending order starting at zero, with no gaps. - * They also need to be in the same order as ipcparse.c's gl_inf array. */ -#define IPC_INF_COMMENT ( 1 << 0 ) -#define IPC_INF_CREATOR ( 1 << 1 ) -#define IPC_INF_DATE ( 1 << 2 ) -#define IPC_INF_FILES ( 1 << 3 ) -#define IPC_INF_HASH ( 1 << 4 ) -#define IPC_INF_ID ( 1 << 5 ) -#define IPC_INF_NAME ( 1 << 6 ) -#define IPC_INF_PATH ( 1 << 7 ) -#define IPC_INF_PRIVATE ( 1 << 8 ) -#define IPC_INF_SIZE ( 1 << 9 ) -#define IPC_INF_TRACKERS ( 1 << 10 ) -#define IPC_INF__MAX ( 1 << 11 ) - -/* If you add or delete a constant here, to renumber the ones after it. - * They need to be in ascending order starting at zero, with no gaps. - * They also need to be in the same order as ipcparse.c's gl_stat array. */ -#define IPC_ST_COMPLETED ( 1 << 0 ) -#define IPC_ST_DOWNSPEED ( 1 << 1 ) -#define IPC_ST_DOWNTOTAL ( 1 << 2 ) -#define IPC_ST_DOWNVALID ( 1 << 3 ) -#define IPC_ST_ERROR ( 1 << 4 ) -#define IPC_ST_ERRMSG ( 1 << 5 ) -#define IPC_ST_ETA ( 1 << 6 ) -#define IPC_ST_ID ( 1 << 7 ) -#define IPC_ST_PEERDOWN ( 1 << 8 ) -#define IPC_ST_PEERFROM ( 1 << 9 ) -#define IPC_ST_PEERMAX ( 1 << 10 ) -#define IPC_ST_PEERTOTAL ( 1 << 11 ) -#define IPC_ST_PEERUP ( 1 << 12 ) -#define IPC_ST_RUNNING ( 1 << 13 ) -#define IPC_ST_TKDONE ( 1 << 14 ) -#define IPC_ST_TKLEECH ( 1 << 15 ) -#define IPC_ST_TKSEED ( 1 << 16 ) -#define IPC_ST_STATE ( 1 << 17 ) -#define IPC_ST_SWARM ( 1 << 18 ) -#define IPC_ST_TRACKER ( 1 << 19 ) -#define IPC_ST_UPSPEED ( 1 << 20 ) -#define IPC_ST_UPTOTAL ( 1 << 21 ) -#define IPC_ST__MAX ( 1 << 22 ) - -struct ipc_funcs; -struct ipc_info; -struct tr_info; -struct tr_benc; -struct tr_stat; - - -#define TORRENT_ID_VALID( id ) ( ( 0 < (id) ) && ( (id) < INT_MAX ) ) - -typedef void ( *trd_msgfunc )( enum ipc_msg msg_id, - struct tr_benc * benc, - int64_t tag, - void * arg ); - -/* any of these functions that can fail may set errno for any of the - errors set by malloc() or calloc() */ - -/* setup */ -struct ipc_funcs * ipc_initmsgs ( void ); -void ipc_addmsg ( struct ipc_funcs *, enum ipc_msg, - trd_msgfunc ); -void ipc_setdefmsg( struct ipc_funcs *, trd_msgfunc ); -void ipc_freemsgs ( struct ipc_funcs * ); -struct ipc_info * ipc_newcon ( struct ipc_funcs * ); -void ipc_freecon ( struct ipc_info * ); -int ipc_hasvers ( const struct ipc_info * ); - -/* message creation */ -/* sets errno to EPERM if requested message not supported by protocol vers */ -struct tr_benc * ipc_initval ( const struct ipc_info *, enum ipc_msg, - int64_t tag, struct tr_benc *, int ); -uint8_t * ipc_serialize ( const struct tr_benc *, size_t * ); -uint8_t * ipc_mkempty ( const struct ipc_info *, size_t *, enum ipc_msg, - int64_t ); -uint8_t * ipc_mkint ( const struct ipc_info *, size_t *, enum ipc_msg, - int64_t tag, int64_t val ); -uint8_t * ipc_mkstr ( const struct ipc_info *, size_t *, enum ipc_msg, - int64_t tag, const char * val ); -uint8_t * ipc_mkvers ( size_t *, const char * ); - -uint8_t * ipc_createInfoRequest( const struct ipc_info * session, - size_t * setme_len, - enum ipc_msg msg_id, - int64_t tag, - int types, - const int * ids ); - -struct tr_torrent; - -int ipc_addinfo ( struct tr_benc * appendme_list, - int torrent_id, - struct tr_torrent * tor, - int info_types ); - -int ipc_addstat ( struct tr_benc * appendme_list, - int torrent_id, - struct tr_torrent * tor, - int stat_types ); - -/* sets errno to EINVAL on parse error or - EPERM for unsupported protocol version */ -ssize_t ipc_handleMessages( struct ipc_info * session, - const uint8_t * serializedMessages , - ssize_t serializedLength, - void * user_data ); - -/* misc info functions, these will always succeed */ -enum ipc_msg ipc_msgid ( const struct ipc_info *, const char * ); -int ipc_ishandled( const struct ipc_info *, enum ipc_msg ); -int ipc_havetags ( const struct ipc_info * ); -int ipc_infotypes( enum ipc_msg, const struct tr_benc * ); -const char * ipc_infoname ( enum ipc_msg, int ); - -#endif /* TR_DAEMON_IPC_H */ diff --git a/libtransmission/json-test.c b/libtransmission/json-test.c index 71db8b13a..d82f623a9 100644 --- a/libtransmission/json-test.c +++ b/libtransmission/json-test.c @@ -1,6 +1,7 @@ #include #include "transmission.h" #include "bencode.h" +#include "json.h" #include "utils.h" /* tr_free */ #define VERBOSE 0 @@ -39,7 +40,7 @@ test1( void ) const char * str; int64_t i; const uint8_t * end = NULL; - const int err = tr_jsonParse( in, in+strlen(in), &top, &end ); + const int err = tr_jsonParse( in, strlen(in), &top, &end ); check( !err ); check( tr_bencIsDict( &top ) ); diff --git a/libtransmission/json.c b/libtransmission/json.c index 59cd50f8a..829f773c3 100644 --- a/libtransmission/json.c +++ b/libtransmission/json.c @@ -120,12 +120,13 @@ callback( void * vdata, int type, const struct JSON_value_struct * value ) int tr_jsonParse( const void * vbuf, - const void * bufend, + size_t len, tr_benc * setme_benc, const uint8_t ** setme_end ) { int err = 0; const char * buf = vbuf; + const void * bufend = buf + len; struct JSON_checker_struct * checker; struct json_benc_data data; diff --git a/libtransmission/json.h b/libtransmission/json.h index 2ba1dd1d9..83fcf77f6 100644 --- a/libtransmission/json.h +++ b/libtransmission/json.h @@ -13,7 +13,7 @@ #ifndef TR_JSON_H int tr_jsonParse( const void * vbuf, - const void * bufend, + size_t len, struct tr_benc * setme_benc, const uint8_t ** setme_end ); diff --git a/libtransmission/makemeta.c b/libtransmission/makemeta.c index a1a3751d1..e3c0c96a3 100644 --- a/libtransmission/makemeta.c +++ b/libtransmission/makemeta.c @@ -22,7 +22,6 @@ #include #include "crypto.h" /* tr_sha1 */ -#include "trcompat.h" /* strlcpy */ #include "transmission.h" #include "bencode.h" #include "makemeta.h" @@ -126,8 +125,8 @@ tr_metaInfoBuilderCreate( tr_handle * handle, const char * topFile ) char *dir, *base; char dirbuf[MAX_PATH_LENGTH]; char basebuf[MAX_PATH_LENGTH]; - strlcpy( dirbuf, topFile, sizeof( dirbuf ) ); - strlcpy( basebuf, topFile, sizeof( basebuf ) ); + tr_strlcpy( dirbuf, topFile, sizeof( dirbuf ) ); + tr_strlcpy( basebuf, topFile, sizeof( basebuf ) ); dir = dirname( dirbuf ); base = basename( basebuf ); files = getFiles( dir, base, NULL ); @@ -326,7 +325,7 @@ makeInfoDict ( tr_benc * dict, } } - strlcpy( base, builder->top, sizeof( base ) ); + tr_strlcpy( base, builder->top, sizeof( base ) ); tr_bencDictAddStr( dict, "name", basename( base ) ); tr_bencDictAddInt( dict, "piece length", builder->pieceSize ); @@ -367,7 +366,7 @@ tr_realMakeMetaInfo ( tr_metainfo_builder * builder ) if ( !builder->result && !builder->abortFlag ) { if( tr_bencSaveFile( builder->outputFile, &top ) ) { builder->my_errno = errno; - strlcpy( builder->errfile, builder->outputFile, sizeof( builder->errfile ) ); + tr_strlcpy( builder->errfile, builder->outputFile, sizeof( builder->errfile ) ); builder->result = TR_MAKEMETA_IO_WRITE; } } diff --git a/libtransmission/metainfo.c b/libtransmission/metainfo.c index 4c2f76b19..3a7089dc2 100644 --- a/libtransmission/metainfo.c +++ b/libtransmission/metainfo.c @@ -39,7 +39,6 @@ #include "crypto.h" /* tr_sha1 */ #include "metainfo.h" #include "platform.h" -#include "trcompat.h" /* strlcpy */ #include "utils.h" /*********************************************************************** diff --git a/libtransmission/peer-mgr.c b/libtransmission/peer-mgr.c index 182379c35..a69cbc2ae 100644 --- a/libtransmission/peer-mgr.c +++ b/libtransmission/peer-mgr.c @@ -32,7 +32,6 @@ #include "ptrarray.h" #include "ratecontrol.h" #include "torrent.h" -#include "trcompat.h" /* strlcpy */ #include "trevent.h" #include "utils.h" @@ -932,7 +931,7 @@ msgsCallbackFunc( void * vpeer, void * vevent, void * vt ) case TR_PEERMSG_ERROR: if( TR_ERROR_IS_IO( e->err ) ) { t->tor->error = e->err; - strlcpy( t->tor->errorString, tr_errorString( e->err ), sizeof(t->tor->errorString) ); + tr_strlcpy( t->tor->errorString, tr_errorString( e->err ), sizeof(t->tor->errorString) ); tr_torrentStop( t->tor ); } else if( e->err == TR_ERROR_ASSERT ) { addStrike( t, peer ); @@ -1492,7 +1491,7 @@ tr_peerMgrPeerStats( const tr_peerMgr * manager, tr_peer_stat * stat = ret + i; tr_netNtop( &peer->in_addr, stat->addr, sizeof(stat->addr) ); - strlcpy( stat->client, (peer->client ? peer->client : ""), sizeof(stat->client) ); + tr_strlcpy( stat->client, (peer->client ? peer->client : ""), sizeof(stat->client) ); stat->port = peer->port; stat->from = atom->from; stat->progress = peer->progress; diff --git a/libtransmission/peer-msgs.c b/libtransmission/peer-msgs.c index ac12153bc..b8ed4f6d3 100644 --- a/libtransmission/peer-msgs.c +++ b/libtransmission/peer-msgs.c @@ -1,5 +1,5 @@ /* - * This file Copyright (C) 2007 Charles Kerr + * This file Copyright (C) 2007-2008 Charles Kerr * * This file is licensed by the GPL version 2. Works owned by the * Transmission project are granted a special exemption to clause 2(b) @@ -1275,7 +1275,7 @@ readBtMessage( tr_peermsgs * msgs, struct evbuffer * inbuf, size_t inlen ) if( tr_bitfieldAdd( msgs->info->have, ui32 ) ) fireError( msgs, TR_ERROR_PEER_MESSAGE ); updatePeerProgress( msgs ); - tr_rcTransferred( msgs->torrent->swarmspeed, msgs->torrent->info.pieceSize ); + tr_rcTransferred( msgs->torrent->swarmSpeed, msgs->torrent->info.pieceSize ); break; case BT_BITFIELD: { diff --git a/libtransmission/platform.c b/libtransmission/platform.c index fc1af3a6d..949e898c6 100644 --- a/libtransmission/platform.c +++ b/libtransmission/platform.c @@ -51,7 +51,6 @@ #include "transmission.h" #include "platform.h" -#include "trcompat.h" #include "utils.h" /*** @@ -309,7 +308,7 @@ getOldConfigDir( void ) "Library", "Application Support", "Transmission", NULL ); #elif defined(__AMIGAOS4__) - strlcpy( buf, "PROGDIR:.transmission", sizeof( buf ) ); + tr_strlcpy( buf, "PROGDIR:.transmission", sizeof( buf ) ); #elif defined(WIN32) char appdata[MAX_PATH_LENGTH]; SHGetFolderPath( NULL, CSIDL_APPDATA, NULL, 0, appdata ); @@ -448,7 +447,7 @@ tr_setConfigDir( tr_handle * handle, const char * configDir ) } const char * -tr_getConfigDir( const tr_handle * handle ) +tr_sessionGetConfigDir( const tr_handle * handle ) { return handle->configDir; } diff --git a/libtransmission/resume.c b/libtransmission/resume.c index 41f1c51fb..5655a3e57 100644 --- a/libtransmission/resume.c +++ b/libtransmission/resume.c @@ -24,17 +24,17 @@ #include "torrent.h" #include "utils.h" /* tr_buildPath */ -#define KEY_CORRUPT "corrupt" -#define KEY_DESTINATION "destination" -#define KEY_DND "dnd" -#define KEY_DOWNLOADED "downloaded" -#define KEY_MAX_PEERS "max-peers" -#define KEY_PAUSED "paused" -#define KEY_PEERS "peers" -#define KEY_PRIORITY "priority" -#define KEY_PROGRESS "progress" -#define KEY_SPEEDLIMIT "speed-limit" -#define KEY_UPLOADED "uploaded" +#define KEY_CORRUPT "corrupt" +#define KEY_DOWNLOAD_DIR "destination" +#define KEY_DND "dnd" +#define KEY_DOWNLOADED "downloaded" +#define KEY_MAX_PEERS "max-peers" +#define KEY_PAUSED "paused" +#define KEY_PEERS "peers" +#define KEY_PRIORITY "priority" +#define KEY_PROGRESS "progress" +#define KEY_SPEEDLIMIT "speed-limit" +#define KEY_UPLOADED "uploaded" #define KEY_SPEEDLIMIT_DOWN_SPEED "down-speed" #define KEY_SPEEDLIMIT_DOWN_MODE "down-mode" @@ -349,8 +349,8 @@ tr_torrentSaveResume( const tr_torrent * tor ) tr_bencInitDict( &top, 12 ); tr_bencDictAddInt( &top, KEY_CORRUPT, tor->corruptPrev + tor->corruptCur ); - tr_bencDictAddStr( &top, KEY_DESTINATION, - tor->destination ); + tr_bencDictAddStr( &top, KEY_DOWNLOAD_DIR, + tor->downloadDir ); tr_bencDictAddInt( &top, KEY_DOWNLOADED, tor->downloadedPrev + tor->downloadedCur ); tr_bencDictAddInt( &top, KEY_UPLOADED, @@ -406,11 +406,11 @@ loadFromFile( tr_torrent * tor, fieldsLoaded |= TR_FR_CORRUPT; } - if( ( fieldsToLoad & ( TR_FR_PROGRESS | TR_FR_DESTINATION ) ) - && tr_bencDictFindStr( &top, KEY_DESTINATION, &str ) ) { - tr_free( tor->destination ); - tor->destination = tr_strdup( str ); - fieldsLoaded |= TR_FR_DESTINATION; + if( ( fieldsToLoad & ( TR_FR_PROGRESS | TR_FR_DOWNLOAD_DIR ) ) + && tr_bencDictFindStr( &top, KEY_DOWNLOAD_DIR, &str ) ) { + tr_free( tor->downloadDir ); + tor->downloadDir = tr_strdup( str ); + fieldsLoaded |= TR_FR_DOWNLOAD_DIR; } if( ( fieldsToLoad & TR_FR_DOWNLOADED ) @@ -473,12 +473,12 @@ setFromCtor( tr_torrent * tor, uint64_t fields, const tr_ctor * ctor, int mode ) if( !tr_ctorGetPeerLimit( ctor, mode, &tor->maxConnectedPeers ) ) ret |= TR_FR_MAX_PEERS; - if( fields & TR_FR_DESTINATION ) { - const char * destination; - if( !tr_ctorGetDestination( ctor, mode, &destination ) ) { - ret |= TR_FR_DESTINATION; - tr_free( tor->destination ); - tor->destination = tr_strdup( destination ); + if( fields & TR_FR_DOWNLOAD_DIR ) { + const char * downloadDir; + if( !tr_ctorGetDownloadDir( ctor, mode, &downloadDir ) ) { + ret |= TR_FR_DOWNLOAD_DIR; + tr_free( tor->downloadDir ); + tor->downloadDir = tr_strdup( downloadDir ); } } diff --git a/libtransmission/resume.h b/libtransmission/resume.h index 2bd24f27a..0d24e4550 100644 --- a/libtransmission/resume.h +++ b/libtransmission/resume.h @@ -15,17 +15,17 @@ enum { - TR_FR_DOWNLOADED = (1<<0), - TR_FR_UPLOADED = (1<<1), - TR_FR_CORRUPT = (1<<2), - TR_FR_PEERS = (1<<3), - TR_FR_PROGRESS = (1<<4), - TR_FR_DND = (1<<5), - TR_FR_PRIORITY = (1<<6), - TR_FR_SPEEDLIMIT = (1<<7), - TR_FR_RUN = (1<<8), - TR_FR_DESTINATION = (1<<9), - TR_FR_MAX_PEERS = (1<<10) + TR_FR_DOWNLOADED = (1<<0), + TR_FR_UPLOADED = (1<<1), + TR_FR_CORRUPT = (1<<2), + TR_FR_PEERS = (1<<3), + TR_FR_PROGRESS = (1<<4), + TR_FR_DND = (1<<5), + TR_FR_PRIORITY = (1<<6), + TR_FR_SPEEDLIMIT = (1<<7), + TR_FR_RUN = (1<<8), + TR_FR_DOWNLOAD_DIR = (1<<9), + TR_FR_MAX_PEERS = (1<<10) }; /** diff --git a/libtransmission/rpc-server.c b/libtransmission/rpc-server.c new file mode 100644 index 000000000..3b8df262f --- /dev/null +++ b/libtransmission/rpc-server.c @@ -0,0 +1,228 @@ +/* + * This file Copyright (C) 2008 Charles Kerr + * + * This file is licensed by the GPL version 2. Works owned by the + * Transmission project are granted a special exemption to clause 2(b) + * so that the bulk of its code can remain under the MIT license. + * This exemption does not extend to derived works not owned by + * the Transmission project. + * + * $Id:$ + */ + +#include +#include + +#include +#include + +#include "transmission.h" +#include "rpc.h" +#include "rpc-server.h" +#include "utils.h" + +#define BUSY_INTERVAL_MSEC 30 +#define IDLE_INTERVAL_MSEC 1000 + +struct tr_rpc_server +{ + int port; + struct shttpd_ctx * ctx; + tr_handle * session; + struct evbuffer * in; + struct evbuffer * out; + struct event timer; + char * acl; +}; + +static void +handle_rpc( struct shttpd_arg * arg ) +{ + struct tr_rpc_server * s = arg->user_data; + + if( !EVBUFFER_LENGTH( s->out ) ) + { + int len = 0; + char * response = NULL; + const char * request_method = shttpd_get_env( arg, "REQUEST_METHOD" ); + const char * query_string = shttpd_get_env( arg, "QUERY_STRING" ); + + if( query_string && *query_string ) + response = tr_rpc_request_exec_uri( s->session, + query_string, + strlen( query_string ), + &len ); + else if( !strcmp( request_method, "POST" ) ) + { + evbuffer_add( s->in, arg->in.buf, arg->in.len ); + arg->in.num_bytes = arg->in.len; + + if( ! ( arg->flags & SHTTPD_MORE_POST_DATA ) ) + return; + response = tr_rpc_request_exec_uri( s->session, + EVBUFFER_DATA( s->in ), + EVBUFFER_LENGTH( s->in ), + &len ); + evbuffer_drain( s->in, EVBUFFER_LENGTH( s->in ) ); + } + + evbuffer_add_printf( s->out, "HTTP/1.1 200 OK\r\n" + "Content-Type: text/x-json\r\n" + "Content-Length: %d\r\n" + "\r\n" + "%*.*s\r\n", len, len, len, response ); + tr_free( response ); + } + + if( EVBUFFER_LENGTH( s->out ) ) + { + const int n = MIN( ( int )EVBUFFER_LENGTH( s->out ), arg->out.len ); + memcpy( arg->out.buf, EVBUFFER_DATA( s->out ), n ); + evbuffer_drain( s->out, n ); + arg->out.num_bytes = n; + } + + if( !EVBUFFER_LENGTH( s->out ) ) + arg->flags |= SHTTPD_END_OF_OUTPUT; +} + +static void +rpcPulse( int socket UNUSED, short action UNUSED, void * vserver ) +{ + int interval; + struct timeval tv; + tr_rpc_server * server = vserver; + + assert( server ); + + shttpd_poll( server->ctx, 1 ); + + /* set a timer for the next pulse */ + if( EVBUFFER_LENGTH( server->in ) || EVBUFFER_LENGTH( server->out ) ) + interval = BUSY_INTERVAL_MSEC; + else + interval = IDLE_INTERVAL_MSEC; + tv = tr_timevalMsec( interval ); + evtimer_add( &server->timer, &tv ); +} + +static void +startServer( tr_rpc_server * server ) +{ + if( !server->ctx ) + { + char ports[128]; + struct timeval tv = tr_timevalMsec( IDLE_INTERVAL_MSEC ); + + server->ctx = shttpd_init( ); + snprintf( ports, sizeof( ports ), "%d", server->port ); + shttpd_register_uri( server->ctx, "/transmission", handle_rpc, server ); + shttpd_set_option( server->ctx, "ports", ports ); + shttpd_set_option( server->ctx, "dir_list", "0" ); + shttpd_set_option( server->ctx, "root", "/dev/null" ); + if( server->acl ) + shttpd_set_option( server->ctx, "acl", server->acl ); + + evtimer_set( &server->timer, rpcPulse, server ); + evtimer_add( &server->timer, &tv ); + } +} + +static void +stopServer( tr_rpc_server * server ) +{ + if( server->ctx ) + { + evtimer_del( &server->timer ); + shttpd_fini( server->ctx ); + server->ctx = NULL; + } +} + +void +tr_rpcSetEnabled( tr_rpc_server * server, int isEnabled ) +{ + if( !isEnabled && server->ctx ) + stopServer( server ); + + if( isEnabled && !server->ctx ) + startServer( server ); +} + +int +tr_rpcIsEnabled( const tr_rpc_server * server ) +{ + return server->ctx != NULL; +} + +void +tr_rpcSetPort( tr_rpc_server * server, int port ) +{ + if( server->port != port ) + { + server->port = port; + + if( server->ctx ) + { + stopServer( server ); + startServer( server ); + } + } +} + +int +tr_rpcGetPort( const tr_rpc_server * server ) +{ + return server->port; +} + +void +tr_rpcSetACL( tr_rpc_server * server, const char * acl ) +{ + const int isRunning = server->ctx != NULL; + + if( isRunning ) + stopServer( server ); + + tr_free( server->acl ); + server->acl = tr_strdup( acl ); + + if( isRunning ) + startServer( server ); +} + +const char* +tr_rpcGetACL( const tr_rpc_server * server ) +{ + return server->acl ? server->acl : ""; +} + +void +tr_rpcClose( tr_rpc_server ** ps ) +{ + tr_rpc_server * s = *ps; + *ps = NULL; + + stopServer( s ); + evbuffer_free( s->in ); + evbuffer_free( s->out ); + tr_free( s ); +} + +tr_rpc_server * +tr_rpcInit( tr_handle * session, + int isEnabled, + int port, + const char * acl ) +{ + tr_rpc_server * s = tr_new0( tr_rpc_server, 1 ); + s->session = session; + s->port = port; + s->in = evbuffer_new( ); + s->out = evbuffer_new( ); + s->acl = tr_strdup( acl ); + + if( isEnabled ) + startServer( s ); + return s; +} diff --git a/libtransmission/rpc-server.h b/libtransmission/rpc-server.h new file mode 100644 index 000000000..d0bb55048 --- /dev/null +++ b/libtransmission/rpc-server.h @@ -0,0 +1,40 @@ +/* + * This file Copyright (C) 2008 Charles Kerr + * + * This file is licensed by the GPL version 2. Works owned by the + * Transmission project are granted a special exemption to clause 2(b) + * so that the bulk of its code can remain under the MIT license. + * This exemption does not extend to derived works not owned by + * the Transmission project. + * + * $Id:$ + */ + +#ifndef TR_RPC_SERVER_H +#define TR_RPC_SERVER_H + +typedef struct tr_rpc_server tr_rpc_server; + +tr_rpc_server * tr_rpcInit ( struct tr_handle * session, + int isEnabled, + int port, + const char * acl ); + +void tr_rpcClose ( tr_rpc_server ** freeme ); + +void tr_rpcSetEnabled( tr_rpc_server * server, + int isEnabled ); + +int tr_rpcIsEnabled ( const tr_rpc_server * server ); + +void tr_rpcSetPort ( tr_rpc_server * server, + int port ); + +int tr_rpcGetPort ( const tr_rpc_server * server ); + +void tr_rpcSetACL ( tr_rpc_server * server, + const char * acl ); + +const char* tr_rpcGetACL ( const tr_rpc_server * server ); + +#endif diff --git a/libtransmission/rpc.c b/libtransmission/rpc.c index e12c0fd9d..1a97b6750 100644 --- a/libtransmission/rpc.c +++ b/libtransmission/rpc.c @@ -11,16 +11,30 @@ */ #include +#include /* isdigit */ +#include /* strtol */ #include /* strcmp */ #include "transmission.h" #include "bencode.h" #include "rpc.h" #include "json.h" +#include "session.h" #include "torrent.h" #include "utils.h" -#define TR_N_ELEMENTS( ary ) ( sizeof( ary ) / sizeof( (ary)[0] ) ) +#define TR_N_ELEMENTS( ary ) ( sizeof( ary ) / sizeof( *ary ) ) + +/*** +**** +***/ + +static void +notify( tr_handle * session, int type, tr_torrent * tor ) +{ + if( session->rpc_func != NULL ) + session->rpc_func( session, type, tor, session->rpc_func_user_data ); +} /*** **** @@ -29,6 +43,7 @@ static tr_torrent ** getTorrents( tr_handle * handle, tr_benc * args, int * setmeCount ) { + int64_t id; tr_torrent ** torrents = NULL; int torrentCount = 0; tr_benc * ids; @@ -54,6 +69,14 @@ getTorrents( tr_handle * handle, tr_benc * args, int * setmeCount ) torrents[torrentCount++] = tor; } } + else if( tr_bencDictFindInt( args, "ids", &id ) + || tr_bencDictFindInt( args, "id", &id ) ) + { + tr_torrent * tor; + torrents = tr_new0( tr_torrent*, 1 ); + if(( tor = tr_torrentFindFromId( handle, id ))) + torrents[torrentCount++] = tor; + } else /* all of them */ { tr_torrent * tor = NULL; @@ -67,109 +90,132 @@ getTorrents( tr_handle * handle, tr_benc * args, int * setmeCount ) return torrents; } -typedef void( *tor_func )( tr_torrent * tor ); - -static void -callTorrentFunc( tr_handle * h, tr_benc * args_in, tor_func func ) +static const char* +torrentStart( tr_handle * h, tr_benc * args_in, tr_benc * args_out UNUSED ) { int i, torrentCount; tr_torrent ** torrents = getTorrents( h, args_in, &torrentCount ); for( i=0; ipeersFrom; - const struct tr_tracker_stat * s = &st->tracker_stat; + const struct tr_tracker_stat * s = &st->trackerStat; + tr_benc * d = tr_bencListAddDict( list, 33 ); + tr_benc * t; - tr_bencDictAddInt( d, "id", tor->uniqueId ); - tr_bencDictAddInt( d, "status", st->status ); + tr_bencDictAddInt( d, "activityDate", st->activityDate ); + tr_bencDictAddInt( d, "completedFromTracker", st->completedFromTracker ); + tr_bencDictAddInt( d, "corruptEver", st->corruptEver ); + tr_bencDictAddInt( d, "desiredAvailable", st->desiredAvailable ); + tr_bencDictAddInt( d, "downloadedEver", st->downloadedEver ); tr_bencDictAddInt( d, "error", st->error ); tr_bencDictAddStr( d, "errorString", st->errorString ); - tr_bencDictAddDouble( d, "recheckProgress", st->recheckProgress ); - tr_bencDictAddDouble( d, "percentComplete", st->percentComplete ); - tr_bencDictAddDouble( d, "percentDone", st->percentDone ); - tr_bencDictAddDouble( d, "rateDownload", st->rateDownload ); - tr_bencDictAddDouble( d, "rateUpload", st->rateUpload ); - tr_bencDictAddDouble( d, "swarmspeed", st->swarmspeed ); - tr_bencDictAddDouble( d, "ratio", st->ratio ); tr_bencDictAddInt( d, "eta", st->eta ); - tr_bencDictAddInt( d, "peersKnown", st->peersKnown ); - tr_bencDictAddInt( d, "peersConnected", st->peersConnected ); - tr_bencDictAddInt( d, "peersSendingToUs", st->peersSendingToUs ); - tr_bencDictAddInt( d, "peersGettingFromUs", st->peersGettingFromUs ); - tr_bencDictAddInt( d, "seeders", st->seeders ); + tr_bencDictAddInt( d, "haveUnchecked", st->haveUnchecked ); + tr_bencDictAddInt( d, "haveValid", st->haveValid ); + tr_bencDictAddInt( d, "id", tr_torrentId( tor ) ); tr_bencDictAddInt( d, "leechers", st->leechers ); - tr_bencDictAddInt( d, "completedFromTracker", st->completedFromTracker ); - tr_bencDictAddInt( d, "manualAnnounceTime", st->manualAnnounceTime ); - tr_bencDictAddInt( d, "sizeWhenDone", st->sizeWhenDone ); tr_bencDictAddInt( d, "leftUntilDone", st->leftUntilDone ); - tr_bencDictAddInt( d, "desiredAvailable", st->desiredAvailable ); - tr_bencDictAddInt( d, "corruptEver", st->corruptEver ); - tr_bencDictAddInt( d, "uploadedEver", st->uploadedEver ); - tr_bencDictAddInt( d, "downloadedEver", st->downloadedEver ); - tr_bencDictAddInt( d, "haveValid", st->haveValid ); - tr_bencDictAddInt( d, "haveUnchecked", st->haveUnchecked ); - tr_bencDictAddInt( d, "startDate", st->startDate ); - tr_bencDictAddInt( d, "activityDate", st->activityDate ); + tr_bencDictAddInt( d, "manualAnnounceTime", st->manualAnnounceTime ); + tr_bencDictAddStr( d, "name", tor->info.name ); + tr_bencDictAddInt( d, "peersConnected", st->peersConnected ); t = tr_bencDictAddDict( d, "peersFrom", 4 ); tr_bencDictAddInt( t, "cache", f[TR_PEER_FROM_CACHE] ); tr_bencDictAddInt( t, "incoming", f[TR_PEER_FROM_INCOMING] ); tr_bencDictAddInt( t, "pex", f[TR_PEER_FROM_PEX] ); tr_bencDictAddInt( t, "tracker", f[TR_PEER_FROM_TRACKER] ); - t = tr_bencDictAddDict( d, "tracker_stat", 7 ); - tr_bencDictAddStr( t, "scrapeResponse", s->scrapeResponse ); + tr_bencDictAddInt( d, "peersGettingFromUs", st->peersGettingFromUs ); + tr_bencDictAddInt( d, "peersKnown", st->peersKnown ); + tr_bencDictAddInt( d, "peersSendingToUs", st->peersSendingToUs ); + tr_bencDictAddDouble( d, "percentComplete", st->percentComplete ); + tr_bencDictAddDouble( d, "percentDone", st->percentDone ); + tr_bencDictAddDouble( d, "rateDownload", st->rateDownload ); + tr_bencDictAddDouble( d, "rateUpload", st->rateUpload ); + tr_bencDictAddDouble( d, "ratio", st->ratio ); + tr_bencDictAddDouble( d, "recheckProgress", st->recheckProgress ); + tr_bencDictAddInt( d, "seeders", st->seeders ); + tr_bencDictAddInt( d, "sizeWhenDone", st->sizeWhenDone ); + tr_bencDictAddInt( d, "startDate", st->startDate ); + tr_bencDictAddInt( d, "status", st->status ); + tr_bencDictAddDouble( d, "swarmSpeed", st->swarmSpeed ); + t = tr_bencDictAddDict( d, "trackerStat", 7 ); tr_bencDictAddStr( t, "announceResponse", s->announceResponse ); - tr_bencDictAddInt( t, "lastScrapeTime", s->lastScrapeTime ); - tr_bencDictAddInt( t, "nextScrapeTime", s->nextScrapeTime ); tr_bencDictAddInt( t, "lastAnnounceTime", s->lastAnnounceTime ); + tr_bencDictAddInt( t, "lastScrapeTime", s->lastScrapeTime ); tr_bencDictAddInt( t, "nextAnnounceTime", s->nextAnnounceTime ); - tr_bencDictAddInt( t, "nextManualAnnounceTime", - s->nextManualAnnounceTime ); + tr_bencDictAddInt( t, "nextScrapeTime", s->nextScrapeTime ); + tr_bencDictAddInt( t, "nextScrapeTime", s->nextScrapeTime ); + tr_bencDictAddStr( t, "scrapeResponse", s->scrapeResponse ); + tr_bencDictAddInt( d, "uploadedEver", st->uploadedEver ); } - /* cleanup */ tr_free( torrents ); return NULL; } @@ -181,15 +227,15 @@ torrentStatus( tr_handle * handle, tr_benc * args_in, tr_benc * args_out ) static void addFiles( const tr_info * info, tr_benc * files ) { - unsigned int i; + tr_file_index_t i; for( i=0; ifileCount; ++i ) { const tr_file * file = &info->files[i]; tr_benc * d = tr_bencListAddDict( files, 4 ); + tr_bencDictAddInt( d, "dnd", file->dnd ); tr_bencDictAddInt( d, "length", file->length ); tr_bencDictAddStr( d, "name", file->name ); tr_bencDictAddInt( d, "priority", file->priority ); - tr_bencDictAddInt( d, "dnd", file->dnd ); } } @@ -201,9 +247,9 @@ addTrackers( const tr_info * info, tr_benc * trackers ) { const tr_tracker_info * t = &info->trackers[i]; tr_benc * d = tr_bencListAddDict( trackers, 3 ); - tr_bencDictAddInt( d, "tier", t->tier ); tr_bencDictAddStr( d, "announce", t->announce ); tr_bencDictAddStr( d, "scrape", t->scrape ); + tr_bencDictAddInt( d, "tier", t->tier ); } } @@ -215,8 +261,9 @@ addInfo( const tr_torrent * tor, tr_benc * d ) tr_bencDictAddStr( d, "comment", inf->comment ? inf->comment : "" ); tr_bencDictAddStr( d, "creator", inf->creator ? inf->creator : "" ); tr_bencDictAddInt( d, "dateCreated", inf->dateCreated ); + addFiles( inf, tr_bencDictAddList( d, "files", inf->fileCount ) ); tr_bencDictAddStr( d, "hashString", inf->hashString ); - tr_bencDictAddInt( d, "id", tor->uniqueId ); + tr_bencDictAddInt( d, "id", tr_torrentId( tor ) ); tr_bencDictAddInt( d, "isMultifile", inf->isMultifile ); tr_bencDictAddInt( d, "isPrivate", inf->isPrivate ); tr_bencDictAddStr( d, "name", inf->name ); @@ -224,7 +271,6 @@ addInfo( const tr_torrent * tor, tr_benc * d ) tr_bencDictAddInt( d, "pieceSize", inf->pieceSize ); tr_bencDictAddStr( d, "torrent", inf->torrent ); tr_bencDictAddInt( d, "totalSize", inf->totalSize ); - addFiles ( inf, tr_bencDictAddList( d, "files", inf->fileCount ) ); addTrackers( inf, tr_bencDictAddList( d, "trackers", inf->trackerCount ) ); } @@ -233,7 +279,7 @@ torrentInfo( tr_handle * handle, tr_benc * args_in, tr_benc * args_out ) { int i, torrentCount; tr_torrent ** torrents = getTorrents( handle, args_in, &torrentCount ); - tr_benc * list = tr_bencDictAddList( args_out, "info", torrentCount ); + tr_benc * list = tr_bencDictAddList( args_out, "torrent-info", torrentCount ); for( i=0; iinfo.name ); + tr_bencDictAddDouble( d, "rateDownload", st->rateDownload ); + tr_bencDictAddDouble( d, "rateUpload", st->rateUpload ); + tr_bencDictAddDouble( d, "ratio", st->ratio ); + tr_bencDictAddInt( d, "status", st->status ); + } + + tr_free( torrents ); + return NULL; +} + +/** +*** +**/ + static const char* torrentGet( tr_handle * handle, tr_benc * args_in, tr_benc * args_out ) { @@ -257,7 +330,7 @@ torrentGet( tr_handle * handle, tr_benc * args_in, tr_benc * args_out ) { tr_torrent * tor = torrents[i]; tr_benc * d = tr_bencListAddDict( list, 6 ); - tr_bencDictAddInt( d, "id", tor->uniqueId ); + tr_bencDictAddInt( d, "id", tr_torrentId( tor ) ); tr_bencDictAddInt( d, "peer-limit", tr_torrentGetPeerLimit( tor ) ); tr_bencDictAddInt( d, "speed-limit-down", @@ -277,10 +350,10 @@ torrentGet( tr_handle * handle, tr_benc * args_in, tr_benc * args_out ) } static const char* -torrentSet( tr_handle * handle, tr_benc * args_in, tr_benc * args_out UNUSED ) +torrentSet( tr_handle * h, tr_benc * args_in, tr_benc * args_out UNUSED ) { int i, torrentCount; - tr_torrent ** torrents = getTorrents( handle, args_in, &torrentCount ); + tr_torrent ** torrents = getTorrents( h, args_in, &torrentCount ); for( i=0; iuniqueId ); - buildFileList( tor, d, "download", testFileDownload ); - buildFileList( tor, d, "no-download", testFileDND ); + tr_bencDictAddInt( d, "id", tr_torrentId( tor ) ); + buildFileList( tor, d, "files-unwanted", testFileDND ); + buildFileList( tor, d, "files-wanted", testFileDownload ); buildFileList( tor, d, "priority-low", testFileLow ); buildFileList( tor, d, "priority-normal", testFileNormal ); buildFileList( tor, d, "priority-high", testFileHigh ); @@ -387,7 +462,7 @@ setFilePriorities( tr_torrent * tor, int priority, tr_benc * list ) tr_file_index_t * files = tr_new0( tr_file_index_t, n ); for( i=0; i %s <--\n", request ); - /* possibly convert o-rison to rison */ - if( request && ( *request != '(' ) ) - { - const int n = strlen( request ); - char * tmp = tr_new( char, n + 3 ); - tmp[0] = '('; - memcpy( tmp+1, request, n ); - tmp[n+1] = ')'; - tmp[n+2] = '\0'; - tr_free( request ); - request = tmp; - } -fprintf( stderr, "after o-rison conversion: --> %s <--\n", request ); - - /* convert rison to json */ + tr_benc top, * args; + char * request = tr_strndup( request_uri, request_len ); + const char * pch; + + tr_bencInitDict( &top, 3 ); + args = tr_bencDictAddDict( &top, "arguments", 0 ); + + /* munge the URI into a usable form. + * we have very loose typing on this to make the URIs as simple as possible: + * - anything not a 'tag' or 'method' is automatically in 'arguments' + * - values that are all-digits are numbers + * - values that are all-digits or commas are number lists + * - all other values are strings + */ + + pch = strchr( request, '?' ); + if( !pch ) pch = request; + while( pch ) { - char * tmp = tr_rison2json( request, strlen( request ) ); - tr_free( request ); - request = tmp; + const char * delim = strchr( pch, '=' ); + const char * next = strchr( pch, '&' ); + if( delim ) + { + int isNum = 1; + int isNumList = 1; + const char * walk; + char * key = tr_strndup( pch, delim-pch ); + int isArg = strcmp( key, "method" ) && strcmp( key, "tag" ); + tr_benc * parent = isArg ? args : ⊤ + char * val = next ? tr_strndup( delim+1, next-(delim+1) ) + : tr_strdup( delim+1 ); + for( walk=val; *walk && ( isNumList || isNum ); ++walk ) { + if( isNumList ) isNumList = *walk=='-' || isdigit(*walk) || *walk==','; + if( isNum ) isNum = *walk=='-' || isdigit(*walk); + } + if( isNum ) + tr_bencDictAddInt( parent, key, strtol( val, NULL, 10 ) ); + else if( !isNumList ) + tr_bencDictAddStr( parent, key, val ); + else { + tr_benc * numList = tr_bencDictAddList( parent, key, 10 ); + walk = val; + for( ;; ) { + char * end; + tr_bencListAddInt( numList, strtol( walk, &end, 10 ) ); + if( *end!=',' ) + break; + walk = end + 1; + } + } + } + pch = next ? next+1 : NULL; } -fprintf( stderr, "after json conversion: --> %s <--\n", request ); - /* parse the json */ - have_content = !tr_jsonParse( request, request+strlen(request), &top, NULL ); - if( have_content ) - { - /* for convenience' sake, our URI rpc notation allows the - * `args' object to be declared implicitly... */ - tr_benc tmp, *args; - int64_t i; - const char * str; - tr_bencInitDict( &tmp, 3 ); - if( tr_bencDictFindInt( &top, "tag", &i ) ) - tr_bencDictAddInt( &tmp, "tag", i ); - if( tr_bencDictFindStr( &top, "method", &str ) ) - tr_bencDictAddStr( &tmp, "method", str ); - args = tr_bencDictAdd( &tmp, "args" ); - *args = top; -tr_bencPrint( &tmp); - ret = request_exec( handle, &tmp, response_len ); - tr_bencInitInt( args, 0 ); - tr_bencFree( &tmp ); - tr_bencFree( &top ); - } + ret = request_exec( handle, &top, response_len ); + /* cleanup */ + tr_bencFree( &top ); tr_free( request ); return ret; } diff --git a/libtransmission/rpc.h b/libtransmission/rpc.h index bff5bfe39..2172418b4 100644 --- a/libtransmission/rpc.h +++ b/libtransmission/rpc.h @@ -13,6 +13,10 @@ #ifndef TR_RPC_H #define TR_RPC_H +/*** +**** RPC processing +***/ + struct tr_handle; /* http://www.json.org/ */ @@ -22,11 +26,11 @@ tr_rpc_request_exec_json( struct tr_handle * handle, int request_len, int * response_len ); -/* http://mjtemplate.org/examples/rison.html */ +/* see the RPC spec's "Request URI Notation" section */ char* -tr_rpc_request_exec_rison( struct tr_handle * handle, - const void * request_rison, - int request_len, - int * response_len ); +tr_rpc_request_exec_uri( struct tr_handle * handle, + const void * request_uri, + int request_len, + int * response_len ); #endif diff --git a/libtransmission/session.c b/libtransmission/session.c index 3783669fb..acf7f3380 100644 --- a/libtransmission/session.c +++ b/libtransmission/session.c @@ -42,6 +42,7 @@ #include "platform.h" /* tr_lock */ #include "port-forwarding.h" #include "ratecontrol.h" +#include "rpc-server.h" #include "stats.h" #include "torrent.h" #include "tracker.h" @@ -118,21 +119,25 @@ tr_sessionSetEncryption( tr_session * session, tr_encryption_mode mode ) static void metainfoLookupRescan( tr_handle * h ); tr_handle * -tr_initFull( const char * configDir, - const char * tag, - int isPexEnabled, - int isPortForwardingEnabled, - int publicPort, - int encryptionMode, - int isUploadLimitEnabled, - int uploadLimit, - int isDownloadLimitEnabled, - int downloadLimit, - int globalPeerLimit, - int messageLevel, - int isMessageQueueingEnabled, - int isBlocklistEnabled, - int peerSocketTOS ) +tr_sessionInitFull( const char * configDir, + const char * downloadDir, + const char * tag, + int isPexEnabled, + int isPortForwardingEnabled, + int publicPort, + int encryptionMode, + int isUploadLimitEnabled, + int uploadLimit, + int isDownloadLimitEnabled, + int downloadLimit, + int globalPeerLimit, + int messageLevel, + int isMessageQueueingEnabled, + int isBlocklistEnabled, + int peerSocketTOS, + int rpcIsEnabled, + int rpcPort, + const char * rpcACL ) { tr_handle * h; char filename[MAX_PATH_LENGTH]; @@ -154,6 +159,7 @@ tr_initFull( const char * configDir, h->isPexEnabled = isPexEnabled ? 1 : 0; h->encryptionMode = encryptionMode; h->peerSocketTOS = peerSocketTOS; + h->downloadDir = tr_strdup( downloadDir ); tr_setConfigDir( h, configDir ); @@ -193,6 +199,7 @@ tr_initFull( const char * configDir, tr_statsInit( h ); h->web = tr_webInit( h ); + h->rpcServer = tr_rpcInit( h, rpcIsEnabled, rpcPort, rpcACL ); metainfoLookupRescan( h ); @@ -200,24 +207,49 @@ tr_initFull( const char * configDir, } tr_handle * -tr_init( const char * configDir, - const char * tag ) -{ - return tr_initFull( configDir, - tag, - TR_DEFAULT_PEX_ENABLED, - TR_DEFAULT_PORT_FORWARDING_ENABLED, - -1, /* public port */ - TR_ENCRYPTION_PREFERRED, /* encryption mode */ - FALSE, /* use upload speed limit? */ - -1, /* upload speed limit */ - FALSE, /* use download speed limit? */ - -1, /* download speed limit */ - TR_DEFAULT_GLOBAL_PEER_LIMIT, - TR_MSG_INF, /* message level */ - FALSE, /* is message queueing enabled? */ - FALSE, /* is the blocklist enabled? */ - TR_DEFAULT_PEER_SOCKET_TOS ); +tr_sessionInit( const char * configDir, + const char * downloadDir, + const char * tag ) +{ + return tr_sessionInitFull( configDir, + downloadDir, + tag, + TR_DEFAULT_PEX_ENABLED, + TR_DEFAULT_PORT_FORWARDING_ENABLED, + -1, /* public port */ + TR_ENCRYPTION_PREFERRED, /* encryption mode */ + FALSE, /* use upload speed limit? */ + -1, /* upload speed limit */ + FALSE, /* use download speed limit? */ + -1, /* download speed limit */ + TR_DEFAULT_GLOBAL_PEER_LIMIT, + TR_MSG_INF, /* message level */ + FALSE, /* is message queueing enabled? */ + FALSE, /* is the blocklist enabled? */ + TR_DEFAULT_PEER_SOCKET_TOS, + TR_DEFAULT_RPC_ENABLED, + TR_DEFAULT_RPC_PORT, + TR_DEFAULT_RPC_ACL ); +} + +/*** +**** +***/ + +void +tr_sessionSetDownloadDir( tr_handle * handle, const char * dir ) +{ + if( handle->downloadDir != dir ) + { + tr_free( handle->downloadDir ); + handle->downloadDir = tr_strdup( dir ); + } +} + +const char * +tr_sessionGetDownloadDir( const tr_handle * handle ) +{ + return handle->downloadDir; } /*** @@ -390,6 +422,7 @@ tr_closeAllConnections( void * vh ) tr_sharedShuttingDown( h->shared ); tr_trackerShuttingDown( h ); + tr_rpcClose( &h->rpcServer ); while(( tor = tr_torrentNext( h, NULL ))) tr_torrentClose( tor ); @@ -411,7 +444,7 @@ deadlineReached( const uint64_t deadline ) #define SHUTDOWN_MAX_SECONDS 30 void -tr_close( tr_handle * h ) +tr_sessionClose( tr_handle * h ) { int i; const int maxwait_msec = SHUTDOWN_MAX_SECONDS * 1000; @@ -445,9 +478,9 @@ tr_close( tr_handle * h ) } tr_torrent ** -tr_loadTorrents ( tr_handle * h, - tr_ctor * ctor, - int * setmeCount ) +tr_sessionLoadTorrents ( tr_handle * h, + tr_ctor * ctor, + int * setmeCount ) { int i, n = 0; struct stat sb; @@ -491,7 +524,8 @@ tr_loadTorrents ( tr_handle * h, if( n ) tr_inf( _( "Loaded %d torrents" ), n ); - *setmeCount = n; + if( setmeCount ) + *setmeCount = n; return torrents; } @@ -701,3 +735,48 @@ tr_torrentNext( tr_handle * session, tr_torrent * tor ) { return tor ? tor->next : session->torrentList; } + +/*** +**** +***/ + +void +tr_sessionSetRPCEnabled( tr_handle * session, int isEnabled ) +{ + tr_rpcSetEnabled( session->rpcServer, isEnabled ); +} +int +tr_sessionIsRPCEnabled( const tr_handle * session ) +{ + return tr_rpcIsEnabled( session->rpcServer ); +} +void +tr_sessionSetRPCPort( tr_handle * session, int port ) +{ + tr_rpcSetPort( session->rpcServer, port ); +} +int +tr_sessionGetRPCPort( const tr_handle * session ) +{ + return tr_rpcGetPort( session->rpcServer ); +} +void +tr_sessionSetRPCCallback( tr_handle * session, + tr_rpc_func func, + void * user_data ) +{ + session->rpc_func = func; + session->rpc_func_user_data = user_data; +} + +void +tr_sessionSetRPCACL( tr_handle * session, const char * acl ) +{ + tr_rpcSetACL( session->rpcServer, acl ); +} + +const char* +tr_sessionGetRPCACL( const tr_session * session ) +{ + return tr_rpcGetACL( session->rpcServer ); +} diff --git a/libtransmission/session.h b/libtransmission/session.h index c053b5962..31ecbf489 100644 --- a/libtransmission/session.h +++ b/libtransmission/session.h @@ -25,23 +25,19 @@ #ifndef TR_INTERNAL_H #define TR_INTERNAL_H 1 +#include + #define TR_NAME "Transmission" +#ifndef UNUSED #ifdef __GNUC__ #define UNUSED __attribute__((unused)) #else #define UNUSED #endif - -typedef enum { TR_NET_OK, TR_NET_ERROR, TR_NET_WAIT } tr_tristate_t; - -#ifndef TRUE -#define TRUE 1 #endif -#ifndef FALSE -#define FALSE 0 -#endif +typedef enum { TR_NET_OK, TR_NET_ERROR, TR_NET_WAIT } tr_tristate_t; uint8_t* tr_peerIdNew( void ); @@ -61,6 +57,9 @@ struct tr_handle unsigned int useUploadLimit : 1; unsigned int useDownloadLimit : 1; + tr_rpc_func rpc_func; + void * rpc_func_user_data; + tr_encryption_mode encryptionMode; struct tr_event_handle * events; @@ -73,8 +72,9 @@ struct tr_handle char * tag; char * configDir; - char * torrentDir; + char * downloadDir; char * resumeDir; + char * torrentDir; struct tr_ratecontrol * upload; struct tr_ratecontrol * download; @@ -87,6 +87,8 @@ struct tr_handle struct tr_web * web; + struct tr_rpc_server * rpcServer; + tr_handle_status stats[2]; int statCur; diff --git a/libtransmission/stats.c b/libtransmission/stats.c index a4400b154..d331efaa9 100644 --- a/libtransmission/stats.c +++ b/libtransmission/stats.c @@ -12,7 +12,7 @@ #include "transmission.h" #include "bencode.h" -#include "platform.h" /* tr_getConfigDir() */ +#include "platform.h" /* tr_sessionGetConfigDir() */ #include "utils.h" /* tr_buildPath */ /*** @@ -29,7 +29,9 @@ struct tr_stats_handle static char* getFilename( const tr_handle * handle, char * buf, size_t buflen ) { - tr_buildPath( buf, buflen, tr_getConfigDir(handle), "stats.benc", NULL ); + tr_buildPath( buf, buflen, tr_sessionGetConfigDir(handle), + "stats.benc", + NULL ); return buf; } diff --git a/libtransmission/torrent-ctor.c b/libtransmission/torrent-ctor.c index 44445a39e..250c24419 100644 --- a/libtransmission/torrent-ctor.c +++ b/libtransmission/torrent-ctor.c @@ -15,7 +15,6 @@ #include "bencode.h" #include "platform.h" #include "session.h" /* tr_sessionFindTorrentFile */ -#include "trcompat.h" /* strlcpy */ #include "utils.h" #define DEFAULT_MAX_CONNECTED_PEERS 50 @@ -24,11 +23,11 @@ struct optional_args { unsigned int isSet_paused : 1; unsigned int isSet_connected : 1; - unsigned int isSet_destination : 1; + unsigned int isSet_downloadDir : 1; unsigned int isPaused : 1; uint16_t peerLimit; - char destination[MAX_PATH_LENGTH]; + char downloadDir[MAX_PATH_LENGTH]; }; struct tr_ctor @@ -201,13 +200,13 @@ tr_ctorSetPeerLimit( tr_ctor * ctor, } void -tr_ctorSetDestination( tr_ctor * ctor, +tr_ctorSetDownloadDir( tr_ctor * ctor, tr_ctorMode mode, const char * directory ) { struct optional_args * args = &ctor->optionalArgs[mode]; - args->isSet_destination = 1; - strlcpy( args->destination, directory, sizeof( args->destination ) ); + args->isSet_downloadDir = 1; + tr_strlcpy( args->downloadDir, directory, sizeof( args->downloadDir ) ); } int @@ -243,17 +242,17 @@ tr_ctorGetPaused( const tr_ctor * ctor, } int -tr_ctorGetDestination( const tr_ctor * ctor, +tr_ctorGetDownloadDir( const tr_ctor * ctor, tr_ctorMode mode, - const char ** setmeDestination ) + const char ** setmeDownloadDir ) { int err = 0; const struct optional_args * args = &ctor->optionalArgs[mode]; - if( !args->isSet_destination ) + if( !args->isSet_downloadDir ) err = 1; - else if( setmeDestination ) - *setmeDestination = args->destination; + else if( setmeDownloadDir ) + *setmeDownloadDir = args->downloadDir; return err; } @@ -283,6 +282,7 @@ tr_ctorNew( const tr_handle * handle ) ctor->handle = handle; tr_ctorSetPeerLimit( ctor, TR_FALLBACK, DEFAULT_MAX_CONNECTED_PEERS ); tr_ctorSetPaused( ctor, TR_FALLBACK, FALSE ); + tr_ctorSetDownloadDir( ctor, TR_FALLBACK, handle->downloadDir ); tr_ctorSetSave( ctor, TRUE ); return ctor; } diff --git a/libtransmission/torrent.c b/libtransmission/torrent.c index d338c1f67..d437b9e5c 100644 --- a/libtransmission/torrent.c +++ b/libtransmission/torrent.c @@ -42,7 +42,6 @@ #include "ratecontrol.h" #include "torrent.h" #include "tracker.h" -#include "trcompat.h" /* for strlcpy */ #include "trevent.h" #include "utils.h" #include "verify.h" @@ -53,6 +52,12 @@ **** ***/ +int +tr_torrentId( const tr_torrent * tor ) +{ + return tor->uniqueId; +} + tr_torrent* tr_torrentFindFromId( tr_handle * handle, int id ) { @@ -202,13 +207,13 @@ onTrackerResponse( void * tracker UNUSED, void * vevent, void * user_data ) case TR_TRACKER_WARNING: tr_torerr( tor, _( "Tracker warning: \"%s\"" ), event->text ); tor->error = TR_ERROR_TC_WARNING; - strlcpy( tor->errorString, event->text, sizeof(tor->errorString) ); + tr_strlcpy( tor->errorString, event->text, sizeof(tor->errorString) ); break; case TR_TRACKER_ERROR: tr_torerr( tor, _( "Tracker error: \"%s\"" ), event->text ); tor->error = TR_ERROR_TC_ERROR; - strlcpy( tor->errorString, event->text, sizeof(tor->errorString) ); + tr_strlcpy( tor->errorString, event->text, sizeof(tor->errorString) ); break; case TR_TRACKER_ERROR_CLEAR: @@ -450,7 +455,7 @@ torrentRealInit( tr_handle * h, tor->upload = tr_rcInit(); tor->download = tr_rcInit(); - tor->swarmspeed = tr_rcInit(); + tor->swarmSpeed = tr_rcInit(); tr_sha1( tor->obfuscatedHash, "req2", 4, info->hash, SHA_DIGEST_LENGTH, @@ -485,9 +490,17 @@ torrentRealInit( tr_handle * h, tor->tracker = tr_trackerNew( tor ); tor->trackerSubscription = tr_trackerSubscribe( tor->tracker, onTrackerResponse, tor ); - tor->next = h->torrentList; - h->torrentList = tor; - h->torrentCount++; + { + tr_torrent * it = NULL; + tr_torrent * last = NULL; + while(( it = tr_torrentNext( h, it ))) + last = it; + if( !last ) + h->torrentList = tor; + else + last->next = tor; + ++h->torrentCount; + } tr_globalUnlock( h ); @@ -562,20 +575,20 @@ tr_torrentNew( tr_handle * handle, **/ void -tr_torrentSetFolder( tr_torrent * tor, const char * path ) +tr_torrentSetDownloadDir( tr_torrent * tor, const char * path ) { - if( !path || !tor->destination || strcmp( path, tor->destination ) ) + if( !path || !tor->downloadDir || strcmp( path, tor->downloadDir ) ) { - tr_free( tor->destination ); - tor->destination = tr_strdup( path ); + tr_free( tor->downloadDir ); + tor->downloadDir = tr_strdup( path ); tr_torrentSaveResume( tor ); } } const char* -tr_torrentGetFolder( const tr_torrent * tor ) +tr_torrentGetDownloadDir( const tr_torrent * tor ) { - return tor->destination; + return tor->downloadDir; } void @@ -690,7 +703,7 @@ tr_torrentStat( tr_torrent * tor ) tc = tor->tracker; s->tracker = tr_trackerGetAddress( tor->tracker ); - tr_trackerStat( tor->tracker, &s->tracker_stat ); + tr_trackerStat( tor->tracker, &s->trackerStat ); tr_peerMgrTorrentStats( tor->handle->peerMgr, tor->info.hash, @@ -718,7 +731,7 @@ tr_torrentStat( tr_torrent * tor ) &s->leechers, &s->seeders ); - s->swarmspeed = tr_rcRate( tor->swarmspeed ); + s->swarmSpeed = tr_rcRate( tor->swarmSpeed ); s->startDate = tor->startDate; s->activityDate = tor->activityDate; @@ -929,7 +942,7 @@ freeTorrent( tr_torrent * tor ) tr_rcClose( tor->upload ); tr_rcClose( tor->download ); - tr_rcClose( tor->swarmspeed ); + tr_rcClose( tor->swarmSpeed ); tr_trackerUnsubscribe( tor->tracker, tor->trackerSubscription ); tr_trackerFree( tor->tracker ); @@ -937,7 +950,7 @@ freeTorrent( tr_torrent * tor ) tr_bitfieldFree( tor->checkedPieces ); - tr_free( tor->destination ); + tr_free( tor->downloadDir ); if( tor == h->torrentList ) h->torrentList = tor->next; @@ -951,8 +964,6 @@ freeTorrent( tr_torrent * tor ) assert( h->torrentCount >= 1 ); h->torrentCount--; - tr_torinf( tor, _( "Closing torrent; %d left" ), h->torrentCount ); - tr_metainfoFree( inf ); tr_free( tor ); @@ -1076,7 +1087,7 @@ stopTorrent( void * vtor ) { char path[MAX_PATH_LENGTH]; const tr_file * file = &tor->info.files[i]; - tr_buildPath( path, sizeof(path), tor->destination, file->name, NULL ); + tr_buildPath( path, sizeof(path), tor->downloadDir, file->name, NULL ); tr_fdFileClose( path ); } } @@ -1517,7 +1528,7 @@ tr_torrentGetMTimes( const tr_torrent * tor, int * setme_n ) char fname[MAX_PATH_LENGTH]; struct stat sb; tr_buildPath( fname, sizeof(fname), - tor->destination, tor->info.files[i].name, NULL ); + tor->downloadDir, tor->info.files[i].name, NULL ); if ( !stat( fname, &sb ) ) { #ifdef SYS_DARWIN m[i] = sb.st_mtimespec.tv_sec; diff --git a/libtransmission/torrent.h b/libtransmission/torrent.h index c862c62aa..4f9d516c4 100644 --- a/libtransmission/torrent.h +++ b/libtransmission/torrent.h @@ -124,7 +124,7 @@ struct tr_torrent tr_speedlimit downloadLimitMode; struct tr_ratecontrol * upload; struct tr_ratecontrol * download; - struct tr_ratecontrol * swarmspeed; + struct tr_ratecontrol * swarmSpeed; int error; char errorString[128]; @@ -132,7 +132,7 @@ struct tr_torrent uint8_t obfuscatedHash[SHA_DIGEST_LENGTH]; /* Where to download */ - char * destination; + char * downloadDir; /* How many bytes we ask for per request */ uint32_t blockSize; diff --git a/libtransmission/tracker.c b/libtransmission/tracker.c index 5057ac6c4..e642403ad 100644 --- a/libtransmission/tracker.c +++ b/libtransmission/tracker.c @@ -25,7 +25,6 @@ #include "publish.h" #include "torrent.h" #include "tracker.h" -#include "trcompat.h" /* strlcpy */ #include "trevent.h" #include "utils.h" #include "web.h" @@ -312,8 +311,8 @@ onTrackerResponse( tr_session * session, if( !t ) /* tracker's been closed */ return; - dbgmsg( t->name, "tracker response: %d", responseCode ); - tr_ndbg( t->name, "tracker response: %d", responseCode ); + dbgmsg( t->name, "tracker response: %ld", responseCode ); + tr_ndbg( t->name, "tracker response: %ld", responseCode ); t->lastAnnounceResponse = responseCode; if( responseCode == HTTP_OK ) @@ -452,7 +451,7 @@ onScrapeResponse( tr_session * session, return; dbgmsg( t->name, "scrape response: %ld\n", responseCode ); - tr_ndbg( t->name, "scrape response: %d", responseCode ); + tr_ndbg( t->name, "scrape response: %ld", responseCode ); t->lastScrapeResponse = responseCode; if( responseCode == HTTP_OK ) @@ -525,7 +524,7 @@ onScrapeResponse( tr_session * session, else { const int interval = t->retryScrapeIntervalSec + t->randOffset; - dbgmsg( t->name, "Tracker responded to scrape with %d. Retrying in %d seconds.", + dbgmsg( t->name, "Tracker responded to scrape with %ld. Retrying in %d seconds.", responseCode, interval ); t->retryScrapeIntervalSec *= 2; t->scrapeAt = time( NULL ) + interval; diff --git a/libtransmission/transmission.h b/libtransmission/transmission.h index d197b513e..f72c82614 100644 --- a/libtransmission/transmission.h +++ b/libtransmission/transmission.h @@ -62,12 +62,12 @@ enum }; /*********************************************************************** - * tr_init + * tr_sessionInit *********************************************************************** * Initializes and returns an opaque libtransmission handle * to be passed to functions below. The tag argument is a short string - * unique to the program invoking tr_init(), it is currently used as - * part of saved torrent files' names to prevent one frontend from + * unique to the program invoking tr_sessionInit(), it is currently used + * as part of saved torrent files' names to prevent one frontend from * deleting a torrent used by another. The following tags are used: * beos cli daemon gtk macosx wx **********************************************************************/ @@ -82,33 +82,95 @@ const char* tr_getDefaultConfigDir( void ); #define TR_DEFAULT_PORT 51413 #define TR_DEFAULT_GLOBAL_PEER_LIMIT 200 #define TR_DEFAULT_PEER_SOCKET_TOS 8 - -tr_handle * tr_initFull( const char * configDir, - const char * tag, - int isPexEnabled, - int isPortForwardingEnabled, - int publicPort, - int encryptionMode, - int isUploadLimitEnabled, - int uploadLimit, - int isDownloadLimitEnabled, - int downloadLimit, - int globalPeerLimit, - int messageLevel, - int isMessageQueueingEnabled, - int isBlocklistEnabled, - int peerSocketTOS ); +#define TR_DEFAULT_BLOCKLIST_ENABLED 0 +#define TR_DEFAULT_RPC_ENABLED 0 +#define TR_DEFAULT_RPC_PORT 9091 +#define TR_DEFAULT_RPC_PORT_STR "9091" +#define TR_DEFAULT_RPC_ACL "+127.0.0.1" + +tr_handle * tr_sessionInitFull( const char * configDir, + const char * downloadDir, + const char * tag, + int isPexEnabled, + int isPortForwardingEnabled, + int publicPort, + int encryptionMode, + int isUploadLimitEnabled, + int uploadLimit, + int isDownloadLimitEnabled, + int downloadLimit, + int globalPeerLimit, + int messageLevel, + int isMessageQueueingEnabled, + int isBlocklistEnabled, + int peerSocketTOS, + int rpcIsEnabled, + int rpcPort, + const char * rpcACL ); /** - * Like tr_initFull() but with default values supplied. + * Like tr_sessionInitFull() but with default values supplied. */ -tr_handle * tr_init( const char * configDir, - const char * tag ); +tr_handle * tr_sessionInit( const char * configDir, + const char * downloadDir, + const char * tag ); /** - * Shut down a libtransmission instance created by tr_init*() + * Shut down a libtransmission instance created by tr_sessionInit*() + */ +void tr_sessionClose( tr_handle * ); + +void tr_sessionSetDownloadDir( tr_handle *, const char * downloadDir ); + +const char * tr_sessionGetDownloadDir( const tr_handle * ); + +void tr_sessionSetRPCEnabled( tr_handle *, int isEnabled ); + +int tr_sessionIsRPCEnabled( const tr_handle * handle ); + +void tr_sessionSetRPCPort( tr_handle *, int port ); + +int tr_sessionGetRPCPort( const tr_handle * ); + +/** + * Specify access control list (ACL). ACL is a comma separated list + * of IP subnets, each subnet is prepended by ’-’ or ’+’ sign. Plus + * means allow, minus means deny. If subnet mask is omitted, like + * "-1.2.3.4", then it means single IP address. Mask may vary from 0 + * to 32 inclusive. + * + * The default string is "+127.0.0.1" + * + * IMPORTANT: a malformed ACL is likely to cause Transmission to crash. + * Client applications need to validate user input, or better yet + * generate it from a higher-level interface that doesn't allow user error, + * before calling this function. */ -void tr_close( tr_handle * ); +void tr_sessionSetRPCACL( tr_handle *, const char * acl ); + +const char* tr_sessionGetRPCACL( const tr_handle * ); + +typedef enum +{ + TR_RPC_TORRENT_ADDED, + TR_RPC_TORRENT_STARTED, + TR_RPC_TORRENT_STOPPED, + TR_RPC_TORRENT_CLOSING, + TR_RPC_TORRENT_CHANGED, + TR_RPC_SESSION_CHANGED +} +tr_rpc_callback_type; + +struct tr_torrent; + +typedef void ( *tr_rpc_func )( tr_handle * handle, + tr_rpc_callback_type type, + struct tr_torrent * tor_or_null, + void * user_data ); + +void tr_sessionSetRPCCallback( tr_handle * handle, + tr_rpc_func func, + void * user_data ); /** @@ -159,7 +221,7 @@ void tr_sessionSetEncryption( tr_handle * handle, tr_encryption_mode mode ); * Returns the full path to a directory which can be used to store * preferences. The string belongs to libtransmission, do not free it. **********************************************************************/ -const char * tr_getConfigDir( const tr_handle * ); +const char * tr_sessionGetConfigDir( const tr_handle * ); @@ -459,7 +521,7 @@ void tr_ctorSetPeerLimit ( tr_ctor * ctor, tr_ctorMode mode, uint16_t peerLimit ); -void tr_ctorSetDestination ( tr_ctor * ctor, +void tr_ctorSetDownloadDir ( tr_ctor * ctor, tr_ctorMode mode, const char * directory ); @@ -475,9 +537,9 @@ int tr_ctorGetPaused ( const tr_ctor * ctor, tr_ctorMode mode, uint8_t * setmeIsPaused ); -int tr_ctorGetDestination ( const tr_ctor * ctor, +int tr_ctorGetDownloadDir ( const tr_ctor * ctor, tr_ctorMode mode, - const char ** setmeDestination ); + const char ** setmeDownloadDir ); int tr_ctorGetMetainfo ( const tr_ctor * ctor, const struct tr_benc ** setme ); @@ -490,6 +552,12 @@ int tr_ctorGetDeleteSource ( const tr_ctor * ctor, /* returns NULL if tr_ctorSetMetainfoFromFile() wasn't used */ const char* tr_ctorGetSourceFile ( const tr_ctor * ctor ); +/** + * Returns this torrent's unique ID. + * IDs are allocated when the torrent is constructed and are + * good until tr_sessionClose() is called. + */ +int tr_torrentId( const tr_torrent * ); typedef struct tr_info tr_info; @@ -498,7 +566,7 @@ typedef struct tr_info tr_info; * Returns TR_OK if it parsed and can be added to Transmission. * Returns TR_EINVALID if it couldn't be parsed. * Returns TR_EDUPLICATE if it parsed but can't be added. - * "destination" must be set to test for TR_EDUPLICATE. + * "download-dir" must be set to test for TR_EDUPLICATE. * * If setme_info is non-NULL and parsing is successful * (that is, if TR_EINVALID is not returned), then the parsed @@ -524,9 +592,9 @@ tr_torrent * tr_torrentNew( tr_handle * handle, * This can be used at startup to kickstart all the torrents * from the previous session. */ -tr_torrent ** tr_loadTorrents ( tr_handle * h, - tr_ctor * ctor, - int * setmeCount ); +tr_torrent ** tr_sessionLoadTorrents ( tr_handle * h, + tr_ctor * ctor, + int * setmeCount ); @@ -541,9 +609,9 @@ int tr_sessionIsPexEnabled( const tr_handle * ); const tr_info * tr_torrentInfo( const tr_torrent * ); -void tr_torrentSetFolder( tr_torrent *, const char * ); +void tr_torrentSetDownloadDir( tr_torrent *, const char * ); -const char * tr_torrentGetFolder( const tr_torrent * ); +const char * tr_torrentGetDownloadDir( const tr_torrent * ); void tr_torrentStart( tr_torrent * ); @@ -839,7 +907,7 @@ struct tr_stat { tr_torrent_status status; - struct tr_tracker_stat tracker_stat; + struct tr_tracker_stat trackerStat; const tr_tracker_info * tracker; tr_errno error; @@ -913,7 +981,7 @@ struct tr_stat * moved to `corrupt' or `haveValid'. */ uint64_t haveUnchecked; - float swarmspeed; + float swarmSpeed; #define TR_RATIO_NA -1 #define TR_RATIO_INF -2 diff --git a/libtransmission/trcompat.h b/libtransmission/trcompat.h deleted file mode 100644 index 520f70b38..000000000 --- a/libtransmission/trcompat.h +++ /dev/null @@ -1,34 +0,0 @@ -/****************************************************************************** - * $Id$ - * - * Copyright (c) 2005-2008 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. - *****************************************************************************/ -#ifndef TRCOMPAT_H -#define TRCOMPAT_H - -#include -#include /* for size_t */ - -#ifndef HAVE_STRLCPY -size_t strlcpy(char *dst, const char *src, size_t siz); -#endif - -#endif /* TRCOMPAT_H */ diff --git a/libtransmission/utils.c b/libtransmission/utils.c index 4c4e1e862..f5bfa2f3a 100644 --- a/libtransmission/utils.c +++ b/libtransmission/utils.c @@ -45,7 +45,6 @@ #endif #include "transmission.h" -#include "trcompat.h" #include "utils.h" #include "platform.h" @@ -506,7 +505,7 @@ tr_buildPath ( char *buf, size_t buflen, const char *first_element, ... ) element = (const char*) va_arg( vl, const char* ); } if( EVBUFFER_LENGTH(evbuf) ) - strlcpy( buf, (char*)EVBUFFER_DATA(evbuf), buflen ); + tr_strlcpy( buf, (char*)EVBUFFER_DATA(evbuf), buflen ); else *buf = '\0'; evbuffer_free( evbuf ); @@ -877,45 +876,44 @@ tr_wait( uint64_t delay_milliseconds ) **** ***/ - -#ifndef HAVE_STRLCPY - /* * Copy src to string dst of size siz. At most siz-1 characters * will be copied. Always NUL terminates (unless siz == 0). * Returns strlen(src); if retval >= siz, truncation occurred. */ size_t -strlcpy(char *dst, const char *src, size_t siz) +tr_strlcpy(char *dst, const char *src, size_t siz) { - char *d = dst; - const char *s = src; - size_t n = siz; - - assert( s != NULL ); - assert( d != NULL ); - - /* Copy as many bytes as will fit */ - if (n != 0) { - while (--n != 0) { - if ((*d++ = *s++) == '\0') - break; - } - } +#ifdef HAVE_STRLCPY + return strlcpy( dst, src, siz ); +#else + char *d = dst; + const char *s = src; + size_t n = siz; + + assert( s != NULL ); + assert( d != NULL ); + + /* Copy as many bytes as will fit */ + if (n != 0) { + while (--n != 0) { + if ((*d++ = *s++) == '\0') + break; + } + } - /* Not enough room in dst, add NUL and traverse rest of src */ - if (n == 0) { - if (siz != 0) - *d = '\0'; /* NUL-terminate dst */ - while (*s++) - ; - } + /* Not enough room in dst, add NUL and traverse rest of src */ + if (n == 0) { + if (siz != 0) + *d = '\0'; /* NUL-terminate dst */ + while (*s++) + ; + } - return(s - src - 1); /* count does not include NUL */ + return(s - src - 1); /* count does not include NUL */ +#endif } -#endif /* HAVE_STRLCPY */ - /*** **** ***/ diff --git a/libtransmission/utils.h b/libtransmission/utils.h index 5ace211f6..c6597ba71 100644 --- a/libtransmission/utils.h +++ b/libtransmission/utils.h @@ -34,6 +34,14 @@ **** ***/ +#ifndef FALSE +#define FALSE 0 +#endif + +#ifndef TRUE +#define TRUE 1 +#endif + #ifndef UNUSED #ifdef __GNUC__ #define UNUSED __attribute__((unused)) @@ -191,6 +199,9 @@ void tr_free ( void* ); char* tr_strdup( const char * str ) TR_GNUC_MALLOC; char* tr_strndup( const char * str, int len ) TR_GNUC_MALLOC; char* tr_strdup_printf( const char * fmt, ... ) TR_GNUC_PRINTF( 1, 2 ) TR_GNUC_MALLOC; +size_t tr_strlcpy( char * dst, const char * src, size_t siz ); + + const char* tr_strerror( int ); diff --git a/libtransmission/verify.c b/libtransmission/verify.c index a190ad6ab..fef7958a6 100644 --- a/libtransmission/verify.c +++ b/libtransmission/verify.c @@ -68,7 +68,7 @@ checkFile( tr_torrent * tor, char path[MAX_PATH_LENGTH]; const tr_file * file = &tor->info.files[fileIndex]; - tr_buildPath ( path, sizeof(path), tor->destination, file->name, NULL ); + tr_buildPath ( path, sizeof(path), tor->downloadDir, file->name, NULL ); nofile = stat( path, &sb ) || !S_ISREG( sb.st_mode ); for( i=file->firstPiece; i<=file->lastPiece && iinfo.pieceCount && (!*abortFlag); ++i ) diff --git a/po/POTFILES.in b/po/POTFILES.in index fc420c21d..453dc43ca 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -6,7 +6,6 @@ gtk/details.c gtk/dialogs.c gtk/file-list.c gtk/hig.c -gtk/ipc.c gtk/main.c gtk/makemeta-ui.c gtk/msgwin.c diff --git a/third-party/shttpd/config.h b/third-party/shttpd/config.h index 5a77b0e47..ec9e10748 100644 --- a/third-party/shttpd/config.h +++ b/third-party/shttpd/config.h @@ -15,7 +15,7 @@ #define VERSION "1.39" /* Version */ #define CONFIG_FILE "shttpd.conf" /* Configuration file */ #define HTPASSWD ".htpasswd" /* Passwords file name */ -#define URI_MAX 65536 /* Default max request size */ +#define URI_MAX 16384 /* Default max request size */ #define LISTENING_PORTS "80" /* Default listening ports */ #define INDEX_FILES "index.html,index.htm,index.php,index.cgi" #define CGI_EXT "cgi,pl,php" /* Default CGI extensions */ diff --git a/wx/xmission.cc b/wx/xmission.cc index e6573f229..2353bf3bf 100755 --- a/wx/xmission.cc +++ b/wx/xmission.cc @@ -391,7 +391,7 @@ void MyFrame :: OnOpen( wxCommandEvent& WXUNUSED(event) ) bool MyApp :: OnInit( ) { - handle = tr_init( "wx" ); + handle = tr_sessionInit( "wx" ); wxCmdLineParser cmdParser( cmdLineDesc, argc, argv ); if( cmdParser.Parse ( ) ) @@ -700,7 +700,7 @@ MyFrame :: OnExit( wxCommandEvent& WXUNUSED( event ) ) mySelectedTorrents.clear( ); ApplyCurrentFilter( ); - tr_close( handle ); + tr_sessionClose( handle ); /* give the connections a max of 10 seconds to shut themselves down */ myExitTime = time(0) + 10; -- 2.40.0