diff --git a/daemon/daemon.c b/daemon/daemon.c
index 8ee835287..6b68463bd 100644
--- a/daemon/daemon.c
+++ b/daemon/daemon.c
@@ -38,12 +38,18 @@ saveState( tr_handle * h )
{
tr_benc d;
const char * str;
+ char * username = tr_sessionGetRPCUsername( h );
+ char * password = tr_sessionGetRPCPassword( h );
+ char * auth = tr_strdup_printf( "%s:%s", username, password );
- tr_bencInitDict( &d, 12 );
+ tr_bencInitDict( &d, 14 );
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_sessionGetPeerPort( h ) );
+ tr_bencDictAddStr( &d, "auth", auth );
+ tr_bencDictAddInt( &d, "auth-required",
+ tr_sessionIsRPCPasswordEnabled( h ) );
tr_bencDictAddInt( &d, "port-forwarding-enabled",
tr_sessionIsPortForwardingEnabled( h ) );
tr_bencDictAddStr( &d, "rpc-acl", tr_sessionGetRPCACL( h ) );
@@ -67,10 +73,28 @@ saveState( tr_handle * h )
tr_bencSaveFile( myConfigFilename, &d );
tr_bencFree( &d );
+ tr_free( auth );
+ tr_free( password );
+ tr_free( username );
+}
+
+static int
+parseAuth( const char * auth, char ** username, char ** password )
+{
+ int err = 0;
+ const char * pch = strchr( auth, ':' );
+ if( !pch )
+ err = -1;
+ else {
+ *username = tr_strndup( auth, pch-auth );
+ *password = tr_strdup( pch+1 );
+ }
+ return err;
}
static void
-session_init( const char * configDir, int rpc_port, const char * rpc_acl )
+session_init( const char * configDir, int rpc_port,
+ const char * acl, const char * auth, int noauth )
{
tr_benc state;
int have_state;
@@ -84,10 +108,15 @@ session_init( const char * configDir, int rpc_port, const char * rpc_acl )
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;
+ const char * acl_fallback = TR_DEFAULT_RPC_ACL;
int64_t rpc_port_fallback = TR_DEFAULT_RPC_PORT;
+ int64_t auth_required_fallback = 0;
+ const char * auth_fallback = NULL;
tr_ctor * ctor;
tr_torrent ** torrents;
+ int auth_required;
+ char * user = NULL;
+ char * pass = NULL;
if(( have_state = !tr_bencLoadFile( myConfigFilename, &state )))
{
@@ -100,7 +129,9 @@ session_init( const char * configDir, int rpc_port, const char * rpc_acl )
tr_bencDictFindInt( &state, "pex-allowed", &pex_enabled );
tr_bencDictFindInt( &state, "port", &peer_port );
tr_bencDictFindInt( &state, "port-forwarding-enabled", &fwd_enabled );
- tr_bencDictFindStr( &state, "rpc-acl", &rpc_acl_fallback );
+ tr_bencDictFindStr( &state, "rpc-acl", &acl_fallback );
+ tr_bencDictFindStr( &state, "auth", &auth_fallback );
+ tr_bencDictFindInt( &state, "auth-required", &auth_required_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 );
@@ -119,8 +150,23 @@ session_init( const char * configDir, int rpc_port, const char * rpc_acl )
getcwd( downloadDir, sizeof( downloadDir ) );
if( rpc_port < 1 )
rpc_port = rpc_port_fallback;
- if( !rpc_acl || !*rpc_acl )
- rpc_acl = rpc_acl_fallback;
+ if( !acl || !*acl )
+ acl = acl_fallback;
+ if( !auth || !*auth )
+ auth = auth_fallback;
+
+ if( auth && parseAuth( auth, &user, &pass ) ) {
+ tr_nerr( MY_NAME, "Unable to parse authentication string \"%s\"", auth );
+ abort( );
+ }
+
+ if( noauth ) {
+ /* user has explicitly turned off authentication */
+ user = NULL;
+ pass = NULL;
+ }
+
+ auth_required = user || pass;
/* start the session */
mySession = tr_sessionInitFull( configDir, "daemon", downloadDir,
@@ -132,7 +178,11 @@ session_init( const char * configDir, int rpc_port, const char * rpc_acl )
TR_MSG_INF, 0,
FALSE, /* is the blocklist enabled? */
TR_DEFAULT_PEER_SOCKET_TOS,
- TRUE, rpc_port, rpc_acl );
+ TRUE, rpc_port, acl,
+ auth_required, user, pass );
+
+ if( auth_required )
+ tr_ninf( MY_NAME, "requiring authentication" );
/* load the torrents */
ctor = tr_ctorNew( mySession );
@@ -156,6 +206,7 @@ daemonUsage( void )
" -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"
+ " -t --auth : Username and password for authentication\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"
@@ -165,24 +216,31 @@ daemonUsage( void )
static void
readargs( int argc, char ** argv,
- int * nofork, int * port, char ** acl,
- char ** configDir )
+ int * nofork, int * port,
+ char ** configDir,
+ char ** acl,
+ char ** auth,
+ int * noauth )
{
int opt;
- char optstr[] = "a:fg:hp:";
+ char optstr[] = "a:fg:hnp:t:u:w:";
struct option longopts[] = {
{ "acl", required_argument, NULL, 'a' },
{ "foreground", no_argument, NULL, 'f' },
{ "config-dir", required_argument, NULL, 'g' },
{ "help", no_argument, NULL, 'h' },
+ { "noauth", no_argument, NULL, 'n' },
{ "port", required_argument, NULL, 'p' },
+ { "auth", required_argument, NULL, 't' },
{ NULL, 0, NULL, '\0' }
};
while((( opt = getopt_long( argc, argv, optstr, longopts, NULL ))) != -1 ) {
switch( opt ) {
case 'a': *acl = tr_strdup( optarg ); break;
case 'f': *nofork = 1; break;
+ case 'n': *noauth = 1; break;
case 'g': *configDir = tr_strdup( optarg ); break;
+ case 't': *auth = tr_strdup( optarg ); break;
case 'p': *port = atoi( optarg ); break;
default: daemonUsage( ); break;
}
@@ -253,9 +311,11 @@ int
main( int argc, char ** argv )
{
int nofork = 0;
+ int noauth = 0;
int port = TR_DEFAULT_RPC_PORT;
char * configDir = NULL;
char * acl = NULL;
+ char * auth = NULL;
signal( SIGINT, gotsig );
signal( SIGQUIT, gotsig );
@@ -263,7 +323,7 @@ main( int argc, char ** argv )
signal( SIGPIPE, SIG_IGN );
signal( SIGHUP, SIG_IGN );
- readargs( argc, argv, &nofork, &port, &acl, &configDir );
+ readargs( argc, argv, &nofork, &port, &configDir, &acl, &auth, &noauth );
if( configDir == NULL )
configDir = tr_strdup_printf( "%s-daemon", tr_getDefaultConfigDir() );
tr_buildPath( myConfigFilename, sizeof( myConfigFilename ),
@@ -276,7 +336,7 @@ main( int argc, char ** argv )
}
}
- session_init( configDir, port, acl );
+ session_init( configDir, port, acl, auth, noauth );
while( !closing )
sleep( 1 );
diff --git a/daemon/remote.c b/daemon/remote.c
index c9131c0a7..a4bbe900d 100644
--- a/daemon/remote.c
+++ b/daemon/remote.c
@@ -65,6 +65,7 @@ showUsage( void )
" -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"
+ " -t --auth : Username and password for authentication\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" );
@@ -86,6 +87,7 @@ numarg( const char * arg )
static char * reqs[256]; /* arbitrary max */
static int reqCount = 0;
static int debug = 0;
+static char * auth = NULL;
static char*
absolutify( char * buf, size_t len, const char * path )
@@ -114,7 +116,7 @@ static void
readargs( int argc, char ** argv )
{
int opt;
- char optstr[] = "a:c:d:DeEf:ghlmMp:r:s:S:u:Uv:";
+ char optstr[] = "a:c:d:DeEf:ghlmMp:r:s:S:t:u:Uv:";
const struct option longopts[] =
{
@@ -134,6 +136,7 @@ readargs( int argc, char ** argv )
{ "remove", required_argument, NULL, 'r' },
{ "start", required_argument, NULL, 's' },
{ "stop", required_argument, NULL, 'S' },
+ { "auth", required_argument, NULL, 't' },
{ "upload-limit", required_argument, NULL, 'u' },
{ "upload-unlimited", no_argument, NULL, 'U' },
{ "verify", required_argument, NULL, 'v' },
@@ -154,6 +157,9 @@ readargs( int argc, char ** argv )
case 'g': debug = 1;
addArg = FALSE;
break;
+ case 't': auth = tr_strdup( optarg );
+ addArg = FALSE;
+ break;
case 'h': showUsage( );
addArg = FALSE;
break;
@@ -287,7 +293,7 @@ processResponse( const char * host, int port,
(int)len, (int)len, (const char*) response );
if( tr_jsonParse( response, len, &top, NULL ) )
- tr_nerr( MY_NAME, "Unable to parse response" );
+ tr_nerr( MY_NAME, "Unable to parse response \"%*.*s\"", (int)len, (int)len, (char*)response );
else
{
tr_benc *args, *list;
@@ -346,6 +352,10 @@ processRequests( const char * host, int port,
curl_easy_setopt( curl, CURLOPT_WRITEDATA, buf );
curl_easy_setopt( curl, CURLOPT_POST, 1 );
curl_easy_setopt( curl, CURLOPT_URL, url );
+ if( auth ) {
+ curl_easy_setopt( curl, CURLOPT_USERPWD, auth );
+ curl_easy_setopt( curl, CURLOPT_HTTPAUTH, CURLAUTH_ANY );
+ }
for( i=0; i