feat: default public trackers

This commit is contained in:
Stefan Talpalaru 2022-02-20 11:54:20 -06:00 committed by Charles Kerr
parent f436d742a2
commit 7b377511a9
19 changed files with 265 additions and 3 deletions

View File

@ -94,7 +94,7 @@ static struct event_base* ev_base = nullptr;
**** Config File
***/
static auto constexpr Options = std::array<tr_option, 43>{
static auto constexpr Options = std::array<tr_option, 44>{
{ { 'a', "allowed", "Allowed IP addresses. (Default: " TR_DEFAULT_RPC_WHITELIST ")", "a", true, "<list>" },
{ 'b', "blocklist", "Enable peer blocklists", "b", false, nullptr },
{ 'B', "no-blocklist", "Disable peer blocklists", "B", false, nullptr },
@ -103,6 +103,7 @@ static auto constexpr Options = std::array<tr_option, 43>{
{ 941, "incomplete-dir", "Where to store new torrents until they're complete", nullptr, true, "<directory>" },
{ 942, "no-incomplete-dir", "Don't store incomplete torrents in a different location", nullptr, false, nullptr },
{ 'd', "dump-settings", "Dump the settings and exit", "d", false, nullptr },
{ 943, "default-trackers", "Trackers for public torrents to use automatically", nullptr, true, "<list>" },
{ 'e', "logfile", "Dump the log messages to this filename", "e", true, "<filename>" },
{ 'f', "foreground", "Run in the foreground instead of daemonizing", "f", false, nullptr },
{ 'g', "config-dir", "Where to look for configuration files", "g", true, "<path>" },
@ -416,6 +417,10 @@ static bool parse_args(
tr_variantDictAddBool(settings, TR_KEY_incomplete_dir_enabled, false);
break;
case 943:
tr_variantDictAddStr(settings, TR_KEY_default_trackers, optstr);
break;
case 'd':
*dump_settings = true;
break;

View File

@ -1022,6 +1022,10 @@ void Application::Impl::on_prefs_changed(tr_quark const key)
tr_sessionSetEncryption(tr, static_cast<tr_encryption_mode>(gtr_pref_int_get(key)));
break;
case TR_KEY_default_trackers:
tr_sessionSetDefaultTrackers(tr, gtr_pref_string_get(key).c_str());
break;
case TR_KEY_download_dir:
tr_sessionSetDownloadDir(tr, gtr_pref_string_get(key).c_str());
break;

View File

@ -176,6 +176,33 @@ Gtk::Entry* new_entry(tr_quark const key, Glib::RefPtr<Session> const& core)
return w;
}
void text_buffer_changed_cb(Glib::RefPtr<Gtk::TextBuffer> buffer, tr_quark const key, Glib::RefPtr<Session> const& core)
{
Gtk::TextBuffer::iterator start, end;
buffer->get_bounds(start, end);
core->set_pref(key, buffer->get_text(start, end, FALSE));
}
Gtk::Widget* new_text_view(tr_quark const key, Glib::RefPtr<Session> const& core)
{
auto* w = Gtk::make_managed<Gtk::TextView>();
auto buffer = w->get_buffer();
buffer->set_text(gtr_pref_string_get(key));
/* set up the scrolled window and put the text view in it */
auto* scroll = Gtk::make_managed<Gtk::ScrolledWindow>();
scroll->set_policy(Gtk::PolicyType::POLICY_AUTOMATIC, Gtk::PolicyType::POLICY_AUTOMATIC);
scroll->set_shadow_type(Gtk::ShadowType::SHADOW_IN);
scroll->add(*w);
scroll->set_size_request(-1, 200);
/* signal */
buffer->signal_changed().connect([buffer, key, core]() { text_buffer_changed_cb(buffer, key, core); });
return scroll;
}
void chosen_cb(Gtk::FileChooser* w, tr_quark const key, Glib::RefPtr<Session> const& core)
{
core->set_pref(key, w->get_filename());
@ -1051,6 +1078,11 @@ Gtk::Widget* PrefsDialog::Impl::networkPage()
w->set_tooltip_text(_("LPD is a tool for finding peers on your local network."));
t->add_wide_control(row, *w);
t->add_section_title(row, _("Default Trackers"));
auto tv = new_text_view(TR_KEY_default_trackers, core_);
tv->set_tooltip_text(_("Trackers for public torrents to use automatically"));
t->add_wide_control(row, *tv);
return t;
}

View File

@ -18,7 +18,7 @@ using namespace std::literals;
namespace
{
auto constexpr my_static = std::array<std::string_view, 388>{ ""sv,
auto constexpr my_static = std::array<std::string_view, 389>{ ""sv,
"activeTorrentCount"sv,
"activity-date"sv,
"activityDate"sv,
@ -74,6 +74,7 @@ auto constexpr my_static = std::array<std::string_view, 388>{ ""sv,
"current-stats"sv,
"date"sv,
"dateCreated"sv,
"default-trackers"sv,
"delete-local-data"sv,
"desiredAvailable"sv,
"destination"sv,

View File

@ -77,6 +77,7 @@ enum
TR_KEY_current_stats,
TR_KEY_date,
TR_KEY_dateCreated,
TR_KEY_default_trackers,
TR_KEY_delete_local_data,
TR_KEY_desiredAvailable,
TR_KEY_destination,

View File

@ -1789,6 +1789,11 @@ static char const* sessionSet(
tr_sessionSetQueueStalledEnabled(session, boolVal);
}
if (tr_variantDictFindStrView(args_in, TR_KEY_default_trackers, &sv))
{
session->setDefaultTrackers(sv);
}
if (tr_variantDictFindInt(args_in, TR_KEY_download_queue_size, &i))
{
tr_sessionSetQueueSize(session, TR_DOWN, (int)i);
@ -2071,6 +2076,10 @@ static void addSessionField(tr_session* s, tr_variant* d, tr_quark key)
tr_variantDictAddStr(d, key, tr_sessionGetConfigDir(s));
break;
case TR_KEY_default_trackers:
tr_variantDictAddStr(d, key, s->defaultTrackers());
break;
case TR_KEY_download_dir:
tr_variantDictAddStr(d, key, s->downloadDir());
break;

View File

@ -333,6 +333,7 @@ void tr_sessionGetDefaultSettings(tr_variant* d)
tr_variantDictAddBool(d, TR_KEY_utp_enabled, true);
tr_variantDictAddBool(d, TR_KEY_lpd_enabled, false);
tr_variantDictAddStr(d, TR_KEY_download_dir, tr_getDefaultDownloadDir());
tr_variantDictAddStr(d, TR_KEY_default_trackers, "");
tr_variantDictAddInt(d, TR_KEY_speed_limit_down, 100);
tr_variantDictAddBool(d, TR_KEY_speed_limit_down_enabled, false);
tr_variantDictAddInt(d, TR_KEY_encryption, TR_DEFAULT_ENCRYPTION);
@ -411,6 +412,7 @@ void tr_sessionGetSettings(tr_session const* s, tr_variant* d)
tr_variantDictAddBool(d, TR_KEY_utp_enabled, s->isUTPEnabled);
tr_variantDictAddBool(d, TR_KEY_lpd_enabled, s->isLPDEnabled);
tr_variantDictAddStr(d, TR_KEY_download_dir, tr_sessionGetDownloadDir(s));
tr_variantDictAddStr(d, TR_KEY_default_trackers, s->defaultTrackers());
tr_variantDictAddInt(d, TR_KEY_download_queue_size, tr_sessionGetQueueSize(s, TR_DOWN));
tr_variantDictAddBool(d, TR_KEY_download_queue_enabled, tr_sessionGetQueueEnabled(s, TR_DOWN));
tr_variantDictAddInt(d, TR_KEY_speed_limit_down, tr_sessionGetSpeedLimit_KBps(s, TR_DOWN));
@ -811,6 +813,11 @@ static void sessionSetImpl(void* vdata)
tr_sessionSetCacheLimit_MB(session, i);
}
if (tr_variantDictFindStrView(settings, TR_KEY_default_trackers, &sv))
{
session->setDefaultTrackers(sv);
}
if (tr_variantDictFindInt(settings, TR_KEY_peer_limit_per_torrent, &i))
{
tr_sessionSetPeerLimitPerTorrent(session, i);
@ -2244,6 +2251,53 @@ int tr_sessionGetCacheLimit_MB(tr_session const* session)
****
***/
void tr_session::setDefaultTrackers(std::string_view trackers)
{
/* keep the string */
this->default_trackers_str_ = trackers;
/* clear out the old list entries */
this->defaultTrackersList.clear();
/* build the new list entries */
auto urlStart = std::string::npos;
auto constexpr Delimiters = " ,;\r\n\t"sv;
auto fragment = default_trackers_str_;
while ((urlStart = fragment.find_first_not_of(Delimiters)) != std::string::npos)
{
auto urlEnd = fragment.find_first_of(Delimiters, urlStart);
if (urlEnd == std::string::npos)
{
urlEnd = fragment.size() - 1;
}
else
{
urlEnd -= 1;
}
this->defaultTrackersList.push_back(fragment.substr(urlStart, urlEnd));
if (fragment.size() > (urlEnd + 1))
{
fragment = fragment.substr(urlEnd + 1, fragment.size());
}
else
{
break;
}
}
}
void tr_sessionSetDefaultTrackers(tr_session* session, char const* trackers)
{
TR_ASSERT(tr_isSession(session));
session->setDefaultTrackers(trackers ? trackers : "");
}
/***
****
***/
struct port_forwarding_data
{
bool enabled;

View File

@ -150,6 +150,15 @@ public:
download_dir_ = dir;
}
// default trackers
std::string const& defaultTrackers() const
{
return default_trackers_str_;
}
void setDefaultTrackers(std::string_view trackers);
// incomplete dir
std::string const& incompleteDir() const
@ -387,6 +396,8 @@ public:
std::unique_ptr<tr_rpc_server> rpc_server_;
std::list<std::string> defaultTrackersList;
// One of <netinet/ip.h>'s IPTOS_ values.
// See tr_netTos*() in libtransmission/net.h for more info
// Only session.cc should use this.
@ -398,6 +409,7 @@ private:
std::array<std::string, TR_SCRIPT_N_TYPES> scripts_;
std::string blocklist_url_;
std::string download_dir_;
std::string default_trackers_str_;
std::string incomplete_dir_;
std::string peer_congestion_algorithm_;

View File

@ -817,6 +817,52 @@ static void torrentInit(tr_torrent* tor, tr_ctor const* ctor)
}
}
static void tr_torrentAddDefaultTrackers(tr_torrent* tor)
{
std::list<std::string> trackerURLs = {};
int numExistingTrackers = tr_torrentTrackerCount(tor);
int numNewTrackers = tor->session->defaultTrackersList.size();
if (!numNewTrackers || tor->isPrivate())
{
return;
}
// copy existing tracker URLs
for (int i = 0; i < numExistingTrackers; ++i)
{
auto tracker = tr_torrentTracker(tor, i);
trackerURLs.push_back(tracker.announce);
}
// add the new ones
for (std::string_view url : tor->session->defaultTrackersList)
{
if (tr_urlIsValidTracker(url))
{
// check for duplicates
bool duplicate = false;
for (auto trackerURL : trackerURLs)
{
if (trackerURL == url)
{
duplicate = true;
break;
}
}
if (duplicate)
{
continue;
}
tor->announceList().add(url);
}
}
/* tell the announcer to reload this torrent's tracker list */
tr_announcerResetTorrent(tor->session->announcer, tor);
}
tr_torrent* tr_torrentNew(tr_ctor* ctor, tr_torrent** setme_duplicate_of)
{
TR_ASSERT(ctor != nullptr);
@ -843,6 +889,7 @@ tr_torrent* tr_torrentNew(tr_ctor* ctor, tr_torrent** setme_duplicate_of)
auto* const tor = new tr_torrent{ std::move(metainfo) };
torrentInit(tor, ctor);
tr_torrentAddDefaultTrackers(tor);
return tor;
}

View File

@ -392,6 +392,8 @@ bool tr_sessionIsRPCPasswordEnabled(tr_session const* session);
char const* tr_sessionGetRPCBindAddress(tr_session const* session);
void tr_sessionSetDefaultTrackers(tr_session* session, char const* trackers);
enum tr_rpc_callback_type
{
TR_RPC_TORRENT_ADDED,

View File

@ -109,6 +109,7 @@ std::array<Prefs::PrefItem, Prefs::PREFS_COUNT> const Prefs::Items{
{ ALT_SPEED_LIMIT_TIME_DAY, TR_KEY_alt_speed_time_day, QVariant::Int },
{ BLOCKLIST_ENABLED, TR_KEY_blocklist_enabled, QVariant::Bool },
{ BLOCKLIST_URL, TR_KEY_blocklist_url, QVariant::String },
{ DEFAULT_TRACKERS, TR_KEY_default_trackers, QVariant::String },
{ DSPEED, TR_KEY_speed_limit_down, QVariant::Int },
{ DSPEED_ENABLED, TR_KEY_speed_limit_down_enabled, QVariant::Bool },
{ DOWNLOAD_DIR, TR_KEY_download_dir, QVariant::String },

View File

@ -83,6 +83,7 @@ public:
ALT_SPEED_LIMIT_TIME_DAY,
BLOCKLIST_ENABLED,
BLOCKLIST_URL,
DEFAULT_TRACKERS,
DSPEED,
DSPEED_ENABLED,
DOWNLOAD_DIR,

View File

@ -190,6 +190,10 @@ bool PrefsDialog::updateWidgetValue(QWidget* widget, int pref_key) const
{
pref_widget.as<FreeSpaceLabel>()->setPath(prefs_.getString(pref_key));
}
else if (pref_widget.is<QPlainTextEdit>())
{
pref_widget.as<QPlainTextEdit>()->setPlainText(prefs_.getString(pref_key));
}
else
{
return false;
@ -233,6 +237,14 @@ void PrefsDialog::linkWidgetToPref(QWidget* widget, int pref_key)
if (auto const* spin_box = qobject_cast<QAbstractSpinBox*>(widget); spin_box != nullptr)
{
connect(spin_box, &QAbstractSpinBox::editingFinished, this, &PrefsDialog::spinBoxEditingFinished);
return;
}
auto const* plain_text_edit = qobject_cast<QPlainTextEdit*>(widget);
if (plain_text_edit != nullptr)
{
connect(plain_text_edit, &QPlainTextEdit::textChanged, this, &PrefsDialog::plainTextChanged);
return;
}
}
@ -295,6 +307,22 @@ void PrefsDialog::pathChanged(QString const& path)
}
}
void PrefsDialog::plainTextChanged()
{
PreferenceWidget const pref_widget(sender());
if (pref_widget.is<QPlainTextEdit>())
{
auto const* const plain_text_edit = pref_widget.as<QPlainTextEdit>();
if (plain_text_edit->document()->isModified())
{
prefs_.set(pref_widget.getPrefKey(), plain_text_edit->toPlainText());
// we avoid using setPref() because the included refreshPref() call would reset the cursor while we're editing
}
}
}
/***
****
***/
@ -423,6 +451,7 @@ void PrefsDialog::initNetworkTab()
linkWidgetToPref(ui_.enablePexCheck, Prefs::PEX_ENABLED);
linkWidgetToPref(ui_.enableDhtCheck, Prefs::DHT_ENABLED);
linkWidgetToPref(ui_.enableLpdCheck, Prefs::LPD_ENABLED);
linkWidgetToPref(ui_.defaultTrackersPlainTextEdit, Prefs::DEFAULT_TRACKERS);
auto* cr = new ColumnResizer(this);
cr->addLayout(ui_.incomingPeersSectionLayout);
@ -775,11 +804,15 @@ void PrefsDialog::refreshPref(int key)
{
QWidget* w(it.value());
w->blockSignals(true);
if (!updateWidgetValue(w, key) && (key == Prefs::ENCRYPTION))
{
auto* combo_box = qobject_cast<QComboBox*>(w);
int const index = combo_box->findData(prefs_.getInt(key));
combo_box->setCurrentIndex(index);
}
w->blockSignals(false);
}
}

View File

@ -34,6 +34,7 @@ private slots:
void timeEditingFinished();
void lineEditingFinished();
void pathChanged(QString const& path);
void plainTextChanged();
void refreshPref(int key);
void encryptionEdited(int);
void altSpeedDaysEdited(int);

View File

@ -983,6 +983,26 @@
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="defaultTrackersLabel">
<property name="toolTip">
<string>a list of default trackers to be added to new public torrents (and existing ones, after a reload)</string>
</property>
<property name="text">
<string>Default Trackers:</string>
</property>
</widget>
</item>
<item>
<widget class="QPlainTextEdit" name="defaultTrackersPlainTextEdit">
<property name="lineWrapMode">
<enum>QPlainTextEdit::NoWrap</enum>
</property>
<property name="plainText">
<string notr="true"/>
</property>
</widget>
</item>
</layout>
</item>
<item>

View File

@ -155,6 +155,7 @@ void Session::updatePref(int key)
case Prefs::BLOCKLIST_DATE:
case Prefs::BLOCKLIST_ENABLED:
case Prefs::BLOCKLIST_URL:
case Prefs::DEFAULT_TRACKERS:
case Prefs::DHT_ENABLED:
case Prefs::DOWNLOAD_QUEUE_ENABLED:
case Prefs::DOWNLOAD_QUEUE_SIZE:

View File

@ -94,7 +94,7 @@ TEST_F(RpcTest, sessionGet)
EXPECT_TRUE(tr_variantDictFindDict(&response, TR_KEY_arguments, &args));
// what we expected
auto const expected_keys = std::array<tr_quark, 57>{
auto const expected_keys = std::array<tr_quark, 58>{
TR_KEY_alt_speed_down,
TR_KEY_alt_speed_enabled,
TR_KEY_alt_speed_time_begin,
@ -109,6 +109,7 @@ TEST_F(RpcTest, sessionGet)
TR_KEY_blocklist_url,
TR_KEY_cache_size_mb,
TR_KEY_config_dir,
TR_KEY_default_trackers,
TR_KEY_dht_enabled,
TR_KEY_download_dir,
TR_KEY_download_dir_free_space,

View File

@ -63,6 +63,10 @@ export class PrefsDialog extends EventTarget {
}
static _getValue(e) {
if (e.tagName === 'TEXTAREA') {
return e.value;
}
switch (e.type) {
case 'checkbox':
case 'radio':
@ -118,6 +122,15 @@ export class PrefsDialog extends EventTarget {
const n = Formatter.number(value);
element.innerHTML = `Blocklist has <span class="blocklist-size-number">${n}</span> rules`;
setTextContent(this.elements.peers.blocklist_update_button, 'Update');
} else if (element.tagName === 'TEXTAREA') {
if (
// eslint-disable-next-line eqeqeq
element.value != value &&
element !== document.activeElement
) {
element.value = value;
element.dispatchEvent(new Event('change'));
}
} else {
switch (element.type) {
case 'checkbox':
@ -646,7 +659,25 @@ export class PrefsDialog extends EventTarget {
root.append(cal.root);
const utp_check = cal.check;
label = document.createElement('div');
label.textContent = 'Default trackers';
label.classList.add('section-label');
root.append(label);
label = document.createElement('label');
label.textContent =
'(added to new public torrents and to existing ones on reload):';
root.append(label);
const textarea = document.createElement('textarea');
textarea.dataset.key = 'default-trackers';
textarea.id = 'default-trackers';
label.setAttribute('for', textarea.id);
root.append(textarea);
const default_trackers_textarea = textarea;
return {
default_trackers_textarea,
port_forwarding_check,
port_input,
port_status_label,
@ -714,6 +745,8 @@ export class PrefsDialog extends EventTarget {
console.trace(`unhandled input: ${element.type}`);
break;
}
} else if (element.tagName === 'TEXTAREA') {
element.addEventListener('change', on_change);
}
}
};

View File

@ -762,6 +762,10 @@ $popup-top: 61px; // TODO: ugly that this is hardcoded
grid-column: span 2;
}
#default-trackers {
height: 300px;
}
.blocklist-size-label,
.blocklist-update-button,
.port-status {