* Fixed a regression that threw an exception for torrents with

empty directories.

* When receiving zero length pieces, remove them from the request list.

* Send the interested state before any requests as some clients,
BitTornado 0.7.14 and uTorrent 0.3.0, disconnect if a request has been
received while uninterested.

* WITH_POSIX_FALLOCATE configure macro should check against "yes", not
"no".

* The new priority queue would cause arg torrents to load before
session torrents in some cases. Fixed to perform the session torrent
tasks before loading arg torrents.

* Allow '*' wildcard in filenames when loading torrents.

* Added load, load_run, stop_tied and remove_tied settings which can
be used to scan a directory for new/removed torrents. These torrents
are tied to the lifetime of the torrent file.


git-svn-id: svn://rakshasa.no/libtorrent/trunk/rtorrent@616 e378c898-3ddf-0310-93e7-cc216c733640
This commit is contained in:
rakshasa
2006-01-03 21:43:01 +00:00
parent 5a8a402745
commit 4431fd1652
20 changed files with 519 additions and 58 deletions
+5
View File
@@ -73,6 +73,9 @@ public:
void set_connection_leech(const std::string& name) { m_connectionLeech = string_to_connection_type(name); }
void set_connection_seed(const std::string& name) { m_connectionSeed = string_to_connection_type(name); }
const std::string& tied_to_file() const { return m_tiedToFile; }
void set_tied_to_file(const std::string& str) { m_tiedToFile = str; }
void enable_udp_trackers(bool state);
// Helper functions for calling functions in torrent::Download
@@ -104,6 +107,8 @@ private:
ConnType m_connectionLeech;
ConnType m_connectionSeed;
std::string m_tiedToFile;
sigc::connection m_connTrackerSucceded;
sigc::connection m_connTrackerFailed;
sigc::connection m_connStorageError;
+31 -11
View File
@@ -40,6 +40,7 @@
#include <sstream>
#include <stdexcept>
#include <torrent/bencode.h>
#include <torrent/exceptions.h>
#include "curl_get.h"
#include "http_queue.h"
@@ -58,7 +59,9 @@ DownloadFactory::DownloadFactory(const std::string& uri, Manager* m) :
m_uri(uri),
m_session(false),
m_start(false) {
m_start(false),
m_printLog(true),
m_tiedToFile(false) {
m_taskLoad.set_slot(rak::mem_fn(this, &DownloadFactory::receive_load));
m_taskCommit.set_slot(rak::mem_fn(this, &DownloadFactory::receive_commit));
@@ -85,7 +88,7 @@ DownloadFactory::commit() {
void
DownloadFactory::receive_load() {
if (m_stream)
throw std::logic_error("DownloadFactory::load() called on an object with m_stream != NULL");
throw torrent::client_error("DownloadFactory::load() called on an object with m_stream != NULL");
if (std::strncmp(m_uri.c_str(), "http://", 7) == 0) {
// Http handling here.
@@ -95,6 +98,8 @@ DownloadFactory::receive_load() {
(*itr)->signal_done().slots().push_front(sigc::mem_fun(*this, &DownloadFactory::receive_loaded));
(*itr)->signal_failed().slots().push_front(sigc::mem_fun(*this, &DownloadFactory::receive_failed));
m_tiedToFile = false;
} else {
m_stream = new std::fstream(m_uri.c_str(), std::ios::in);
@@ -124,9 +129,9 @@ DownloadFactory::receive_commit() {
void
DownloadFactory::receive_success() {
if (m_stream == NULL)
throw std::logic_error("DownloadFactory::receive_success() called on an object with m_stream == NULL");
throw torrent::client_error("DownloadFactory::receive_success() called on an object with m_stream == NULL");
Manager::DListItr itr = m_manager->insert(m_stream);
Manager::DListItr itr = m_manager->insert(m_stream, m_printLog);
if (itr == m_manager->get_download_list().end()) {
// core::Manager should already have added the error message to
@@ -135,15 +140,28 @@ DownloadFactory::receive_success() {
return;
}
torrent::Bencode& bencode = (*itr)->get_bencode();
if (m_session) {
torrent::Bencode& bencode = (*itr)->get_bencode();
// Hmm... this safe?
if (bencode.get_key("rtorrent").get_key("state").as_string() == "started")
m_manager->start(*itr);
m_manager->start(*itr, m_printLog);
if (bencode.get_key("rtorrent").has_key("tied") &&
bencode.get_key("rtorrent").get_key("tied").is_string())
(*itr)->set_tied_to_file(bencode.get_key("rtorrent").get_key("tied").as_string());
} else {
// Remove the settings if this isn't a session torrent.
//bencode.erase_key("rtorrent");
if (m_tiedToFile) {
(*itr)->set_tied_to_file(m_uri);
bencode.get_key("rtorrent").insert_key("tied", m_uri);
}
if (m_start)
m_manager->start(*itr);
m_manager->start(*itr, m_printLog);
m_manager->get_download_store().save(*itr);
}
@@ -154,11 +172,13 @@ DownloadFactory::receive_success() {
void
DownloadFactory::receive_failed(const std::string& msg) {
if (m_stream == NULL)
throw std::logic_error("DownloadFactory::receive_success() called on an object with m_stream == NULL");
throw torrent::client_error("DownloadFactory::receive_success() called on an object with m_stream == NULL");
// Add message to log.
m_manager->get_log_important().push_front(msg + ": \"" + m_uri + "\"");
m_manager->get_log_complete().push_front(msg + ": \"" + m_uri + "\"");
if (m_printLog) {
m_manager->get_log_important().push_front(msg + ": \"" + m_uri + "\"");
m_manager->get_log_complete().push_front(msg + ": \"" + m_uri + "\"");
}
m_slotFinished();
}
+12
View File
@@ -34,6 +34,10 @@
// Skomakerveien 33
// 3185 Skoppum, NORWAY
// The DownloadFactory class assures that loading torrents can be done
// anywhere in the code by queueing the task. The user may change
// settings while, or even after, the torrent is loading.
#ifndef RTORRENT_CORE_DOWNLOAD_FACTORY_H
#define RTORRENT_CORE_DOWNLOAD_FACTORY_H
@@ -67,6 +71,12 @@ public:
bool get_start() const { return m_start; }
void set_start(bool v) { m_start = v; }
bool print_log() const { return m_printLog; }
void set_print_log(bool v) { m_printLog = v; }
bool tied_to_file() const { return m_tiedToFile; }
void set_tied_to_file(bool v) { m_tiedToFile = v; }
void slot_finished(Slot s) { m_slotFinished = s; }
private:
@@ -85,6 +95,8 @@ private:
std::string m_uri;
bool m_session;
bool m_start;
bool m_printLog;
bool m_tiedToFile;
Slot m_slotFinished;
rak::priority_item m_taskLoad;
+3
View File
@@ -46,6 +46,9 @@ namespace core {
void
Log::push_front(const std::string& msg) {
if (!m_enabled)
return;
Base::push_front(Type(cachedTime, msg));
if (size() > 50)
+9
View File
@@ -64,6 +64,13 @@ public:
using Base::empty;
using Base::size;
Log() : m_enabled(true) {}
bool is_enabled() const { return m_enabled; }
void enable() { m_enabled = true; }
void disable() { m_enabled = false; }
void push_front(const std::string& msg);
iterator find_older(rak::timer t);
@@ -71,6 +78,8 @@ public:
Signal& signal_update() { return m_signalUpdate; }
private:
bool m_enabled;
Signal m_signalUpdate;
};
+83 -6
View File
@@ -43,6 +43,8 @@
#include <istream>
#include <unistd.h>
#include <sys/select.h>
#include <rak/regex.h>
#include <rak/string_manip.h>
#include <sigc++/bind.h>
#include <sigc++/hide.h>
#include <torrent/bencode.h>
@@ -50,6 +52,7 @@
#include "curl_get.h"
#include "download.h"
#include "download_factory.h"
#include "manager.h"
#include "poll_manager_epoll.h"
#include "poll_manager_select.h"
@@ -149,13 +152,15 @@ Manager::shutdown(bool force) {
}
Manager::DListItr
Manager::insert(std::istream* s) {
Manager::insert(std::istream* s, bool printLog) {
try {
return m_downloadList.insert(s);
} catch (torrent::local_error& e) {
m_logImportant.push_front(e.what());
m_logComplete.push_front(e.what());
if (printLog) {
m_logImportant.push_front(e.what());
m_logComplete.push_front(e.what());
}
return m_downloadList.end();
}
@@ -173,7 +178,7 @@ Manager::erase(DListItr itr) {
}
void
Manager::start(Download* d) {
Manager::start(Download* d, bool printLog) {
try {
d->get_bencode().get_key("rtorrent").get_key("state") = "started";
@@ -190,8 +195,10 @@ Manager::start(Download* d) {
m_hashQueue.insert(d, sigc::bind(sigc::mem_fun(m_downloadList, &DownloadList::start), d));
} catch (torrent::local_error& e) {
m_logImportant.push_front(e.what());
m_logComplete.push_front(e.what());
if (printLog) {
m_logImportant.push_front(e.what());
m_logComplete.push_front(e.what());
}
}
}
@@ -302,4 +309,74 @@ Manager::receive_download_done_hash_checked(Download* d) {
d->get_download().tracker_send_completed();
}
void
Manager::try_create_download(const std::string& uri, bool start, bool printLog, bool tied) {
// Adding download.
DownloadFactory* f = new DownloadFactory(uri, this);
f->set_start(start);
f->set_print_log(printLog);
f->set_tied_to_file(tied);
f->slot_finished(sigc::bind(sigc::ptr_fun(&rak::call_delete_func<core::DownloadFactory>), f));
f->load();
f->commit();
}
// Move this somewhere better.
void
path_expand(std::vector<std::string>* paths, const std::string& pattern) {
std::vector<utils::Directory> currentCache;
std::vector<utils::Directory> nextCache;
rak::split_iterator_t<std::string> first = rak::split_iterator(pattern, '/');
rak::split_iterator_t<std::string> last = rak::split_iterator(pattern);
if (first == last)
return;
// Check for initial '/' that indicates the root.
if ((*first).empty()) {
currentCache.push_back(utils::Directory("/"));
++first;
} else {
currentCache.push_back(utils::Directory("./"));
}
// Might be an idea to use depth-first search instead.
for (; first != last; ++first) {
rak::regex r(*first);
if (r.pattern().empty())
continue;
for (std::vector<utils::Directory>::iterator itr = currentCache.begin(); itr != currentCache.end(); ++itr) {
itr->update(false);
itr->erase(std::remove_if(itr->begin(), itr->end(), std::not1(r)), itr->end());
std::transform(itr->begin(), itr->end(), std::back_inserter(nextCache), std::bind1st(std::plus<std::string>(), itr->get_path() + "/"));
}
currentCache.clear();
currentCache.swap(nextCache);
}
std::transform(currentCache.begin(), currentCache.end(), std::back_inserter(*paths), std::mem_fun_ref(&utils::Directory::get_path));
}
void
Manager::try_create_download_expand(const std::string& uri, bool start, bool printLog, bool tied) {
std::vector<std::string> paths;
paths.reserve(32);
path_expand(&paths, uri);
if (!paths.empty())
for (std::vector<std::string>::iterator itr = paths.begin(); itr != paths.end(); ++itr)
try_create_download(*itr, start, printLog, tied);
else
try_create_download(uri, start, printLog, tied);
}
}
+6 -2
View File
@@ -83,16 +83,20 @@ public:
void shutdown(bool force);
DListItr insert(std::istream* s);
DListItr insert(std::istream* s, bool printLog = true);
DListItr erase(DListItr itr);
void start(Download* d);
void start(Download* d, bool printLog = true);
void stop(Download* d);
void check_hash(Download* d);
void push_log(const std::string& msg) { m_logImportant.push_front(msg); m_logComplete.push_front(msg); }
// Temporary, find a better place for this.
void try_create_download(const std::string& uri, bool start, bool printLog = true, bool tied = false);
void try_create_download_expand(const std::string& uri, bool start, bool printLog = true, bool tied = false);
private:
void create_http(const std::string& uri);
void create_final(std::istream* s);
+2
View File
@@ -83,6 +83,8 @@ WindowPeerInfo::redraw() {
core::Download::connection_type_to_string(m_download->get_connection_current()),
core::Download::connection_type_to_string(m_download->get_connection_leech()),
core::Download::connection_type_to_string(m_download->get_connection_seed()));
m_canvas->print(0, y++, "Tied to file: %s",
m_download->tied_to_file().c_str());
y++;
+14 -24
View File
@@ -143,6 +143,11 @@ initialize_option_handler(Control* c, OptionHandler* optionHandler) {
optionHandler->insert("connection_leech", new OptionHandlerString(c, &apply_connection_leech));
optionHandler->insert("connection_seed", new OptionHandlerString(c, &apply_connection_seed));
optionHandler->insert("load", new OptionHandlerString(c, &apply_load));
optionHandler->insert("load_run", new OptionHandlerString(c, &apply_load_run));
optionHandler->insert("stop_untied", new OptionHandlerString(c, &apply_stop_untied));
optionHandler->insert("remove_untied", new OptionHandlerString(c, &apply_remove_untied));
optionHandler->insert("session", new OptionHandlerString(c, &apply_session_directory));
optionHandler->insert("encoding_list", new OptionHandlerString(c, &apply_encoding_list));
optionHandler->insert("tracker_dump", new OptionHandlerString(c, &apply_tracker_dump));
@@ -220,15 +225,15 @@ main(int argc, char** argv) {
// Just to make sure we did all the stuff on the queue before
// loading any torrents.
while (!taskScheduler.empty() && taskScheduler.top()->time() <= cachedTime) {
rak::priority_item* v = taskScheduler.top();
taskScheduler.pop();
v->clear_time();
v->call();
}
//
// Remove this?
//rak::priority_queue_perform(&taskScheduler, cachedTime);
// Load session torrents and perform scheduled tasks to ensure
// session torrents are loaded before arg torrents.
load_session_torrents(&control);
rak::priority_queue_perform(&taskScheduler, cachedTime);
load_arg_torrents(&control, argv + firstArg, argv + argc);
control.display()->adjust_layout();
@@ -237,22 +242,7 @@ main(int argc, char** argv) {
countTicks++;
cachedTime = rak::timer::current();
// std::list<rak::priority_item*> workQueue;
// std::copy(rak::queue_popper(taskScheduler, rak::bind2nd(std::mem_fun(&rak::priority_item::compare), cachedTime)),
// rak::queue_popper(taskScheduler, rak::bind2nd(std::mem_fun(&rak::priority_item::compare), rak::timer())),
// std::back_inserter(workQueue));
// std::for_each(workQueue.begin(), workQueue.end(), std::mem_fun(&rak::priority_item::clear_time));
// std::for_each(workQueue.begin(), workQueue.end(), std::mem_fun(&rak::priority_item::call));
while (!taskScheduler.empty() && taskScheduler.top()->time() <= cachedTime) {
rak::priority_item* v = taskScheduler.top();
taskScheduler.pop();
v->clear_time();
v->call();
}
rak::priority_queue_perform(&taskScheduler, cachedTime);
// This needs to be called every second or so. Currently done by
// the throttle task in libtorrent.
@@ -268,7 +258,7 @@ main(int argc, char** argv) {
} catch (torrent::base_error& e) {
display::Canvas::cleanup();
std::cout << "Caught exception from libtorrent: " << e.what() << std::endl;
std::cout << "Caught exception: " << e.what() << std::endl;
return -1;
} catch (std::exception& e) {
+56
View File
@@ -37,17 +37,21 @@
#include "config.h"
#include <cstdio>
#include <functional>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <rak/functional.h>
#include <rak/string_manip.h>
#include <torrent/bencode.h>
#include <torrent/exceptions.h>
#include <torrent/torrent.h>
#include "core/manager.h"
#include "ui/root.h"
#include "utils/directory.h"
#include "utils/file_stat.h"
#include "control.h"
#include "option_handler_rules.h"
@@ -218,6 +222,58 @@ apply_http_proxy(Control* m, const std::string& arg) {
m->core()->get_poll_manager()->get_http_stack()->set_http_proxy(arg);
}
void
apply_load(Control* m, const std::string& arg) {
m->core()->try_create_download_expand(arg, false, false, true);
}
void
apply_load_run(Control* m, const std::string& arg) {
m->core()->try_create_download_expand(arg, true, false, true);
}
void
apply_stop_untied(Control* m, const std::string& arg) {
core::Manager::DListItr itr = m->core()->get_download_list().begin();
while ((itr = std::find_if(itr, m->core()->get_download_list().end(),
rak::on(std::mem_fun(&core::Download::tied_to_file), std::not1(std::mem_fun_ref(&std::string::empty)))))
!= m->core()->get_download_list().end()) {
utils::FileStat fs;
if (fs.update((*itr)->tied_to_file().c_str()) != 0) {
(*itr)->set_tied_to_file(std::string());
(*itr)->get_bencode().get_key("rtorrent").erase_key("tied");
m->core()->stop(*itr);
}
++itr;
}
}
void
apply_remove_untied(Control* m, const std::string& arg) {
core::Manager::DListItr itr = m->core()->get_download_list().begin();
while ((itr = std::find_if(itr, m->core()->get_download_list().end(),
rak::on(std::mem_fun(&core::Download::tied_to_file), std::not1(std::mem_fun_ref(&std::string::empty)))))
!= m->core()->get_download_list().end()) {
utils::FileStat fs;
if (fs.update((*itr)->tied_to_file().c_str()) != 0) {
(*itr)->set_tied_to_file(std::string());
(*itr)->get_bencode().get_key("rtorrent").erase_key("tied");
m->core()->stop(*itr);
itr = m->core()->erase(itr);
} else {
++itr;
}
}
}
void
apply_session_directory(Control* m, const std::string& arg) {
m->core()->get_download_store().use(arg);
+5
View File
@@ -77,6 +77,11 @@ void apply_check_hash(Control* m, const std::string& arg);
void apply_http_proxy(Control* m, const std::string& arg);
void apply_load(Control* m, const std::string& arg);
void apply_load_run(Control* m, const std::string& arg);
void apply_stop_untied(Control* m, const std::string& arg);
void apply_remove_untied(Control* m, const std::string& arg);
void apply_session_directory(Control* m, const std::string& arg);
void apply_encoding_list(Control* m, const std::string& arg);
+2 -9
View File
@@ -38,12 +38,12 @@
#include <stdexcept>
#include <rak/functional.h>
#include <rak/string_manip.h>
#include <sigc++/bind.h>
#include <sigc++/hide.h>
#include <torrent/torrent.h>
#include "core/download.h"
#include "core/download_factory.h"
#include "core/manager.h"
#include "input/bindings.h"
@@ -265,16 +265,9 @@ DownloadList::receive_exit_input(bool useDefault) {
m_control->ui()->window_statusbar()->set_active(true);
m_windowTextInput->set_active(false);
m_control->input()->set_text_input();
// Adding download.
core::DownloadFactory* f = new core::DownloadFactory(m_windowTextInput->get_input()->str(), m_control->core());
f->set_start(useDefault);
f->slot_finished(sigc::bind(sigc::ptr_fun(&rak::call_delete_func<core::DownloadFactory>), f));
f->load();
f->commit();
m_control->core()->try_create_download_expand(m_windowTextInput->get_input()->str(), useDefault);
// Clean up.
m_windowTextInput->get_input()->clear();
+2 -2
View File
@@ -57,7 +57,7 @@ Directory::is_valid() const {
}
bool
Directory::update() {
Directory::update(bool hideDot) {
if (m_path.empty())
throw std::logic_error("Directory::update() tried to open an empty path");
@@ -71,7 +71,7 @@ Directory::update() {
while ((ent = readdir(d)) != NULL) {
std::string de(ent->d_name);
if (!de.empty() && de[0] != '.')
if (!de.empty() && (!hideDot || de[0] != '.'))
Base::push_back(ent->d_name);
}
+1 -1
View File
@@ -66,7 +66,7 @@ public:
bool is_valid() const;
bool update();
bool update(bool hideDot = true);
const std::string& get_path() { return m_path; }