Merge branch 'trac5348-misidentification-of-local-session'

This commit is contained in:
Mike Gelfand 2016-09-27 21:26:51 +03:00
commit efba03695a
16 changed files with 760 additions and 111 deletions

View File

@ -518,15 +518,16 @@
Method name: "session-set"
Request arguments: one or more of 4.1's arguments, except: "blocklist-size",
"config-dir", "rpc-version", "rpc-version-minimum", and
"version"
"config-dir", "rpc-version", "rpc-version-minimum",
"version", and "session-id"
Response arguments: none
4.1.2. Accessors
Method name: "session-get"
Request arguments: none
Response arguments: all of 4.1's arguments
Request arguments: an optional "fields" array of keys (see 4.1)
Response arguments: key/value pairs matching the request's "fields"
argument if present, or all supported fields (see 4.1) otherwise.
4.2. Session Statistics
@ -743,7 +744,7 @@
13 | 2.30 | yes | session-get | new arg "isUTP" to the "peers" list
| | yes | torrent-add | new arg "cookies"
| | NO | torrent-get | removed arg "peersKnown"
------+---------+-----------+--------------------------+-------------------------------
------+---------+-----------+----------------------+-------------------------------
14 | 2.40 | NO | torrent-get | values of "status" field changed
| | yes | torrent-get | new arg "queuePosition"
| | yes | torrent-get | new arg "isStalled"
@ -760,11 +761,14 @@
| | yes | | new method "queue-move-down"
| | yes | | new method "queue-move-bottom"
| | yes | | new method "torrent-start-now"
------+---------+-----------+--------------------------+-------------------------------
------+---------+-----------+----------------------+-------------------------------
15 | 2.80 | yes | torrent-get | new arg "etaIdle"
| | yes | torrent-rename-path | new method
| | yes | free-space | new method
| | yes | torrent-add | new return return arg "torrent-duplicate"
| | yes | torrent-add | new return arg "torrent-duplicate"
------+---------+-----------+----------------------+-------------------------------
16 | 3.00 | yes | session-get | new request arg "fields"
| | yes | session-get | new arg "session-id"
5.1. Upcoming Breakage

View File

@ -46,6 +46,7 @@ set(${PROJECT_NAME}_SOURCES
rpcimpl.c
rpc-server.c
session.c
session-id.c
stats.c
torrent.c
torrent-ctor.c
@ -105,6 +106,7 @@ set(${PROJECT_NAME}_PUBLIC_HEADERS
makemeta.h
quark.h
rpcimpl.h
session-id.h
tr-getopt.h
transmission.h
utils.h

View File

@ -6,21 +6,8 @@
*
*/
#if defined (HAVE_MKDTEMP) && (!defined (_XOPEN_SOURCE) || _XOPEN_SOURCE < 700)
#undef _XOPEN_SOURCE
#define _XOPEN_SOURCE 700
#elif (defined (HAVE_POSIX_FADVISE) || defined (HAVE_POSIX_FALLOCATE)) && (!defined (_XOPEN_SOURCE) || _XOPEN_SOURCE < 600)
#undef _XOPEN_SOURCE
#define _XOPEN_SOURCE 600
#endif
#if (defined (HAVE_FALLOCATE64) || defined (HAVE_CANONICALIZE_FILE_NAME)) && !defined (_GNU_SOURCE)
#define _GNU_SOURCE
#endif
#if defined (__APPLE__) && !defined (_DARWIN_C_SOURCE)
#define _DARWIN_C_SOURCE
#endif
#undef _GNU_SOURCE
#define _GNU_SOURCE
#include <assert.h>
#include <dirent.h>
@ -31,8 +18,9 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h> /* mmap (), munmap () */
#include <sys/types.h>
#include <sys/file.h> /* flock () */
#include <sys/mman.h> /* mmap (), munmap () */
#include <sys/stat.h>
#include <unistd.h> /* lseek (), write (), ftruncate (), pread (), pwrite (), pathconf (), etc */
@ -920,6 +908,35 @@ tr_sys_file_unmap (const void * address,
return ret;
}
bool
tr_sys_file_lock (tr_sys_file_t handle,
int operation,
tr_error ** error)
{
bool ret;
int native_operation = 0;
assert (handle != TR_BAD_SYS_FILE);
assert ((operation & ~(TR_SYS_FILE_LOCK_SH | TR_SYS_FILE_LOCK_EX | TR_SYS_FILE_LOCK_NB | TR_SYS_FILE_LOCK_UN)) == 0);
assert (!!(operation & TR_SYS_FILE_LOCK_SH) + !!(operation & TR_SYS_FILE_LOCK_EX) + !!(operation & TR_SYS_FILE_LOCK_UN) == 1);
if ((operation & TR_SYS_FILE_LOCK_SH) != 0)
native_operation |= LOCK_SH;
if ((operation & TR_SYS_FILE_LOCK_EX) != 0)
native_operation |= LOCK_EX;
if ((operation & TR_SYS_FILE_LOCK_NB) != 0)
native_operation |= LOCK_NB;
if ((operation & TR_SYS_FILE_LOCK_UN) != 0)
native_operation |= LOCK_UN;
ret = flock (handle, native_operation) != -1;
if (!ret)
set_system_error (error, errno);
return ret;
}
char *
tr_sys_dir_get_current (tr_error ** error)
{

View File

@ -1144,6 +1144,40 @@ tr_sys_file_unmap (const void * address,
return ret;
}
bool
tr_sys_file_lock (tr_sys_file_t handle,
int operation,
tr_error ** error)
{
bool ret;
OVERLAPPED overlapped = { .Pointer = 0, .hEvent = NULL };
assert (handle != TR_BAD_SYS_FILE);
assert ((operation & ~(TR_SYS_FILE_LOCK_SH | TR_SYS_FILE_LOCK_EX | TR_SYS_FILE_LOCK_NB | TR_SYS_FILE_LOCK_UN)) == 0);
assert (!!(operation & TR_SYS_FILE_LOCK_SH) + !!(operation & TR_SYS_FILE_LOCK_EX) + !!(operation & TR_SYS_FILE_LOCK_UN) == 1);
if ((operation & TR_SYS_FILE_LOCK_UN) == 0)
{
DWORD native_flags = 0;
if ((operation & TR_SYS_FILE_LOCK_EX) != 0)
native_flags |= LOCKFILE_EXCLUSIVE_LOCK;
if ((operation & TR_SYS_FILE_LOCK_NB) != 0)
native_flags |= LOCKFILE_FAIL_IMMEDIATELY;
ret = LockFileEx (handle, native_flags, 0, MAXDWORD, MAXDWORD, &overlapped) != FALSE;
}
else
{
ret = UnlockFileEx (handle, 0, MAXDWORD, MAXDWORD, &overlapped) != FALSE;
}
if (!ret)
set_system_error (error, GetLastError ());
return ret;
}
char *
tr_sys_dir_get_current (tr_error ** error)
{

View File

@ -76,6 +76,15 @@ typedef enum
}
tr_seek_origin_t;
typedef enum
{
TR_SYS_FILE_LOCK_SH = 1 << 0,
TR_SYS_FILE_LOCK_EX = 1 << 1,
TR_SYS_FILE_LOCK_NB = 1 << 2,
TR_SYS_FILE_LOCK_UN = 1 << 3
}
tr_sys_file_lock_flags_t;
typedef enum
{
TR_SYS_PATH_NO_FOLLOW = 1 << 0
@ -524,6 +533,23 @@ bool tr_sys_file_unmap (const void * address,
uint64_t size,
struct tr_error ** error);
/**
* @brief Portability wrapper for `flock ()`.
*
* Don't try to upgrade or downgrade the lock unless you know what you are
* doing, as behavior varies a bit between platforms.
*
* @param[in] handle Valid file descriptor.
* @param[in] operation Combination of @ref tr_sys_file_lock_flags_t values.
* @param[out] error Pointer to error object. Optional, pass `NULL` if you
* are not interested in error details.
*
* @return `True` on success, `false` otherwise (with `error` set accordingly).
*/
bool tr_sys_file_lock (tr_sys_file_t handle,
int operation,
struct tr_error ** error);
/* File-related wrappers (utility) */
/**

View File

@ -224,13 +224,12 @@ tr_lockUnlock (tr_lock * l)
#ifdef _WIN32
static char *
win32_get_known_folder (REFKNOWNFOLDERID folder_id)
win32_get_known_folder_ex (REFKNOWNFOLDERID folder_id, DWORD flags)
{
char * ret = NULL;
PWSTR path;
if (SHGetKnownFolderPath (folder_id, KF_FLAG_DONT_UNEXPAND | KF_FLAG_DONT_VERIFY,
NULL, &path) == S_OK)
if (SHGetKnownFolderPath (folder_id, flags | KF_FLAG_DONT_UNEXPAND, NULL, &path) == S_OK)
{
ret = tr_win32_native_to_utf8 (path, -1);
CoTaskMemFree (path);
@ -239,6 +238,12 @@ win32_get_known_folder (REFKNOWNFOLDERID folder_id)
return ret;
}
static char *
win32_get_known_folder (REFKNOWNFOLDERID folder_id)
{
return win32_get_known_folder_ex (folder_id, KF_FLAG_DONT_VERIFY);
}
#endif
static const char *
@ -586,3 +591,21 @@ tr_getWebClientDir (const tr_session * session UNUSED)
return s;
}
char *
tr_getSessionIdDir (void)
{
#ifndef _WIN32
return tr_strdup ("/tmp");
#else
char * program_data_dir = win32_get_known_folder_ex (&FOLDERID_ProgramData, KF_FLAG_CREATE);
char * result = tr_buildPath (program_data_dir, "Transmission", NULL);
tr_free (program_data_dir);
tr_sys_dir_create (result, 0, 0, NULL);
return result;
#endif
}

View File

@ -37,6 +37,9 @@ const char * tr_getTorrentDir (const tr_session *);
/** @brief return the directory where the Web Client's web ui files are kept */
const char * tr_getWebClientDir (const tr_session *);
/** @brief return the directory where session id lock files are stored */
char * tr_getSessionIdDir (void);
/** @} */

View File

@ -315,6 +315,7 @@ static const struct tr_key_struct my_static[] =
{ "seederCount", 11 },
{ "seeding-time-seconds", 20 },
{ "session-count", 13 },
{ "session-id", 10 },
{ "sessionCount", 12 },
{ "show-backup-trackers", 20 },
{ "show-extra-peer-details", 23 },

View File

@ -316,6 +316,7 @@ enum
TR_KEY_seederCount,
TR_KEY_seeding_time_seconds,
TR_KEY_session_count,
TR_KEY_session_id,
TR_KEY_sessionCount,
TR_KEY_show_backup_trackers,
TR_KEY_show_extra_peer_details,

View File

@ -30,6 +30,7 @@
#include "rpcimpl.h"
#include "rpc-server.h"
#include "session.h"
#include "session-id.h"
#include "trevent.h"
#include "utils.h"
#include "variant.h"
@ -63,9 +64,6 @@ struct tr_rpc_server
char * whitelistStr;
tr_list * whitelist;
char * sessionId;
time_t sessionIdExpiresAt;
bool isStreamInitialized;
z_stream stream;
};
@ -81,33 +79,12 @@ struct tr_rpc_server
****
***/
static char*
static const char *
get_current_session_id (struct tr_rpc_server * server)
{
const time_t now = tr_time ();
if (!server->sessionId || (now >= server->sessionIdExpiresAt))
{
int i;
const int n = 48;
const char * pool = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
const size_t pool_size = strlen (pool);
unsigned char * buf = tr_new (unsigned char, n+1);
tr_rand_buffer (buf, n);
for (i=0; i<n; ++i)
buf[i] = pool[ buf[i] % pool_size ];
buf[n] = '\0';
tr_free (server->sessionId);
server->sessionId = (char*) buf;
server->sessionIdExpiresAt = now + (60*60); /* expire in an hour */
}
return server->sessionId;
return tr_session_id_get_current (server->session->session_id);
}
/**
***
**/
@ -1004,7 +981,6 @@ closeServer (void * vserver)
if (s->isStreamInitialized)
deflateEnd (&s->stream);
tr_free (s->url);
tr_free (s->sessionId);
tr_free (s->whitelistStr);
tr_free (s->username);
tr_free (s->password);

View File

@ -26,13 +26,14 @@
#include "platform-quota.h" /* tr_device_info_get_free_space() */
#include "rpcimpl.h"
#include "session.h"
#include "session-id.h"
#include "torrent.h"
#include "utils.h"
#include "variant.h"
#include "version.h"
#include "web.h"
#define RPC_VERSION 15
#define RPC_VERSION 16
#define RPC_VERSION_MIN 1
#define RECENTLY_ACTIVE_SECONDS 60
@ -2039,71 +2040,268 @@ sessionStats (tr_session * session,
return NULL;
}
static void
addSessionField (tr_session * s,
tr_variant * d,
tr_quark key)
{
switch (key)
{
case TR_KEY_alt_speed_up:
tr_variantDictAddInt (d, key, tr_sessionGetAltSpeed_KBps (s, TR_UP));
break;
case TR_KEY_alt_speed_down:
tr_variantDictAddInt (d, key, tr_sessionGetAltSpeed_KBps (s, TR_DOWN));
break;
case TR_KEY_alt_speed_enabled:
tr_variantDictAddBool (d, key, tr_sessionUsesAltSpeed (s));
break;
case TR_KEY_alt_speed_time_begin:
tr_variantDictAddInt (d, key, tr_sessionGetAltSpeedBegin (s));
break;
case TR_KEY_alt_speed_time_end:
tr_variantDictAddInt (d, key,tr_sessionGetAltSpeedEnd (s));
break;
case TR_KEY_alt_speed_time_day:
tr_variantDictAddInt (d, key,tr_sessionGetAltSpeedDay (s));
break;
case TR_KEY_alt_speed_time_enabled:
tr_variantDictAddBool (d, key, tr_sessionUsesAltSpeedTime (s));
break;
case TR_KEY_blocklist_enabled:
tr_variantDictAddBool (d, key, tr_blocklistIsEnabled (s));
break;
case TR_KEY_blocklist_url:
tr_variantDictAddStr (d, key, tr_blocklistGetURL (s));
break;
case TR_KEY_cache_size_mb:
tr_variantDictAddInt (d, key, tr_sessionGetCacheLimit_MB (s));
break;
case TR_KEY_blocklist_size:
tr_variantDictAddInt (d, key, tr_blocklistGetRuleCount (s));
break;
case TR_KEY_config_dir:
tr_variantDictAddStr (d, key, tr_sessionGetConfigDir (s));
break;
case TR_KEY_download_dir:
tr_variantDictAddStr (d, key, tr_sessionGetDownloadDir (s));
break;
case TR_KEY_download_dir_free_space:
tr_variantDictAddInt (d, key, tr_device_info_get_free_space (s->downloadDir));
break;
case TR_KEY_download_queue_enabled:
tr_variantDictAddBool (d, key, tr_sessionGetQueueEnabled (s, TR_DOWN));
break;
case TR_KEY_download_queue_size:
tr_variantDictAddInt (d, key, tr_sessionGetQueueSize (s, TR_DOWN));
break;
case TR_KEY_peer_limit_global:
tr_variantDictAddInt (d, key, tr_sessionGetPeerLimit (s));
break;
case TR_KEY_peer_limit_per_torrent:
tr_variantDictAddInt (d, key, tr_sessionGetPeerLimitPerTorrent (s));
break;
case TR_KEY_incomplete_dir:
tr_variantDictAddStr (d, key, tr_sessionGetIncompleteDir (s));
break;
case TR_KEY_incomplete_dir_enabled:
tr_variantDictAddBool (d, key, tr_sessionIsIncompleteDirEnabled (s));
break;
case TR_KEY_pex_enabled:
tr_variantDictAddBool (d, key, tr_sessionIsPexEnabled (s));
break;
case TR_KEY_utp_enabled:
tr_variantDictAddBool (d, key, tr_sessionIsUTPEnabled (s));
break;
case TR_KEY_dht_enabled:
tr_variantDictAddBool (d, key, tr_sessionIsDHTEnabled (s));
break;
case TR_KEY_lpd_enabled:
tr_variantDictAddBool (d, key, tr_sessionIsLPDEnabled (s));
break;
case TR_KEY_peer_port:
tr_variantDictAddInt (d, key, tr_sessionGetPeerPort (s));
break;
case TR_KEY_peer_port_random_on_start:
tr_variantDictAddBool (d, key, tr_sessionGetPeerPortRandomOnStart (s));
break;
case TR_KEY_port_forwarding_enabled:
tr_variantDictAddBool (d, key, tr_sessionIsPortForwardingEnabled (s));
break;
case TR_KEY_rename_partial_files:
tr_variantDictAddBool (d, key, tr_sessionIsIncompleteFileNamingEnabled (s));
break;
case TR_KEY_rpc_version:
tr_variantDictAddInt (d, key, RPC_VERSION);
break;
case TR_KEY_rpc_version_minimum:
tr_variantDictAddInt (d, key, RPC_VERSION_MIN);
break;
case TR_KEY_seedRatioLimit:
tr_variantDictAddReal (d, key, tr_sessionGetRatioLimit (s));
break;
case TR_KEY_seedRatioLimited:
tr_variantDictAddBool (d, key, tr_sessionIsRatioLimited (s));
break;
case TR_KEY_idle_seeding_limit:
tr_variantDictAddInt (d, key, tr_sessionGetIdleLimit (s));
break;
case TR_KEY_idle_seeding_limit_enabled:
tr_variantDictAddBool (d, key, tr_sessionIsIdleLimited (s));
break;
case TR_KEY_seed_queue_enabled:
tr_variantDictAddBool (d, key, tr_sessionGetQueueEnabled (s, TR_UP));
break;
case TR_KEY_seed_queue_size:
tr_variantDictAddInt (d, key, tr_sessionGetQueueSize (s, TR_UP));
break;
case TR_KEY_start_added_torrents:
tr_variantDictAddBool (d, key, !tr_sessionGetPaused (s));
break;
case TR_KEY_trash_original_torrent_files:
tr_variantDictAddBool (d, key, tr_sessionGetDeleteSource (s));
break;
case TR_KEY_speed_limit_up:
tr_variantDictAddInt (d, key, tr_sessionGetSpeedLimit_KBps (s, TR_UP));
break;
case TR_KEY_speed_limit_up_enabled:
tr_variantDictAddBool (d, key, tr_sessionIsSpeedLimited (s, TR_UP));
break;
case TR_KEY_speed_limit_down:
tr_variantDictAddInt (d, key, tr_sessionGetSpeedLimit_KBps (s, TR_DOWN));
break;
case TR_KEY_speed_limit_down_enabled:
tr_variantDictAddBool (d, key, tr_sessionIsSpeedLimited (s, TR_DOWN));
break;
case TR_KEY_script_torrent_done_filename:
tr_variantDictAddStr (d, key, tr_sessionGetTorrentDoneScript (s));
break;
case TR_KEY_script_torrent_done_enabled:
tr_variantDictAddBool (d, key, tr_sessionIsTorrentDoneScriptEnabled (s));
break;
case TR_KEY_queue_stalled_enabled:
tr_variantDictAddBool (d, key, tr_sessionGetQueueStalledEnabled (s));
break;
case TR_KEY_queue_stalled_minutes:
tr_variantDictAddInt (d, key, tr_sessionGetQueueStalledMinutes (s));
break;
case TR_KEY_units:
tr_formatter_get_units (tr_variantDictAddDict (d, key, 0));
break;
case TR_KEY_version:
tr_variantDictAddStr (d, key, LONG_VERSION_STRING);
break;
case TR_KEY_encryption:
{
const char * str;
switch (tr_sessionGetEncryption (s))
{
case TR_CLEAR_PREFERRED:
str = "tolerated";
break;
case TR_ENCRYPTION_REQUIRED:
str = "required";
break;
default:
str = "preferred";
break;
}
tr_variantDictAddStr (d, key, str);
break;
}
case TR_KEY_session_id:
tr_variantDictAddStr (d, key, tr_session_id_get_current (s->session_id));
break;
}
}
static const char*
sessionGet (tr_session * s,
tr_variant * args_in UNUSED,
tr_variant * args_in,
tr_variant * args_out,
struct tr_rpc_idle_data * idle_data UNUSED)
{
const char * str;
tr_variant * d = args_out;
tr_variant * fields;
assert (idle_data == NULL);
tr_variantDictAddInt (d, TR_KEY_alt_speed_up, tr_sessionGetAltSpeed_KBps (s,TR_UP));
tr_variantDictAddInt (d, TR_KEY_alt_speed_down, tr_sessionGetAltSpeed_KBps (s,TR_DOWN));
tr_variantDictAddBool (d, TR_KEY_alt_speed_enabled, tr_sessionUsesAltSpeed (s));
tr_variantDictAddInt (d, TR_KEY_alt_speed_time_begin, tr_sessionGetAltSpeedBegin (s));
tr_variantDictAddInt (d, TR_KEY_alt_speed_time_end,tr_sessionGetAltSpeedEnd (s));
tr_variantDictAddInt (d, TR_KEY_alt_speed_time_day,tr_sessionGetAltSpeedDay (s));
tr_variantDictAddBool (d, TR_KEY_alt_speed_time_enabled, tr_sessionUsesAltSpeedTime (s));
tr_variantDictAddBool (d, TR_KEY_blocklist_enabled, tr_blocklistIsEnabled (s));
tr_variantDictAddStr (d, TR_KEY_blocklist_url, tr_blocklistGetURL (s));
tr_variantDictAddInt (d, TR_KEY_cache_size_mb, tr_sessionGetCacheLimit_MB (s));
tr_variantDictAddInt (d, TR_KEY_blocklist_size, tr_blocklistGetRuleCount (s));
tr_variantDictAddStr (d, TR_KEY_config_dir, tr_sessionGetConfigDir (s));
tr_variantDictAddStr (d, TR_KEY_download_dir, tr_sessionGetDownloadDir (s));
tr_variantDictAddInt (d, TR_KEY_download_dir_free_space, tr_device_info_get_free_space (s->downloadDir));
tr_variantDictAddBool (d, TR_KEY_download_queue_enabled, tr_sessionGetQueueEnabled (s, TR_DOWN));
tr_variantDictAddInt (d, TR_KEY_download_queue_size, tr_sessionGetQueueSize (s, TR_DOWN));
tr_variantDictAddInt (d, TR_KEY_peer_limit_global, tr_sessionGetPeerLimit (s));
tr_variantDictAddInt (d, TR_KEY_peer_limit_per_torrent, tr_sessionGetPeerLimitPerTorrent (s));
tr_variantDictAddStr (d, TR_KEY_incomplete_dir, tr_sessionGetIncompleteDir (s));
tr_variantDictAddBool (d, TR_KEY_incomplete_dir_enabled, tr_sessionIsIncompleteDirEnabled (s));
tr_variantDictAddBool (d, TR_KEY_pex_enabled, tr_sessionIsPexEnabled (s));
tr_variantDictAddBool (d, TR_KEY_utp_enabled, tr_sessionIsUTPEnabled (s));
tr_variantDictAddBool (d, TR_KEY_dht_enabled, tr_sessionIsDHTEnabled (s));
tr_variantDictAddBool (d, TR_KEY_lpd_enabled, tr_sessionIsLPDEnabled (s));
tr_variantDictAddInt (d, TR_KEY_peer_port, tr_sessionGetPeerPort (s));
tr_variantDictAddBool (d, TR_KEY_peer_port_random_on_start, tr_sessionGetPeerPortRandomOnStart (s));
tr_variantDictAddBool (d, TR_KEY_port_forwarding_enabled, tr_sessionIsPortForwardingEnabled (s));
tr_variantDictAddBool (d, TR_KEY_rename_partial_files, tr_sessionIsIncompleteFileNamingEnabled (s));
tr_variantDictAddInt (d, TR_KEY_rpc_version, RPC_VERSION);
tr_variantDictAddInt (d, TR_KEY_rpc_version_minimum, RPC_VERSION_MIN);
tr_variantDictAddReal (d, TR_KEY_seedRatioLimit, tr_sessionGetRatioLimit (s));
tr_variantDictAddBool (d, TR_KEY_seedRatioLimited, tr_sessionIsRatioLimited (s));
tr_variantDictAddInt (d, TR_KEY_idle_seeding_limit, tr_sessionGetIdleLimit (s));
tr_variantDictAddBool (d, TR_KEY_idle_seeding_limit_enabled, tr_sessionIsIdleLimited (s));
tr_variantDictAddBool (d, TR_KEY_seed_queue_enabled, tr_sessionGetQueueEnabled (s, TR_UP));
tr_variantDictAddInt (d, TR_KEY_seed_queue_size, tr_sessionGetQueueSize (s, TR_UP));
tr_variantDictAddBool (d, TR_KEY_start_added_torrents, !tr_sessionGetPaused (s));
tr_variantDictAddBool (d, TR_KEY_trash_original_torrent_files, tr_sessionGetDeleteSource (s));
tr_variantDictAddInt (d, TR_KEY_speed_limit_up, tr_sessionGetSpeedLimit_KBps (s, TR_UP));
tr_variantDictAddBool (d, TR_KEY_speed_limit_up_enabled, tr_sessionIsSpeedLimited (s, TR_UP));
tr_variantDictAddInt (d, TR_KEY_speed_limit_down, tr_sessionGetSpeedLimit_KBps (s, TR_DOWN));
tr_variantDictAddBool (d, TR_KEY_speed_limit_down_enabled, tr_sessionIsSpeedLimited (s, TR_DOWN));
tr_variantDictAddStr (d, TR_KEY_script_torrent_done_filename, tr_sessionGetTorrentDoneScript (s));
tr_variantDictAddBool (d, TR_KEY_script_torrent_done_enabled, tr_sessionIsTorrentDoneScriptEnabled (s));
tr_variantDictAddBool (d, TR_KEY_queue_stalled_enabled, tr_sessionGetQueueStalledEnabled (s));
tr_variantDictAddInt (d, TR_KEY_queue_stalled_minutes, tr_sessionGetQueueStalledMinutes (s));
tr_formatter_get_units (tr_variantDictAddDict (d, TR_KEY_units, 0));
tr_variantDictAddStr (d, TR_KEY_version, LONG_VERSION_STRING);
switch (tr_sessionGetEncryption (s))
if (tr_variantDictFindList (args_in, TR_KEY_fields, &fields))
{
case TR_CLEAR_PREFERRED: str = "tolerated"; break;
case TR_ENCRYPTION_REQUIRED: str = "required"; break;
default: str = "preferred"; break;
const size_t field_count = tr_variantListSize (fields);
for (size_t i = 0; i < field_count; ++i)
{
const char * field_name;
size_t field_name_len;
tr_quark field_id;
if (!tr_variantGetStr (tr_variantListChild (fields, i), &field_name, &field_name_len))
continue;
if (!tr_quark_lookup (field_name, field_name_len, &field_id))
continue;
addSessionField (s, args_out, field_id);
}
}
else
{
for (tr_quark field_id = TR_KEY_NONE + 1; field_id < TR_N_KEYS; ++field_id)
addSessionField (s, args_out, field_id);
}
tr_variantDictAddStr (d, TR_KEY_encryption, str);
return NULL;
}

View File

@ -0,0 +1,210 @@
/*
* This file Copyright (C) 2016 Mnemosyne LLC
*
* It may be used under the GNU GPL versions 2 or 3
* or any future license endorsed by Mnemosyne LLC.
*
*/
#include <string.h>
#include <time.h>
#ifndef _WIN32
#include <sys/stat.h>
#endif
#include "transmission.h"
#include "crypto-utils.h"
#include "error.h"
#include "error-types.h"
#include "file.h"
#include "log.h"
#include "platform.h"
#include "session-id.h"
#include "utils.h"
#define SESSION_ID_SIZE 48
#define SESSION_ID_DURATION_SEC (60 * 60) /* expire in an hour */
struct tr_session_id
{
char * current_value;
char * previous_value;
tr_sys_file_t current_lock_file;
tr_sys_file_t previous_lock_file;
time_t expires_at;
};
static char *
generate_new_session_id_value (void)
{
const char pool[] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
const size_t pool_size = sizeof (pool) - 1;
char * buf = tr_new (char, SESSION_ID_SIZE + 1);
tr_rand_buffer (buf, SESSION_ID_SIZE);
for (size_t i = 0; i < SESSION_ID_SIZE; ++i)
buf[i] = pool[(unsigned char) buf[i] % pool_size];
buf[SESSION_ID_SIZE] = '\0';
return buf;
}
static char *
get_session_id_lock_file_path (const char * session_id)
{
char * lock_file_dir = tr_getSessionIdDir ();
char * lock_file_path = tr_strdup_printf ("%s/tr_session_id_%s", lock_file_dir, session_id);
tr_free (lock_file_dir);
return lock_file_path;
}
static tr_sys_file_t
create_session_id_lock_file (const char * session_id)
{
if (session_id == NULL)
return TR_BAD_SYS_FILE;
char * lock_file_path = get_session_id_lock_file_path (session_id);
tr_sys_file_t lock_file;
tr_error * error = NULL;
lock_file = tr_sys_file_open (lock_file_path, TR_SYS_FILE_READ | TR_SYS_FILE_WRITE | TR_SYS_FILE_CREATE, 0600, &error);
if (lock_file != TR_BAD_SYS_FILE)
{
if (tr_sys_file_lock (lock_file, TR_SYS_FILE_LOCK_EX | TR_SYS_FILE_LOCK_NB, &error))
{
#ifndef _WIN32
/* Allow any user to lock the file regardless of current umask */
fchmod (lock_file, 0644);
#endif
}
else
{
tr_sys_file_close (lock_file, NULL);
lock_file = TR_BAD_SYS_FILE;
}
}
if (error != NULL)
{
tr_logAddError ("Unable to create session lock file (%d): %s", error->code, error->message);
tr_error_free (error);
}
tr_free (lock_file_path);
return lock_file;
}
static void
destroy_session_id_lock_file (tr_sys_file_t lock_file,
const char * session_id)
{
if (lock_file != TR_BAD_SYS_FILE)
tr_sys_file_close (lock_file, NULL);
if (session_id != NULL)
{
char * lock_file_path = get_session_id_lock_file_path (session_id);
tr_sys_path_remove (lock_file_path, NULL);
tr_free (lock_file_path);
}
}
tr_session_id_t
tr_session_id_new (void)
{
const tr_session_id_t session_id = tr_new0 (struct tr_session_id, 1);
session_id->current_lock_file = TR_BAD_SYS_FILE;
session_id->previous_lock_file = TR_BAD_SYS_FILE;
return session_id;
}
void
tr_session_id_free (tr_session_id_t session_id)
{
if (session_id == NULL)
return;
destroy_session_id_lock_file (session_id->previous_lock_file, session_id->previous_value);
destroy_session_id_lock_file (session_id->current_lock_file, session_id->current_value);
tr_free (session_id->previous_value);
tr_free (session_id->current_value);
tr_free (session_id);
}
const char *
tr_session_id_get_current (tr_session_id_t session_id)
{
const time_t now = tr_time ();
if (session_id->current_value == NULL || now >= session_id->expires_at)
{
destroy_session_id_lock_file (session_id->previous_lock_file, session_id->previous_value);
tr_free (session_id->previous_value);
session_id->previous_value = session_id->current_value;
session_id->current_value = generate_new_session_id_value ();
session_id->previous_lock_file = session_id->current_lock_file;
session_id->current_lock_file = create_session_id_lock_file (session_id->current_value);
session_id->expires_at = now + SESSION_ID_DURATION_SEC;
}
return session_id->current_value;
}
bool
tr_session_id_is_local (const char * session_id)
{
bool ret = false;
if (session_id != NULL)
{
char * lock_file_path = get_session_id_lock_file_path (session_id);
tr_sys_file_t lock_file;
tr_error * error = NULL;
lock_file = tr_sys_file_open (lock_file_path, TR_SYS_FILE_READ, 0, &error);
if (lock_file == TR_BAD_SYS_FILE)
{
if (TR_ERROR_IS_ENOENT (error->code))
tr_error_clear (&error);
}
else
{
if (!tr_sys_file_lock (lock_file, TR_SYS_FILE_LOCK_SH | TR_SYS_FILE_LOCK_NB, &error))
{
#ifndef _WIN32
if (error->code == EWOULDBLOCK)
#else
if (error->code == ERROR_LOCK_VIOLATION)
#endif
{
ret = true;
tr_error_clear (&error);
}
}
tr_sys_file_close (lock_file, NULL);
}
if (error != NULL)
{
tr_logAddError ("Unable to open session lock file (%d): %s", error->code, error->message);
tr_error_free (error);
}
tr_free (lock_file_path);
}
return ret;
}

View File

@ -0,0 +1,57 @@
/*
* This file Copyright (C) 2016 Mnemosyne LLC
*
* It may be used under the GNU GPL versions 2 or 3
* or any future license endorsed by Mnemosyne LLC.
*
*/
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
typedef struct tr_session_id * tr_session_id_t;
/**
* Create new session identifier object.
*
* @return New session identifier object.
*/
tr_session_id_t tr_session_id_new (void);
/**
* Free session identifier object.
*
* @param[in] session_id Session identifier object.
*/
void tr_session_id_free (tr_session_id_t session_id);
/**
* Get current session identifier as string.
*
* @param[in] session_id Session identifier object.
*
* @return String representation of current session identifier.
*/
const char * tr_session_id_get_current (tr_session_id_t session_id);
/**
* Check if session ID corresponds to session running on the same machine as
* the caller.
*
* This is useful for various behavior alterations, such as transforming
* relative paths to absolute before passing through RPC, or presenting
* different UI for local and remote sessions.
*
* @param[in] session_id String representation of session identifier object.
*
* @return `True` if session is valid and local, `false` otherwise.
*/
bool tr_session_id_is_local (const char * session_id);
#ifdef __cplusplus
}
#endif

View File

@ -11,6 +11,7 @@
#include <string.h>
#include "transmission.h"
#include "session.h"
#include "session-id.h"
#include "utils.h"
#include "version.h"
@ -45,4 +46,96 @@ testPeerId (void)
return 0;
}
MAIN_SINGLE_TEST (testPeerId)
static int
test_session_id (void)
{
tr_session_id_t session_id;
const char * session_id_str_1 = NULL;
const char * session_id_str_2 = NULL;
const char * session_id_str_3 = NULL;
check (!tr_session_id_is_local (NULL));
check (!tr_session_id_is_local (""));
check (!tr_session_id_is_local ("test"));
session_id = tr_session_id_new ();
check (session_id != NULL);
tr_timeUpdate (0);
session_id_str_1 = tr_session_id_get_current (session_id);
check (session_id_str_1 != NULL);
check (strlen (session_id_str_1) == 48);
session_id_str_1 = tr_strdup (session_id_str_1);
check (tr_session_id_is_local (session_id_str_1));
tr_timeUpdate (60 * 60 - 1);
check (tr_session_id_is_local (session_id_str_1));
session_id_str_2 = tr_session_id_get_current (session_id);
check (session_id_str_2 != NULL);
check (strlen (session_id_str_2) == 48);
check (strcmp (session_id_str_2, session_id_str_1) == 0);
tr_timeUpdate (60 * 60);
check (tr_session_id_is_local (session_id_str_1));
session_id_str_2 = tr_session_id_get_current (session_id);
check (session_id_str_2 != NULL);
check (strlen (session_id_str_2) == 48);
check (strcmp (session_id_str_2, session_id_str_1) != 0);
session_id_str_2 = tr_strdup (session_id_str_2);
check (tr_session_id_is_local (session_id_str_2));
check (tr_session_id_is_local (session_id_str_1));
tr_timeUpdate (60 * 60 * 2);
check (tr_session_id_is_local (session_id_str_2));
check (tr_session_id_is_local (session_id_str_1));
session_id_str_3 = tr_session_id_get_current (session_id);
check (session_id_str_3 != NULL);
check (strlen (session_id_str_3) == 48);
check (strcmp (session_id_str_3, session_id_str_2) != 0);
check (strcmp (session_id_str_3, session_id_str_1) != 0);
session_id_str_3 = tr_strdup (session_id_str_3);
check (tr_session_id_is_local (session_id_str_3));
check (tr_session_id_is_local (session_id_str_2));
check (!tr_session_id_is_local (session_id_str_1));
tr_timeUpdate (60 * 60 * 10);
check (tr_session_id_is_local (session_id_str_3));
check (tr_session_id_is_local (session_id_str_2));
check (!tr_session_id_is_local (session_id_str_1));
check (!tr_session_id_is_local (NULL));
check (!tr_session_id_is_local (""));
check (!tr_session_id_is_local ("test"));
tr_session_id_free (session_id);
check (!tr_session_id_is_local (session_id_str_3));
check (!tr_session_id_is_local (session_id_str_2));
check (!tr_session_id_is_local (session_id_str_1));
tr_free (session_id_str_3);
tr_free (session_id_str_2);
tr_free (session_id_str_1);
return 0;
}
int
main (void)
{
const testFunc tests[] = { testPeerId,
test_session_id };
return runTests (tests, NUM_TESTS (tests));
}

View File

@ -45,6 +45,7 @@
#include "port-forwarding.h"
#include "rpc-server.h"
#include "session.h"
#include "session-id.h"
#include "stats.h"
#include "torrent.h"
#include "tr-dht.h" /* tr_dhtUpkeep () */
@ -599,6 +600,7 @@ tr_sessionInit (const char * configDir,
session->lock = tr_lockNew ();
session->cache = tr_cacheNew (1024*1024*2);
session->magicNumber = SESSION_MAGIC_NUMBER;
session->session_id = tr_session_id_new ();
tr_bandwidthConstruct (&session->bandwidth, session, NULL);
tr_variantInitList (&session->removedTorrents, 0);
@ -1955,6 +1957,7 @@ tr_sessionClose (tr_session * session)
tr_variantFree (&session->removedTorrents);
tr_bandwidthDestruct (&session->bandwidth);
tr_bitfieldDestruct (&session->turtle.minutes);
tr_session_id_free (session->session_id);
tr_lockFree (session->lock);
if (session->metainfoLookup)
{

View File

@ -196,6 +196,7 @@ struct tr_session
struct tr_web * web;
struct tr_session_id * session_id;
struct tr_rpc_server * rpcServer;
tr_rpc_func rpc_func;
void * rpc_func_user_data;