mirror of
https://github.com/rakshasa/rtorrent.git
synced 2026-08-12 21:22:31 +00:00
* 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:
@@ -10,6 +10,7 @@ EXTRA_DIST= \
|
||||
rak/functional_fun.h \
|
||||
rak/priority_queue.h \
|
||||
rak/priority_queue_default.h \
|
||||
rak/regex.h
|
||||
rak/string_manip.h \
|
||||
rak/timer.h \
|
||||
rak/unordered_vector.h \
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
AC_INIT(rtorrent, 0.4.1, jaris@ifi.uio.no)
|
||||
AC_INIT(rtorrent, 0.4.2, jaris@ifi.uio.no)
|
||||
|
||||
AM_INIT_AUTOMAKE
|
||||
AM_CONFIG_HEADER(config.h)
|
||||
|
||||
@@ -85,6 +85,17 @@ struct priority_compare {
|
||||
typedef std::equal_to<priority_item*> priority_equal;
|
||||
typedef priority_queue<priority_item*, priority_compare, priority_equal> priority_queue_default;
|
||||
|
||||
inline void
|
||||
priority_queue_perform(priority_queue_default* queue, timer t) {
|
||||
while (!queue->empty() && queue->top()->time() <= t) {
|
||||
priority_item* v = queue->top();
|
||||
queue->pop();
|
||||
|
||||
v->clear_time();
|
||||
v->call();
|
||||
}
|
||||
}
|
||||
|
||||
inline void
|
||||
priority_queue_insert(priority_queue_default* queue, priority_item* item, timer t) {
|
||||
if (t == timer())
|
||||
|
||||
+220
@@ -0,0 +1,220 @@
|
||||
// rak - Rakshasa's toolbox
|
||||
// Copyright (C) 2005, Jari Sundell
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
//
|
||||
// In addition, as a special exception, the copyright holders give
|
||||
// permission to link the code of portions of this program with the
|
||||
// OpenSSL library under certain conditions as described in each
|
||||
// individual source file, and distribute linked combinations
|
||||
// including the two.
|
||||
//
|
||||
// You must obey the GNU General Public License in all respects for
|
||||
// all of the code used other than OpenSSL. If you modify file(s)
|
||||
// with this exception, you may extend this exception to your version
|
||||
// of the file(s), but you are not obligated to do so. If you do not
|
||||
// wish to do so, delete this exception statement from your version.
|
||||
// If you delete this exception statement from all source files in the
|
||||
// program, then also delete it here.
|
||||
//
|
||||
// Contact: Jari Sundell <jaris@ifi.uio.no>
|
||||
//
|
||||
// Skomakerveien 33
|
||||
// 3185 Skoppum, NORWAY
|
||||
|
||||
// This is a hacked up whole string pattern matching. Replace with
|
||||
// TR1's regex when that becomes widely available. It is intended for
|
||||
// small strings.
|
||||
|
||||
#ifndef RAK_REGEX_H
|
||||
#define RAK_REGEX_H
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <list>
|
||||
|
||||
namespace rak {
|
||||
|
||||
class regex : public std::unary_function<std::string, bool> {
|
||||
public:
|
||||
regex() {}
|
||||
regex(const std::string& p) : m_pattern(p) {}
|
||||
|
||||
const std::string& pattern() const { return m_pattern; }
|
||||
|
||||
bool operator () (const std::string& p) const;
|
||||
|
||||
private:
|
||||
std::string m_pattern;
|
||||
};
|
||||
|
||||
// This isn't optimized, or very clean. A simple hack that should work.
|
||||
bool
|
||||
regex::operator () (const std::string& text) const {
|
||||
if (m_pattern.empty() ||
|
||||
text.empty() ||
|
||||
(m_pattern[0] != '*' && m_pattern[0] != text[0]))
|
||||
return false;
|
||||
|
||||
// Replace with unordered_vector?
|
||||
std::list<unsigned int> paths;
|
||||
paths.push_front(0);
|
||||
|
||||
for (std::string::const_iterator itrText = ++text.begin(), lastText = text.end(); itrText != lastText; ++itrText) {
|
||||
|
||||
for (std::list<unsigned int>::iterator itrPaths = paths.begin(), lastPaths = paths.end(); itrPaths != lastPaths; ) {
|
||||
|
||||
unsigned int next = *itrPaths + 1;
|
||||
|
||||
if (m_pattern[*itrPaths] != '*')
|
||||
itrPaths = paths.erase(itrPaths);
|
||||
else
|
||||
itrPaths++;
|
||||
|
||||
// When we reach the end of 'm_pattern', we don't have a whole
|
||||
// match of 'text'.
|
||||
if (next == m_pattern.size())
|
||||
continue;
|
||||
|
||||
// Push to the back so that '*' will match zero length strings.
|
||||
if (m_pattern[next] == '*')
|
||||
paths.push_back(next);
|
||||
|
||||
if (m_pattern[next] == *itrText)
|
||||
paths.push_front(next);
|
||||
}
|
||||
|
||||
if (paths.empty())
|
||||
return false;
|
||||
}
|
||||
|
||||
return std::find(paths.begin(), paths.end(), m_pattern.size() - 1) != paths.end();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
// rak - Rakshasa's toolbox
|
||||
// Copyright (C) 2005, Jari Sundell
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
//
|
||||
// In addition, as a special exception, the copyright holders give
|
||||
// permission to link the code of portions of this program with the
|
||||
// OpenSSL library under certain conditions as described in each
|
||||
// individual source file, and distribute linked combinations
|
||||
// including the two.
|
||||
//
|
||||
// You must obey the GNU General Public License in all respects for
|
||||
// all of the code used other than OpenSSL. If you modify file(s)
|
||||
// with this exception, you may extend this exception to your version
|
||||
// of the file(s), but you are not obligated to do so. If you do not
|
||||
// wish to do so, delete this exception statement from your version.
|
||||
// If you delete this exception statement from all source files in the
|
||||
// program, then also delete it here.
|
||||
//
|
||||
// Contact: Jari Sundell <jaris@ifi.uio.no>
|
||||
//
|
||||
// Skomakerveien 33
|
||||
// 3185 Skoppum, NORWAY
|
||||
|
||||
// This is a hacked up whole string pattern matching. Replace with
|
||||
// TR1's regex when that becomes widely available. It is intended for
|
||||
// small strings.
|
||||
|
||||
#ifndef RAK_REGEX_H
|
||||
#define RAK_REGEX_H
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <list>
|
||||
|
||||
namespace rak {
|
||||
|
||||
class regex : public std::unary_function<std::string, bool> {
|
||||
public:
|
||||
regex() {}
|
||||
regex(const std::string& p) : m_pattern(p) {}
|
||||
|
||||
const std::string& pattern() const { return m_pattern; }
|
||||
|
||||
bool operator () (const std::string& p) const;
|
||||
|
||||
private:
|
||||
std::string m_pattern;
|
||||
};
|
||||
|
||||
// This isn't optimized, or very clean. A simple hack that should work.
|
||||
bool
|
||||
regex::operator () (const std::string& text) const {
|
||||
if (m_pattern.empty() ||
|
||||
text.empty() ||
|
||||
(m_pattern[0] != '*' && m_pattern[0] != text[0]))
|
||||
return false;
|
||||
|
||||
// Replace with unordered_vector?
|
||||
std::list<unsigned int> paths;
|
||||
paths.push_front(0);
|
||||
|
||||
for (std::string::const_iterator itrText = ++text.begin(), lastText = text.end(); itrText != lastText; ++itrText) {
|
||||
|
||||
for (std::list<unsigned int>::iterator itrPaths = paths.begin(), lastPaths = paths.end(); itrPaths != lastPaths; ) {
|
||||
|
||||
unsigned int next = *itrPaths + 1;
|
||||
|
||||
if (m_pattern[*itrPaths] != '*')
|
||||
itrPaths = paths.erase(itrPaths);
|
||||
else
|
||||
itrPaths++;
|
||||
|
||||
// When we reach the end of 'm_pattern', we don't have a whole
|
||||
// match of 'text'.
|
||||
if (next == m_pattern.size())
|
||||
continue;
|
||||
|
||||
// Push to the back so that '*' will match zero length strings.
|
||||
if (m_pattern[next] == '*')
|
||||
paths.push_back(next);
|
||||
|
||||
if (m_pattern[next] == *itrText)
|
||||
paths.push_front(next);
|
||||
}
|
||||
|
||||
if (paths.empty())
|
||||
return false;
|
||||
}
|
||||
|
||||
return std::find(paths.begin(), paths.end(), m_pattern.size() - 1) != paths.end();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -37,6 +37,7 @@
|
||||
#ifndef RAK_STRING_MANIP_H
|
||||
#define RAK_STRING_MANIP_H
|
||||
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
|
||||
namespace rak {
|
||||
@@ -74,6 +75,58 @@ Sequence trim(const Sequence& seq) {
|
||||
return trim_begin(trim_end(seq));
|
||||
}
|
||||
|
||||
// Consider rewritting such that m_seq is replaced by first/last.
|
||||
template <typename Sequence>
|
||||
class split_iterator_t {
|
||||
public:
|
||||
typedef typename Sequence::const_iterator const_iterator;
|
||||
typedef typename Sequence::value_type value_type;
|
||||
|
||||
split_iterator_t() {}
|
||||
|
||||
split_iterator_t(const Sequence& seq, value_type delim) :
|
||||
m_seq(&seq),
|
||||
m_delim(delim),
|
||||
m_pos(seq.begin()),
|
||||
m_next(std::find(seq.begin(), seq.end(), delim)) {
|
||||
}
|
||||
|
||||
Sequence operator * () { return Sequence(m_pos, m_next); }
|
||||
|
||||
split_iterator_t& operator ++ () {
|
||||
m_pos = m_next;
|
||||
|
||||
if (m_pos == m_seq->end())
|
||||
return *this;
|
||||
|
||||
m_pos++;
|
||||
m_next = std::find(m_pos, m_seq->end(), m_delim);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator == (const split_iterator_t& itr) const { return m_pos == m_seq->end(); }
|
||||
bool operator != (const split_iterator_t& itr) const { return m_pos != m_seq->end(); }
|
||||
|
||||
private:
|
||||
const Sequence* m_seq;
|
||||
value_type m_delim;
|
||||
const_iterator m_pos;
|
||||
const_iterator m_next;
|
||||
};
|
||||
|
||||
template <typename Sequence>
|
||||
inline split_iterator_t<Sequence>
|
||||
split_iterator(const Sequence& seq, typename Sequence::value_type delim) {
|
||||
return split_iterator_t<Sequence>(seq, delim);
|
||||
}
|
||||
|
||||
template <typename Sequence>
|
||||
inline split_iterator_t<Sequence>
|
||||
split_iterator(const Sequence& seq) {
|
||||
return split_iterator_t<Sequence>();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
+2
-2
@@ -174,9 +174,9 @@ AC_DEFUN([TORRENT_CHECK_POSIX_FALLOCATE], [
|
||||
|
||||
AC_DEFUN([TORRENT_WITH_POSIX_FALLOCATE], [
|
||||
AC_ARG_WITH(posix-fallocate,
|
||||
[ --with-fallocate Check for posix_fallocate],
|
||||
[ --with-posix-fallocate Check for and use posix_fallocate to allocate files.],
|
||||
[
|
||||
if test "$withval" = "no"; then
|
||||
if test "$withval" = "yes"; then
|
||||
TORRENT_CHECK_POSIX_FALLOCATE
|
||||
fi
|
||||
])
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
@@ -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
@@ -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);
|
||||
|
||||
@@ -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
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -66,7 +66,7 @@ public:
|
||||
|
||||
bool is_valid() const;
|
||||
|
||||
bool update();
|
||||
bool update(bool hideDot = true);
|
||||
|
||||
const std::string& get_path() { return m_path; }
|
||||
|
||||
|
||||
Reference in New Issue
Block a user