500 lines
11 KiB
C
500 lines
11 KiB
C
/*
|
|
* This file Copyright (C) 2009-2014 Mnemosyne LLC
|
|
*
|
|
* It may be used under the GNU GPL versions 2 or 3
|
|
* or any future license endorsed by Mnemosyne LLC.
|
|
*
|
|
*/
|
|
|
|
#include <errno.h> /* EINVAL */
|
|
|
|
#include "transmission.h"
|
|
#include "file.h"
|
|
#include "magnet.h"
|
|
#include "session.h" /* tr_sessionFindTorrentFile() */
|
|
#include "torrent.h" /* tr_ctorGetSave() */
|
|
#include "tr-assert.h"
|
|
#include "utils.h" /* tr_new0 */
|
|
#include "variant.h"
|
|
|
|
struct optional_args
|
|
{
|
|
bool isSet_paused;
|
|
bool isSet_connected;
|
|
bool isSet_downloadDir;
|
|
|
|
bool isPaused;
|
|
uint16_t peerLimit;
|
|
char* downloadDir;
|
|
};
|
|
|
|
/** Opaque class used when instantiating torrents.
|
|
* @ingroup tr_ctor */
|
|
struct tr_ctor
|
|
{
|
|
tr_session const* session;
|
|
bool saveInOurTorrentsDir;
|
|
bool doDelete;
|
|
|
|
tr_priority_t bandwidthPriority;
|
|
bool isSet_metainfo;
|
|
bool isSet_delete;
|
|
tr_variant metainfo;
|
|
char* sourceFile;
|
|
|
|
struct optional_args optionalArgs[2];
|
|
|
|
char* cookies;
|
|
char* incompleteDir;
|
|
|
|
tr_file_index_t* want;
|
|
tr_file_index_t wantSize;
|
|
tr_file_index_t* notWant;
|
|
tr_file_index_t notWantSize;
|
|
tr_file_index_t* low;
|
|
tr_file_index_t lowSize;
|
|
tr_file_index_t* normal;
|
|
tr_file_index_t normalSize;
|
|
tr_file_index_t* high;
|
|
tr_file_index_t highSize;
|
|
};
|
|
|
|
/***
|
|
****
|
|
***/
|
|
|
|
static void setSourceFile(tr_ctor* ctor, char const* sourceFile)
|
|
{
|
|
tr_free(ctor->sourceFile);
|
|
ctor->sourceFile = tr_strdup(sourceFile);
|
|
}
|
|
|
|
static void clearMetainfo(tr_ctor* ctor)
|
|
{
|
|
if (ctor->isSet_metainfo)
|
|
{
|
|
ctor->isSet_metainfo = false;
|
|
tr_variantFree(&ctor->metainfo);
|
|
}
|
|
|
|
setSourceFile(ctor, NULL);
|
|
}
|
|
|
|
int tr_ctorSetMetainfo(tr_ctor* ctor, void const* metainfo, size_t len)
|
|
{
|
|
int err;
|
|
|
|
clearMetainfo(ctor);
|
|
err = tr_variantFromBenc(&ctor->metainfo, metainfo, len);
|
|
ctor->isSet_metainfo = err == 0;
|
|
return err;
|
|
}
|
|
|
|
char const* tr_ctorGetSourceFile(tr_ctor const* ctor)
|
|
{
|
|
return ctor->sourceFile;
|
|
}
|
|
|
|
int tr_ctorSetMetainfoFromMagnetLink(tr_ctor* ctor, char const* magnet_link)
|
|
{
|
|
int err;
|
|
tr_magnet_info* magnet_info = tr_magnetParse(magnet_link);
|
|
|
|
if (magnet_info == NULL)
|
|
{
|
|
err = -1;
|
|
}
|
|
else
|
|
{
|
|
size_t len;
|
|
tr_variant tmp;
|
|
char* str;
|
|
|
|
tr_magnetCreateMetainfo(magnet_info, &tmp);
|
|
str = tr_variantToStr(&tmp, TR_VARIANT_FMT_BENC, &len);
|
|
err = tr_ctorSetMetainfo(ctor, (uint8_t const*)str, len);
|
|
|
|
tr_free(str);
|
|
tr_variantFree(&tmp);
|
|
tr_magnetFree(magnet_info);
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
int tr_ctorSetMetainfoFromFile(tr_ctor* ctor, char const* filename)
|
|
{
|
|
uint8_t* metainfo;
|
|
size_t len;
|
|
int err;
|
|
|
|
metainfo = tr_loadFile(filename, &len, NULL);
|
|
|
|
if (metainfo != NULL && len != 0)
|
|
{
|
|
err = tr_ctorSetMetainfo(ctor, metainfo, len);
|
|
}
|
|
else
|
|
{
|
|
clearMetainfo(ctor);
|
|
err = 1;
|
|
}
|
|
|
|
setSourceFile(ctor, filename);
|
|
|
|
/* if no `name' field was set, then set it from the filename */
|
|
if (ctor->isSet_metainfo)
|
|
{
|
|
tr_variant* info;
|
|
|
|
if (tr_variantDictFindDict(&ctor->metainfo, TR_KEY_info, &info))
|
|
{
|
|
char const* name;
|
|
|
|
if (!tr_variantDictFindStr(info, TR_KEY_name_utf_8, &name, NULL))
|
|
{
|
|
if (!tr_variantDictFindStr(info, TR_KEY_name, &name, NULL))
|
|
{
|
|
name = NULL;
|
|
}
|
|
}
|
|
|
|
if (tr_str_is_empty(name))
|
|
{
|
|
char* base = tr_sys_path_basename(filename, NULL);
|
|
|
|
if (base != NULL)
|
|
{
|
|
tr_variantDictAddStr(info, TR_KEY_name, base);
|
|
tr_free(base);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
tr_free(metainfo);
|
|
return err;
|
|
}
|
|
|
|
int tr_ctorSetMetainfoFromHash(tr_ctor* ctor, char const* hashString)
|
|
{
|
|
int err;
|
|
char const* filename;
|
|
|
|
filename = tr_sessionFindTorrentFile(ctor->session, hashString);
|
|
|
|
if (filename == NULL)
|
|
{
|
|
err = EINVAL;
|
|
}
|
|
else
|
|
{
|
|
err = tr_ctorSetMetainfoFromFile(ctor, filename);
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
/***
|
|
****
|
|
***/
|
|
|
|
void tr_ctorSetFilePriorities(tr_ctor* ctor, tr_file_index_t const* files, tr_file_index_t fileCount, tr_priority_t priority)
|
|
{
|
|
tr_file_index_t** myfiles;
|
|
tr_file_index_t* mycount;
|
|
|
|
switch (priority)
|
|
{
|
|
case TR_PRI_LOW:
|
|
myfiles = &ctor->low;
|
|
mycount = &ctor->lowSize;
|
|
break;
|
|
|
|
case TR_PRI_HIGH:
|
|
myfiles = &ctor->high;
|
|
mycount = &ctor->highSize;
|
|
break;
|
|
|
|
default /*TR_PRI_NORMAL*/:
|
|
myfiles = &ctor->normal;
|
|
mycount = &ctor->normalSize;
|
|
break;
|
|
}
|
|
|
|
tr_free(*myfiles);
|
|
*myfiles = tr_memdup(files, sizeof(tr_file_index_t) * fileCount);
|
|
*mycount = fileCount;
|
|
}
|
|
|
|
void tr_ctorInitTorrentPriorities(tr_ctor const* ctor, tr_torrent* tor)
|
|
{
|
|
for (tr_file_index_t i = 0; i < ctor->lowSize; ++i)
|
|
{
|
|
tr_torrentInitFilePriority(tor, ctor->low[i], TR_PRI_LOW);
|
|
}
|
|
|
|
for (tr_file_index_t i = 0; i < ctor->normalSize; ++i)
|
|
{
|
|
tr_torrentInitFilePriority(tor, ctor->normal[i], TR_PRI_NORMAL);
|
|
}
|
|
|
|
for (tr_file_index_t i = 0; i < ctor->highSize; ++i)
|
|
{
|
|
tr_torrentInitFilePriority(tor, ctor->high[i], TR_PRI_HIGH);
|
|
}
|
|
}
|
|
|
|
void tr_ctorSetFilesWanted(tr_ctor* ctor, tr_file_index_t const* files, tr_file_index_t fileCount, bool wanted)
|
|
{
|
|
tr_file_index_t** myfiles = wanted ? &ctor->want : &ctor->notWant;
|
|
tr_file_index_t* mycount = wanted ? &ctor->wantSize : &ctor->notWantSize;
|
|
|
|
tr_free(*myfiles);
|
|
*myfiles = tr_memdup(files, sizeof(tr_file_index_t) * fileCount);
|
|
*mycount = fileCount;
|
|
}
|
|
|
|
void tr_ctorInitTorrentWanted(tr_ctor const* ctor, tr_torrent* tor)
|
|
{
|
|
if (ctor->notWantSize != 0)
|
|
{
|
|
tr_torrentInitFileDLs(tor, ctor->notWant, ctor->notWantSize, false);
|
|
}
|
|
|
|
if (ctor->wantSize != 0)
|
|
{
|
|
tr_torrentInitFileDLs(tor, ctor->want, ctor->wantSize, true);
|
|
}
|
|
}
|
|
|
|
/***
|
|
****
|
|
***/
|
|
|
|
void tr_ctorSetDeleteSource(tr_ctor* ctor, bool deleteSource)
|
|
{
|
|
ctor->doDelete = deleteSource;
|
|
ctor->isSet_delete = true;
|
|
}
|
|
|
|
bool tr_ctorGetDeleteSource(tr_ctor const* ctor, bool* setme)
|
|
{
|
|
bool ret = true;
|
|
|
|
if (!ctor->isSet_delete)
|
|
{
|
|
ret = false;
|
|
}
|
|
else if (setme != NULL)
|
|
{
|
|
*setme = ctor->doDelete;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/***
|
|
****
|
|
***/
|
|
|
|
void tr_ctorSetSave(tr_ctor* ctor, bool saveInOurTorrentsDir)
|
|
{
|
|
ctor->saveInOurTorrentsDir = saveInOurTorrentsDir;
|
|
}
|
|
|
|
bool tr_ctorGetSave(tr_ctor const* ctor)
|
|
{
|
|
return ctor != NULL && ctor->saveInOurTorrentsDir;
|
|
}
|
|
|
|
void tr_ctorSetPaused(tr_ctor* ctor, tr_ctorMode mode, bool isPaused)
|
|
{
|
|
TR_ASSERT(ctor != NULL);
|
|
TR_ASSERT(mode == TR_FALLBACK || mode == TR_FORCE);
|
|
|
|
struct optional_args* args = &ctor->optionalArgs[mode];
|
|
args->isSet_paused = true;
|
|
args->isPaused = isPaused;
|
|
}
|
|
|
|
void tr_ctorSetPeerLimit(tr_ctor* ctor, tr_ctorMode mode, uint16_t peerLimit)
|
|
{
|
|
TR_ASSERT(ctor != NULL);
|
|
TR_ASSERT(mode == TR_FALLBACK || mode == TR_FORCE);
|
|
|
|
struct optional_args* args = &ctor->optionalArgs[mode];
|
|
args->isSet_connected = true;
|
|
args->peerLimit = peerLimit;
|
|
}
|
|
|
|
void tr_ctorSetDownloadDir(tr_ctor* ctor, tr_ctorMode mode, char const* directory)
|
|
{
|
|
TR_ASSERT(ctor != NULL);
|
|
TR_ASSERT(mode == TR_FALLBACK || mode == TR_FORCE);
|
|
|
|
struct optional_args* args = &ctor->optionalArgs[mode];
|
|
tr_free(args->downloadDir);
|
|
args->downloadDir = NULL;
|
|
args->isSet_downloadDir = false;
|
|
|
|
if (!tr_str_is_empty(directory))
|
|
{
|
|
args->isSet_downloadDir = true;
|
|
args->downloadDir = tr_strdup(directory);
|
|
}
|
|
}
|
|
|
|
void tr_ctorSetIncompleteDir(tr_ctor* ctor, char const* directory)
|
|
{
|
|
tr_free(ctor->incompleteDir);
|
|
ctor->incompleteDir = tr_strdup(directory);
|
|
}
|
|
|
|
bool tr_ctorGetPeerLimit(tr_ctor const* ctor, tr_ctorMode mode, uint16_t* setmeCount)
|
|
{
|
|
bool ret = true;
|
|
struct optional_args const* args = &ctor->optionalArgs[mode];
|
|
|
|
if (!args->isSet_connected)
|
|
{
|
|
ret = false;
|
|
}
|
|
else if (setmeCount != NULL)
|
|
{
|
|
*setmeCount = args->peerLimit;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
bool tr_ctorGetPaused(tr_ctor const* ctor, tr_ctorMode mode, bool* setmeIsPaused)
|
|
{
|
|
bool ret = true;
|
|
struct optional_args const* args = &ctor->optionalArgs[mode];
|
|
|
|
if (!args->isSet_paused)
|
|
{
|
|
ret = false;
|
|
}
|
|
else if (setmeIsPaused != NULL)
|
|
{
|
|
*setmeIsPaused = args->isPaused;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
bool tr_ctorGetDownloadDir(tr_ctor const* ctor, tr_ctorMode mode, char const** setmeDownloadDir)
|
|
{
|
|
bool ret = true;
|
|
struct optional_args const* args = &ctor->optionalArgs[mode];
|
|
|
|
if (!args->isSet_downloadDir)
|
|
{
|
|
ret = false;
|
|
}
|
|
else if (setmeDownloadDir != NULL)
|
|
{
|
|
*setmeDownloadDir = args->downloadDir;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
bool tr_ctorGetIncompleteDir(tr_ctor const* ctor, char const** setmeIncompleteDir)
|
|
{
|
|
bool ret = true;
|
|
|
|
if (ctor->incompleteDir == NULL)
|
|
{
|
|
ret = false;
|
|
}
|
|
else
|
|
{
|
|
*setmeIncompleteDir = ctor->incompleteDir;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
bool tr_ctorGetMetainfo(tr_ctor const* ctor, tr_variant const** setme)
|
|
{
|
|
bool ret = true;
|
|
|
|
if (!ctor->isSet_metainfo)
|
|
{
|
|
ret = false;
|
|
}
|
|
else if (setme != NULL)
|
|
{
|
|
*setme = &ctor->metainfo;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
tr_session* tr_ctorGetSession(tr_ctor const* ctor)
|
|
{
|
|
return (tr_session*)ctor->session;
|
|
}
|
|
|
|
/***
|
|
****
|
|
***/
|
|
|
|
static bool isPriority(int i)
|
|
{
|
|
return i == TR_PRI_LOW || i == TR_PRI_NORMAL || i == TR_PRI_HIGH;
|
|
}
|
|
|
|
void tr_ctorSetBandwidthPriority(tr_ctor* ctor, tr_priority_t priority)
|
|
{
|
|
if (isPriority(priority))
|
|
{
|
|
ctor->bandwidthPriority = priority;
|
|
}
|
|
}
|
|
|
|
tr_priority_t tr_ctorGetBandwidthPriority(tr_ctor const* ctor)
|
|
{
|
|
return ctor->bandwidthPriority;
|
|
}
|
|
|
|
/***
|
|
****
|
|
***/
|
|
|
|
tr_ctor* tr_ctorNew(tr_session const* session)
|
|
{
|
|
tr_ctor* ctor = tr_new0(struct tr_ctor, 1);
|
|
|
|
ctor->session = session;
|
|
ctor->bandwidthPriority = TR_PRI_NORMAL;
|
|
|
|
if (session != NULL)
|
|
{
|
|
tr_ctorSetDeleteSource(ctor, tr_sessionGetDeleteSource(session));
|
|
tr_ctorSetPaused(ctor, TR_FALLBACK, tr_sessionGetPaused(session));
|
|
tr_ctorSetPeerLimit(ctor, TR_FALLBACK, session->peerLimitPerTorrent);
|
|
tr_ctorSetDownloadDir(ctor, TR_FALLBACK, tr_sessionGetDownloadDir(session));
|
|
}
|
|
|
|
tr_ctorSetSave(ctor, true);
|
|
return ctor;
|
|
}
|
|
|
|
void tr_ctorFree(tr_ctor* ctor)
|
|
{
|
|
clearMetainfo(ctor);
|
|
tr_free(ctor->optionalArgs[1].downloadDir);
|
|
tr_free(ctor->optionalArgs[0].downloadDir);
|
|
tr_free(ctor->incompleteDir);
|
|
tr_free(ctor->want);
|
|
tr_free(ctor->notWant);
|
|
tr_free(ctor->low);
|
|
tr_free(ctor->high);
|
|
tr_free(ctor->normal);
|
|
tr_free(ctor);
|
|
}
|