prepping for fix release

This commit is contained in:
Marcos Pinto 2007-10-28 07:47:54 +00:00
commit ba7143acb9
57 changed files with 9535 additions and 450 deletions

View file

@ -1,4 +1,8 @@
Deluge 0.5.6 (xx October 2007) Deluge 0.5.7 (xx November 2007)
* Set default piece size to 256-KiB in TorrentCreator plugin and add 2048KiB
as a size option.
Deluge 0.5.6 (24 October 2007)
* Web Interface Plugin * Web Interface Plugin
* Hopefully fix "losing data" and having to re-download parts (for real this time :p) * Hopefully fix "losing data" and having to re-download parts (for real this time :p)
* Use new full allocation method which does not create files until one of its * Use new full allocation method which does not create files until one of its

7
TODO
View file

@ -1,2 +1,5 @@
for 0.5.6 for 0.5.7
1. fix ratio saving 1. manual recheck
2. preference for .torrent location
3. have blocklist detect 7zip files and popup a warning instead of crashing
4. add auto-pickup folder

View file

@ -375,12 +375,6 @@ namespace libtorrent
// buffers from. // buffers from.
boost::pool<> m_send_buffers; boost::pool<> m_send_buffers;
// this is where all active sockets are stored.
// the selector can sleep while there's no activity on
// them
io_service m_io_service;
asio::strand m_strand;
// the file pool that all storages in this session's // the file pool that all storages in this session's
// torrents uses. It sets a limit on the number of // torrents uses. It sets a limit on the number of
// open files by this session. // open files by this session.
@ -395,6 +389,12 @@ namespace libtorrent
// object. // object.
disk_io_thread m_disk_thread; disk_io_thread m_disk_thread;
// this is where all active sockets are stored.
// the selector can sleep while there's no activity on
// them
io_service m_io_service;
asio::strand m_strand;
// this is a list of half-open tcp connections // this is a list of half-open tcp connections
// (only outgoing connections) // (only outgoing connections)
// this has to be one of the last // this has to be one of the last
@ -646,7 +646,7 @@ namespace libtorrent
void debug_log(const std::string& line) void debug_log(const std::string& line)
{ {
(*m_ses.m_logger) << line << "\n"; (*m_ses.m_logger) << time_now_string() << " " << line << "\n";
} }
session_impl& m_ses; session_impl& m_ses;
}; };

View file

@ -56,6 +56,7 @@ public:
void done(int ticket); void done(int ticket);
void limit(int limit); void limit(int limit);
int limit() const; int limit() const;
void close();
#ifndef NDEBUG #ifndef NDEBUG

View file

@ -130,6 +130,8 @@ namespace libtorrent
, proxy_settings const& ps , proxy_settings const& ps
, std::string const& password = ""); , std::string const& password = "");
void close();
private: private:
boost::intrusive_ptr<http_tracker_connection> self() boost::intrusive_ptr<http_tracker_connection> self()

View file

@ -357,6 +357,7 @@ namespace libtorrent
// the different priority levels // the different priority levels
switch (piece_priority) switch (piece_priority)
{ {
case 1: return prio;
case 2: return prio - 1; case 2: return prio - 1;
case 3: return (std::max)(prio / 2, 1); case 3: return (std::max)(prio / 2, 1);
case 4: return (std::max)(prio / 2 - 1, 1); case 4: return (std::max)(prio / 2 - 1, 1);

View file

@ -111,6 +111,7 @@ namespace libtorrent
, initial_picker_threshold(4) , initial_picker_threshold(4)
, allowed_fast_set_size(10) , allowed_fast_set_size(10)
, max_outstanding_disk_bytes_per_connection(64 * 1024) , max_outstanding_disk_bytes_per_connection(64 * 1024)
, handshake_timeout(10)
#ifndef TORRENT_DISABLE_DHT #ifndef TORRENT_DISABLE_DHT
, use_dht_as_fallback(true) , use_dht_as_fallback(true)
#endif #endif
@ -270,6 +271,11 @@ namespace libtorrent
// to not completely disrupt normal downloads. // to not completely disrupt normal downloads.
int max_outstanding_disk_bytes_per_connection; int max_outstanding_disk_bytes_per_connection;
// the number of seconds to wait for a handshake
// response from a peer. If no response is received
// within this time, the peer is disconnected.
int handshake_timeout;
#ifndef TORRENT_DISABLE_DHT #ifndef TORRENT_DISABLE_DHT
// while this is true, the dht will note be used unless the // while this is true, the dht will note be used unless the
// tracker is online // tracker is online

View file

@ -240,8 +240,8 @@ namespace libtorrent
void parse_info_section(entry const& e); void parse_info_section(entry const& e);
entry extra(char const* key) const entry const* extra(char const* key) const
{ return m_extra_info[key]; } { return m_extra_info.find_key(key); }
// frees parts of the metadata that isn't // frees parts of the metadata that isn't
// used by seeds // used by seeds

View file

@ -202,7 +202,7 @@ namespace libtorrent
void fail(int code, char const* msg); void fail(int code, char const* msg);
void fail_timeout(); void fail_timeout();
void close(); virtual void close();
address const& bind_interface() const { return m_bind_interface; } address const& bind_interface() const { return m_bind_interface; }
protected: protected:

View file

@ -74,6 +74,8 @@ namespace libtorrent
, boost::weak_ptr<request_callback> c , boost::weak_ptr<request_callback> c
, session_settings const& stn); , session_settings const& stn);
void close();
private: private:
enum action_t enum action_t

View file

@ -148,8 +148,19 @@ private:
{ {
mapping[0].protocol = 0; mapping[0].protocol = 0;
mapping[1].protocol = 1; mapping[1].protocol = 1;
#ifndef NDEBUG
magic = 1337;
#endif
} }
#ifndef NDEBUG
~rootdevice()
{
TORRENT_ASSERT(magic == 1337);
magic = 0;
}
#endif
// the interface url, through which the list of // the interface url, through which the list of
// supported interfaces are fetched // supported interfaces are fetched
std::string url; std::string url;
@ -174,8 +185,12 @@ private:
mutable boost::shared_ptr<http_connection> upnp_connection; mutable boost::shared_ptr<http_connection> upnp_connection;
#ifndef NDEBUG
int magic;
#endif
void close() const void close() const
{ {
TORRENT_ASSERT(magic == 1337);
if (!upnp_connection) return; if (!upnp_connection) return;
upnp_connection->close(); upnp_connection->close();
upnp_connection.reset(); upnp_connection.reset();

View file

@ -48,8 +48,8 @@ namespace aux
template<class IO_Control_Command> template<class IO_Control_Command>
struct io_control_visitor_ec: boost::static_visitor<> struct io_control_visitor_ec: boost::static_visitor<>
{ {
io_control_visitor_ec(IO_Control_Command& ioc, asio::error_code& ec) io_control_visitor_ec(IO_Control_Command& ioc, asio::error_code& ec_)
: ioc(ioc), ec(ec) {} : ioc(ioc), ec(ec_) {}
template <class T> template <class T>
void operator()(T* p) const void operator()(T* p) const
@ -188,7 +188,7 @@ namespace aux
: boost::static_visitor<> : boost::static_visitor<>
{ {
close_visitor_ec(asio::error_code& ec_) close_visitor_ec(asio::error_code& ec_)
: ec(ec) : ec(ec_)
{} {}
template <class T> template <class T>
@ -329,9 +329,9 @@ namespace aux
struct read_some_visitor_ec struct read_some_visitor_ec
: boost::static_visitor<std::size_t> : boost::static_visitor<std::size_t>
{ {
read_some_visitor_ec(Mutable_Buffers const& buffers, asio::error_code& ec) read_some_visitor_ec(Mutable_Buffers const& buffers, asio::error_code& ec_)
: buffers(buffers) : buffers(buffers)
, ec(ec) , ec(ec_)
{} {}
template <class T> template <class T>

View file

@ -86,6 +86,11 @@ namespace libtorrent
try_connect(); try_connect();
} }
void connection_queue::close()
{
m_timer.cancel();
}
void connection_queue::limit(int limit) void connection_queue::limit(int limit)
{ m_half_open_limit = limit; } { m_half_open_limit = limit; }

View file

@ -342,6 +342,10 @@ namespace libtorrent
try { if (handler) handler(ret, j); } try { if (handler) handler(ret, j); }
catch (std::exception&) {} catch (std::exception&) {}
#ifndef NDEBUG
m_current.storage = 0;
#endif
if (j.buffer && free_buffer) if (j.buffer && free_buffer)
{ {
l.lock(); l.lock();

View file

@ -489,7 +489,9 @@ namespace libtorrent
, boost::lexical_cast<std::string>(m_port)); , boost::lexical_cast<std::string>(m_port));
m_name_lookup.async_resolve(q, m_strand.wrap( m_name_lookup.async_resolve(q, m_strand.wrap(
boost::bind(&http_tracker_connection::name_lookup, self(), _1, _2))); boost::bind(&http_tracker_connection::name_lookup, self(), _1, _2)));
set_timeout(m_settings.tracker_completion_timeout set_timeout(req.event == tracker_request::stopped
? m_settings.stop_tracker_timeout
: m_settings.tracker_completion_timeout
, m_settings.tracker_receive_timeout); , m_settings.tracker_receive_timeout);
} }
@ -503,6 +505,17 @@ namespace libtorrent
fail_timeout(); fail_timeout();
} }
void http_tracker_connection::close()
{
asio::error_code ec;
m_socket.close(ec);
m_name_lookup.cancel();
if (m_connection_ticket > -1) m_cc.done(m_connection_ticket);
m_connection_ticket = -1;
m_timed_out = true;
tracker_connection::close();
}
void http_tracker_connection::name_lookup(asio::error_code const& error void http_tracker_connection::name_lookup(asio::error_code const& error
, tcp::resolver::iterator i) try , tcp::resolver::iterator i) try
{ {
@ -759,7 +772,6 @@ namespace libtorrent
if (m_parser.status_code() != 200) if (m_parser.status_code() != 200)
{ {
fail(m_parser.status_code(), m_parser.message().c_str()); fail(m_parser.status_code(), m_parser.message().c_str());
close();
return; return;
} }
@ -821,6 +833,7 @@ namespace libtorrent
TORRENT_ASSERT(false); TORRENT_ASSERT(false);
} }
#endif #endif
close();
} }
peer_entry http_tracker_connection::extract_peer_info(const entry& info) peer_entry http_tracker_connection::extract_peer_info(const entry& info)

View file

@ -184,5 +184,6 @@ void lsd::on_announce(udp::endpoint const& from, char* buffer
void lsd::close() void lsd::close()
{ {
m_socket.close(); m_socket.close();
m_broadcast_timer.cancel();
} }

View file

@ -382,6 +382,8 @@ void natpmp::try_next_mapping(int i)
void natpmp::close() void natpmp::close()
{ {
asio::error_code ec;
m_socket.close(ec);
if (m_disabled) return; if (m_disabled) return;
for (int i = 0; i < num_mappings; ++i) for (int i = 0; i < num_mappings; ++i)
{ {
@ -390,5 +392,7 @@ void natpmp::close()
m_mappings[i].external_port = 0; m_mappings[i].external_port = 0;
refresh_mapping(i); refresh_mapping(i);
} }
m_refresh_timer.cancel();
m_send_timer.cancel();
} }

View file

@ -3000,13 +3000,12 @@ namespace libtorrent
return true; return true;
} }
// if it takes more than 5 seconds to receive // do not stall waiting for a handshake
// handshake, disconnect if (in_handshake() && d > seconds(m_ses.settings().handshake_timeout))
if (in_handshake() && d > seconds(5))
{ {
#ifdef TORRENT_VERBOSE_LOGGING #ifdef TORRENT_VERBOSE_LOGGING
(*m_logger) << time_now_string() << " *** NO HANDSHAKE [ " (*m_logger) << time_now_string() << " *** NO HANDSHAKE [ waited "
<< total_seconds(d) << " seconds ago ] ***\n"; << total_seconds(d) << " seconds ] ***\n";
#endif #endif
return true; return true;
} }

View file

@ -547,8 +547,8 @@ namespace detail
, fingerprint const& cl_fprint , fingerprint const& cl_fprint
, char const* listen_interface) , char const* listen_interface)
: m_send_buffers(send_buffer_size) : m_send_buffers(send_buffer_size)
, m_strand(m_io_service)
, m_files(40) , m_files(40)
, m_strand(m_io_service)
, m_half_open(m_io_service) , m_half_open(m_io_service)
, m_download_channel(m_io_service, peer_connection::download_channel) , m_download_channel(m_io_service, peer_connection::download_channel)
, m_upload_channel(m_io_service, peer_connection::upload_channel) , m_upload_channel(m_io_service, peer_connection::upload_channel)
@ -675,6 +675,14 @@ namespace detail
if (m_dht) m_dht->stop(); if (m_dht) m_dht->stop();
#endif #endif
m_timer.cancel(); m_timer.cancel();
// close the listen sockets
for (std::list<listen_socket_t>::iterator i = m_listen_sockets.begin()
, end(m_listen_sockets.end()); i != end; ++i)
{
i->sock->close();
}
// abort all torrents // abort all torrents
for (torrent_map::iterator i = m_torrents.begin() for (torrent_map::iterator i = m_torrents.begin()
, end(m_torrents.end()); i != end; ++i) , end(m_torrents.end()); i != end; ++i)
@ -682,7 +690,15 @@ namespace detail
i->second->abort(); i->second->abort();
} }
m_io_service.stop(); #if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
(*m_logger) << time_now_string() << " aborting all tracker requests\n";
#endif
m_tracker_manager.abort_all_requests();
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
(*m_logger) << time_now_string() << " shutting down connection queue\n";
#endif
m_half_open.close();
mutex::scoped_lock l2(m_checker_impl.m_mutex); mutex::scoped_lock l2(m_checker_impl.m_mutex);
// abort the checker thread // abort the checker thread
@ -977,6 +993,17 @@ namespace detail
return; return;
} }
// don't allow more connections than the max setting
if (m_connections.size() > max_connections())
{
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
(*m_logger) << "number of connections limit exceeded (conns: "
<< num_connections() << ", limit: " << max_connections()
<< "), connection rejected\n";
#endif
return;
}
// check if we have any active torrents // check if we have any active torrents
// if we don't reject the connection // if we don't reject the connection
if (m_torrents.empty()) return; if (m_torrents.empty()) return;
@ -1474,20 +1501,12 @@ namespace detail
while (!m_abort); while (!m_abort);
deadline_timer tracker_timer(m_io_service); deadline_timer tracker_timer(m_io_service);
// this will remove the port mappings
if (m_natpmp.get())
m_natpmp->close();
if (m_upnp.get())
m_upnp->close();
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) #if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
(*m_logger) << time_now_string() << " locking mutex\n"; (*m_logger) << time_now_string() << " locking mutex\n";
#endif #endif
session_impl::mutex_t::scoped_lock l(m_mutex); session_impl::mutex_t::scoped_lock l(m_mutex);
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
(*m_logger) << time_now_string() << " aborting all tracker requests\n";
#endif
m_tracker_manager.abort_all_requests(); m_tracker_manager.abort_all_requests();
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) #if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
(*m_logger) << time_now_string() << " sending stopped to all torrent's trackers\n"; (*m_logger) << time_now_string() << " sending stopped to all torrent's trackers\n";
@ -2127,16 +2146,10 @@ namespace detail
session_impl::~session_impl() session_impl::~session_impl()
{ {
abort();
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) #if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
(*m_logger) << time_now_string() << "\n\n *** shutting down session *** \n\n"; (*m_logger) << time_now_string() << "\n\n *** shutting down session *** \n\n";
#endif #endif
// lock the main thread and abort it abort();
mutex_t::scoped_lock l(m_mutex);
m_abort = true;
m_io_service.stop();
l.unlock();
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) #if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
(*m_logger) << time_now_string() << " waiting for main thread\n"; (*m_logger) << time_now_string() << " waiting for main thread\n";

View file

@ -1526,7 +1526,7 @@ namespace libtorrent
INVARIANT_CHECK; INVARIANT_CHECK;
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) #if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
(*m_ses.m_logger) << time_now_string() << " resolving: " << url << "\n"; (*m_ses.m_logger) << time_now_string() << " resolving web seed: " << url << "\n";
#endif #endif
m_resolving_web_seeds.insert(url); m_resolving_web_seeds.insert(url);
@ -3055,7 +3055,7 @@ namespace libtorrent
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) #if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
void torrent::debug_log(const std::string& line) void torrent::debug_log(const std::string& line)
{ {
(*m_ses.m_logger) << line << "\n"; (*m_ses.m_logger) << time_now_string() << " " << line << "\n";
} }
#endif #endif

View file

@ -302,12 +302,12 @@ namespace libtorrent
{ {
m_completion_timeout = completion_timeout; m_completion_timeout = completion_timeout;
m_read_timeout = read_timeout; m_read_timeout = read_timeout;
m_start_time = time_now(); m_start_time = m_read_time = time_now();
m_read_time = time_now();
m_timeout.expires_at((std::min)( m_timeout.expires_at((std::min)(
m_read_time + seconds(m_read_timeout) m_read_time + seconds(m_read_timeout)
, m_start_time + seconds(m_completion_timeout))); , m_start_time + seconds((std::min)(m_completion_timeout
, m_read_timeout))));
m_timeout.async_wait(m_strand.wrap(bind( m_timeout.async_wait(m_strand.wrap(bind(
&timeout_handler::timeout_callback, self(), _1))); &timeout_handler::timeout_callback, self(), _1)));
} }
@ -343,7 +343,8 @@ namespace libtorrent
m_timeout.expires_at((std::min)( m_timeout.expires_at((std::min)(
m_read_time + seconds(m_read_timeout) m_read_time + seconds(m_read_timeout)
, m_start_time + seconds(m_completion_timeout))); , m_start_time + seconds((std::min)(m_completion_timeout
, m_read_timeout))));
m_timeout.async_wait(m_strand.wrap( m_timeout.async_wait(m_strand.wrap(
bind(&timeout_handler::timeout_callback, self(), _1))); bind(&timeout_handler::timeout_callback, self(), _1)));
} }
@ -567,12 +568,24 @@ namespace libtorrent
m_abort = true; m_abort = true;
tracker_connections_t keep_connections; tracker_connections_t keep_connections;
for (tracker_connections_t::const_iterator i = while (!m_connections.empty())
m_connections.begin(); i != m_connections.end(); ++i)
{ {
tracker_request const& req = (*i)->tracker_req(); boost::intrusive_ptr<tracker_connection>& c = m_connections.back();
if (!c)
{
m_connections.pop_back();
continue;
}
tracker_request const& req = c->tracker_req();
if (req.event == tracker_request::stopped) if (req.event == tracker_request::stopped)
keep_connections.push_back(*i); {
keep_connections.push_back(c);
m_connections.pop_back();
continue;
}
// close will remove the entry from m_connections
// so no need to pop
c->close();
} }
std::swap(m_connections, keep_connections); std::swap(m_connections, keep_connections);

View file

@ -96,7 +96,9 @@ namespace libtorrent
m_name_lookup.async_resolve(q m_name_lookup.async_resolve(q
, m_strand.wrap(boost::bind( , m_strand.wrap(boost::bind(
&udp_tracker_connection::name_lookup, self(), _1, _2))); &udp_tracker_connection::name_lookup, self(), _1, _2)));
set_timeout(m_settings.tracker_completion_timeout set_timeout(req.event == tracker_request::stopped
? m_settings.stop_tracker_timeout
: m_settings.tracker_completion_timeout
, m_settings.tracker_receive_timeout); , m_settings.tracker_receive_timeout);
} }
@ -156,11 +158,20 @@ namespace libtorrent
void udp_tracker_connection::on_timeout() void udp_tracker_connection::on_timeout()
{ {
m_socket.close(); asio::error_code ec;
m_socket.close(ec);
m_name_lookup.cancel(); m_name_lookup.cancel();
fail_timeout(); fail_timeout();
} }
void udp_tracker_connection::close()
{
asio::error_code ec;
m_socket.close(ec);
m_name_lookup.cancel();
tracker_connection::close();
}
void udp_tracker_connection::send_udp_connect() void udp_tracker_connection::send_udp_connect()
{ {
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) #if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
@ -468,6 +479,7 @@ namespace libtorrent
, complete, incomplete); , complete, incomplete);
m_man.remove_request(this); m_man.remove_request(this);
close();
return; return;
} }
catch (std::exception& e) catch (std::exception& e)
@ -543,6 +555,7 @@ namespace libtorrent
if (!cb) if (!cb)
{ {
m_man.remove_request(this); m_man.remove_request(this);
close();
return; return;
} }
@ -551,6 +564,7 @@ namespace libtorrent
, complete, incomplete); , complete, incomplete);
m_man.remove_request(this); m_man.remove_request(this);
close();
} }
catch (std::exception& e) catch (std::exception& e)
{ {

View file

@ -148,6 +148,7 @@ void upnp::set_mappings(int tcp, int udp)
, end(m_devices.end()); i != end; ++i) , end(m_devices.end()); i != end; ++i)
{ {
rootdevice& d = const_cast<rootdevice&>(*i); rootdevice& d = const_cast<rootdevice&>(*i);
TORRENT_ASSERT(d.magic == 1337);
if (d.mapping[0].local_port != m_tcp_local_port) if (d.mapping[0].local_port != m_tcp_local_port)
{ {
if (d.mapping[0].external_port == 0) if (d.mapping[0].external_port == 0)
@ -200,8 +201,13 @@ try
// we don't have a WANIP or WANPPP url for this device, // we don't have a WANIP or WANPPP url for this device,
// ask for it // ask for it
rootdevice& d = const_cast<rootdevice&>(*i); rootdevice& d = const_cast<rootdevice&>(*i);
TORRENT_ASSERT(d.magic == 1337);
try try
{ {
#ifdef TORRENT_UPNP_LOGGING
m_log << time_now_string()
<< " ==> connecting to " << d.url << std::endl;
#endif
d.upnp_connection.reset(new http_connection(m_io_service d.upnp_connection.reset(new http_connection(m_io_service
, m_cc, m_strand.wrap(bind(&upnp::on_upnp_xml, self(), _1, _2 , m_cc, m_strand.wrap(bind(&upnp::on_upnp_xml, self(), _1, _2
, boost::ref(d))))); , boost::ref(d)))));
@ -270,7 +276,7 @@ try
{ {
#ifdef TORRENT_UPNP_LOGGING #ifdef TORRENT_UPNP_LOGGING
m_log << time_now_string() m_log << time_now_string()
<< " <== Rootdevice responded with incorrect HTTP packet. Ignoring device (" << e.what() << ")" << std::endl; << " <== (" << from << ") Rootdevice responded with incorrect HTTP packet. Ignoring device (" << e.what() << ")" << std::endl;
#endif #endif
return; return;
} }
@ -280,11 +286,11 @@ try
#ifdef TORRENT_UPNP_LOGGING #ifdef TORRENT_UPNP_LOGGING
if (p.method().empty()) if (p.method().empty())
m_log << time_now_string() m_log << time_now_string()
<< " <== Device responded with HTTP status: " << p.status_code() << " <== (" << from << ") Device responded with HTTP status: " << p.status_code()
<< ". Ignoring device" << std::endl; << ". Ignoring device" << std::endl;
else else
m_log << time_now_string() m_log << time_now_string()
<< " <== Device with HTTP method: " << p.method() << " <== (" << from << ") Device with HTTP method: " << p.method()
<< ". Ignoring device" << std::endl; << ". Ignoring device" << std::endl;
#endif #endif
return; return;
@ -294,7 +300,7 @@ try
{ {
#ifdef TORRENT_UPNP_LOGGING #ifdef TORRENT_UPNP_LOGGING
m_log << time_now_string() m_log << time_now_string()
<< " <== Rootdevice responded with incomplete HTTP " << " <== (" << from << ") Rootdevice responded with incomplete HTTP "
"packet. Ignoring device" << std::endl; "packet. Ignoring device" << std::endl;
#endif #endif
return; return;
@ -305,7 +311,7 @@ try
{ {
#ifdef TORRENT_UPNP_LOGGING #ifdef TORRENT_UPNP_LOGGING
m_log << time_now_string() m_log << time_now_string()
<< " <== Rootdevice response is missing a location header. " << " <== (" << from << ") Rootdevice response is missing a location header. "
"Ignoring device" << std::endl; "Ignoring device" << std::endl;
#endif #endif
return; return;
@ -332,7 +338,7 @@ try
{ {
#ifdef TORRENT_UPNP_LOGGING #ifdef TORRENT_UPNP_LOGGING
m_log << time_now_string() m_log << time_now_string()
<< " <== Rootdevice uses unsupported protocol: '" << protocol << " <== (" << from << ") Rootdevice uses unsupported protocol: '" << protocol
<< "'. Ignoring device" << std::endl; << "'. Ignoring device" << std::endl;
#endif #endif
return; return;
@ -342,16 +348,27 @@ try
{ {
#ifdef TORRENT_UPNP_LOGGING #ifdef TORRENT_UPNP_LOGGING
m_log << time_now_string() m_log << time_now_string()
<< " <== Rootdevice responded with a url with port 0. " << " <== (" << from << ") Rootdevice responded with a url with port 0. "
"Ignoring device" << std::endl; "Ignoring device" << std::endl;
#endif #endif
return; return;
} }
#ifdef TORRENT_UPNP_LOGGING #ifdef TORRENT_UPNP_LOGGING
m_log << time_now_string() m_log << time_now_string()
<< " <== Found rootdevice: " << d.url << std::endl; << " <== (" << from << ") Found rootdevice: " << d.url
<< " total: " << m_devices.size() << std::endl;
#endif #endif
if (m_devices.size() >= 50)
{
#ifdef TORRENT_UPNP_LOGGING
m_log << time_now_string()
<< " <== (" << from << ") Too many devices (" << m_devices.size() << "), "
"ignoring: " << d.url << std::endl;
#endif
return;
}
if (m_tcp_local_port != 0) if (m_tcp_local_port != 0)
{ {
d.mapping[0].need_update = true; d.mapping[0].need_update = true;
@ -390,8 +407,13 @@ try
// we don't have a WANIP or WANPPP url for this device, // we don't have a WANIP or WANPPP url for this device,
// ask for it // ask for it
rootdevice& d = const_cast<rootdevice&>(*i); rootdevice& d = const_cast<rootdevice&>(*i);
TORRENT_ASSERT(d.magic == 1337);
try try
{ {
#ifdef TORRENT_UPNP_LOGGING
m_log << time_now_string()
<< " ==> connecting to " << d.url << std::endl;
#endif
d.upnp_connection.reset(new http_connection(m_io_service d.upnp_connection.reset(new http_connection(m_io_service
, m_cc, m_strand.wrap(bind(&upnp::on_upnp_xml, self(), _1, _2 , m_cc, m_strand.wrap(bind(&upnp::on_upnp_xml, self(), _1, _2
, boost::ref(d))))); , boost::ref(d)))));
@ -420,6 +442,7 @@ catch (std::exception&)
void upnp::post(upnp::rootdevice const& d, std::string const& soap void upnp::post(upnp::rootdevice const& d, std::string const& soap
, std::string const& soap_action) , std::string const& soap_action)
{ {
TORRENT_ASSERT(d.magic == 1337);
std::stringstream header; std::stringstream header;
header << "POST " << d.control_url << " HTTP/1.1\r\n" header << "POST " << d.control_url << " HTTP/1.1\r\n"
@ -439,6 +462,7 @@ void upnp::post(upnp::rootdevice const& d, std::string const& soap
void upnp::create_port_mapping(http_connection& c, rootdevice& d, int i) void upnp::create_port_mapping(http_connection& c, rootdevice& d, int i)
{ {
TORRENT_ASSERT(d.magic == 1337);
std::string soap_action = "AddPortMapping"; std::string soap_action = "AddPortMapping";
std::stringstream soap; std::stringstream soap;
@ -463,6 +487,7 @@ void upnp::create_port_mapping(http_connection& c, rootdevice& d, int i)
void upnp::map_port(rootdevice& d, int i) void upnp::map_port(rootdevice& d, int i)
{ {
TORRENT_ASSERT(d.magic == 1337);
if (d.upnp_connection) return; if (d.upnp_connection) return;
if (!d.mapping[i].need_update) if (!d.mapping[i].need_update)
@ -479,6 +504,10 @@ void upnp::map_port(rootdevice& d, int i)
TORRENT_ASSERT(!d.upnp_connection); TORRENT_ASSERT(!d.upnp_connection);
TORRENT_ASSERT(d.service_namespace); TORRENT_ASSERT(d.service_namespace);
#ifdef TORRENT_UPNP_LOGGING
m_log << time_now_string()
<< " ==> connecting to " << d.hostname << std::endl;
#endif
d.upnp_connection.reset(new http_connection(m_io_service d.upnp_connection.reset(new http_connection(m_io_service
, m_cc, m_strand.wrap(bind(&upnp::on_upnp_map_response, self(), _1, _2 , m_cc, m_strand.wrap(bind(&upnp::on_upnp_map_response, self(), _1, _2
, boost::ref(d), i)), true , boost::ref(d), i)), true
@ -490,6 +519,7 @@ void upnp::map_port(rootdevice& d, int i)
void upnp::delete_port_mapping(rootdevice& d, int i) void upnp::delete_port_mapping(rootdevice& d, int i)
{ {
TORRENT_ASSERT(d.magic == 1337);
std::stringstream soap; std::stringstream soap;
std::string soap_action = "DeletePortMapping"; std::string soap_action = "DeletePortMapping";
@ -510,23 +540,24 @@ void upnp::delete_port_mapping(rootdevice& d, int i)
// requires the mutex to be locked // requires the mutex to be locked
void upnp::unmap_port(rootdevice& d, int i) void upnp::unmap_port(rootdevice& d, int i)
{ {
if (d.mapping[i].external_port == 0) TORRENT_ASSERT(d.magic == 1337);
if (d.mapping[i].external_port == 0
|| d.disabled)
{ {
if (i < num_mappings - 1) if (i < num_mappings - 1)
{ {
unmap_port(d, i + 1); unmap_port(d, i + 1);
} }
else
{
m_devices.erase(d);
}
return; return;
} }
#ifdef TORRENT_UPNP_LOGGING
m_log << time_now_string()
<< " ==> connecting to " << d.hostname << std::endl;
#endif
d.upnp_connection.reset(new http_connection(m_io_service d.upnp_connection.reset(new http_connection(m_io_service
, m_cc, m_strand.wrap(bind(&upnp::on_upnp_unmap_response, self(), _1, _2 , m_cc, m_strand.wrap(bind(&upnp::on_upnp_unmap_response, self(), _1, _2
, boost::ref(d), i)), true , boost::ref(d), i)), true
, bind(&upnp::delete_port_mapping, self(), boost::ref(d), i))); , bind(&upnp::delete_port_mapping, self(), boost::ref(d), i)));
d.upnp_connection->start(d.hostname, boost::lexical_cast<std::string>(d.port) d.upnp_connection->start(d.hostname, boost::lexical_cast<std::string>(d.port)
, seconds(10)); , seconds(10));
} }
@ -591,6 +622,7 @@ namespace
void upnp::on_upnp_xml(asio::error_code const& e void upnp::on_upnp_xml(asio::error_code const& e
, libtorrent::http_parser const& p, rootdevice& d) try , libtorrent::http_parser const& p, rootdevice& d) try
{ {
TORRENT_ASSERT(d.magic == 1337);
if (d.upnp_connection) if (d.upnp_connection)
{ {
d.upnp_connection->close(); d.upnp_connection->close();
@ -601,8 +633,10 @@ void upnp::on_upnp_xml(asio::error_code const& e
{ {
#ifdef TORRENT_UPNP_LOGGING #ifdef TORRENT_UPNP_LOGGING
m_log << time_now_string() m_log << time_now_string()
<< " <== error while fetching control url: " << e.message() << std::endl; << " <== (" << d.url << ") error while fetching control url: "
<< e.message() << std::endl;
#endif #endif
d.disabled = true;
return; return;
} }
@ -610,8 +644,9 @@ void upnp::on_upnp_xml(asio::error_code const& e
{ {
#ifdef TORRENT_UPNP_LOGGING #ifdef TORRENT_UPNP_LOGGING
m_log << time_now_string() m_log << time_now_string()
<< " <== error while fetching control url: incomplete http message" << std::endl; << " <== (" << d.url << ") error while fetching control url: incomplete http message" << std::endl;
#endif #endif
d.disabled = true;
return; return;
} }
@ -619,8 +654,9 @@ void upnp::on_upnp_xml(asio::error_code const& e
{ {
#ifdef TORRENT_UPNP_LOGGING #ifdef TORRENT_UPNP_LOGGING
m_log << time_now_string() m_log << time_now_string()
<< " <== error while fetching control url: " << p.message() << std::endl; << " <== (" << d.url << ") error while fetching control url: " << p.message() << std::endl;
#endif #endif
d.disabled = true;
return; return;
} }
@ -647,15 +683,17 @@ void upnp::on_upnp_xml(asio::error_code const& e
{ {
#ifdef TORRENT_UPNP_LOGGING #ifdef TORRENT_UPNP_LOGGING
m_log << time_now_string() m_log << time_now_string()
<< " <== Rootdevice response, did not find a port mapping interface" << std::endl; << " <== (" << d.url << ") Rootdevice response, did not find "
"a port mapping interface" << std::endl;
#endif #endif
d.disabled = true;
return; return;
} }
} }
#ifdef TORRENT_UPNP_LOGGING #ifdef TORRENT_UPNP_LOGGING
m_log << time_now_string() m_log << time_now_string()
<< " <== Rootdevice response, found control URL: " << s.control_url << " <== (" << d.url << ") Rootdevice response, found control URL: " << s.control_url
<< " namespace: " << d.service_namespace << std::endl; << " namespace: " << d.service_namespace << std::endl;
#endif #endif
@ -732,6 +770,7 @@ namespace
void upnp::on_upnp_map_response(asio::error_code const& e void upnp::on_upnp_map_response(asio::error_code const& e
, libtorrent::http_parser const& p, rootdevice& d, int mapping) try , libtorrent::http_parser const& p, rootdevice& d, int mapping) try
{ {
TORRENT_ASSERT(d.magic == 1337);
if (d.upnp_connection) if (d.upnp_connection)
{ {
d.upnp_connection->close(); d.upnp_connection->close();
@ -744,7 +783,7 @@ void upnp::on_upnp_map_response(asio::error_code const& e
m_log << time_now_string() m_log << time_now_string()
<< " <== error while adding portmap: " << e.message() << std::endl; << " <== error while adding portmap: " << e.message() << std::endl;
#endif #endif
m_devices.erase(d); d.disabled = true;
return; return;
} }
@ -773,7 +812,7 @@ void upnp::on_upnp_map_response(asio::error_code const& e
m_log << time_now_string() m_log << time_now_string()
<< " <== error while adding portmap: incomplete http message" << std::endl; << " <== error while adding portmap: incomplete http message" << std::endl;
#endif #endif
m_devices.erase(d); d.disabled = true;
return; return;
} }
@ -877,6 +916,7 @@ catch (std::exception&)
void upnp::on_upnp_unmap_response(asio::error_code const& e void upnp::on_upnp_unmap_response(asio::error_code const& e
, libtorrent::http_parser const& p, rootdevice& d, int mapping) try , libtorrent::http_parser const& p, rootdevice& d, int mapping) try
{ {
TORRENT_ASSERT(d.magic == 1337);
if (d.upnp_connection) if (d.upnp_connection)
{ {
d.upnp_connection->close(); d.upnp_connection->close();
@ -906,7 +946,7 @@ void upnp::on_upnp_unmap_response(asio::error_code const& e
m_log << time_now_string() m_log << time_now_string()
<< " <== error while deleting portmap: " << p.message() << std::endl; << " <== error while deleting portmap: " << p.message() << std::endl;
#endif #endif
m_devices.erase(d); d.disabled = true;
return; return;
} }
@ -922,10 +962,6 @@ void upnp::on_upnp_unmap_response(asio::error_code const& e
unmap_port(d, mapping + 1); unmap_port(d, mapping + 1);
return; return;
} }
// the main thread is likely to be waiting for
// all the unmap operations to complete
m_devices.erase(d);
} }
catch (std::exception&) catch (std::exception&)
{ {
@ -943,6 +979,7 @@ void upnp::on_expire(asio::error_code const& e) try
, end(m_devices.end()); i != end; ++i) , end(m_devices.end()); i != end; ++i)
{ {
rootdevice& d = const_cast<rootdevice&>(*i); rootdevice& d = const_cast<rootdevice&>(*i);
TORRENT_ASSERT(d.magic == 1337);
for (int m = 0; m < num_mappings; ++m) for (int m = 0; m < num_mappings; ++m)
{ {
if (d.mapping[m].expires != max_time()) if (d.mapping[m].expires != max_time())
@ -984,15 +1021,11 @@ void upnp::close()
} }
for (std::set<rootdevice>::iterator i = m_devices.begin() for (std::set<rootdevice>::iterator i = m_devices.begin()
, end(m_devices.end()); i != end;) , end(m_devices.end()); i != end; ++i)
{ {
rootdevice& d = const_cast<rootdevice&>(*i); rootdevice& d = const_cast<rootdevice&>(*i);
if (d.control_url.empty()) TORRENT_ASSERT(d.magic == 1337);
{ if (d.control_url.empty()) continue;
m_devices.erase(i++);
continue;
}
++i;
unmap_port(d, 0); unmap_port(d, 0);
} }
} }

View file

@ -82,7 +82,7 @@ class TorrentCreator:
self.glade = gtk.glade.XML(self.path + "/torrentcreator.glade") self.glade = gtk.glade.XML(self.path + "/torrentcreator.glade")
self.dialog = self.glade.get_widget("torrentcreator") self.dialog = self.glade.get_widget("torrentcreator")
self.glade.get_widget("piece_size_combobox").set_active(0) self.glade.get_widget("piece_size_combobox").set_active(3)
self.glade.get_widget("torrent_chooserbutton").connect("clicked", self.torrent_chooserbutton_clicked) self.glade.get_widget("torrent_chooserbutton").connect("clicked", self.torrent_chooserbutton_clicked)
self.glade.get_widget("ok_button").connect("clicked", self.create_torrent) self.glade.get_widget("ok_button").connect("clicked", self.create_torrent)
self.glade.get_widget("close_button").connect("clicked", self.destroy) self.glade.get_widget("close_button").connect("clicked", self.destroy)

View file

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd"> <!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd">
<!--Generated with glade3 3.2.2 on Wed Sep 5 12:27:39 2007 by markybob@peg--> <!--Generated with glade3 3.2.2 on Thu Oct 25 22:18:52 2007 by markybob@peg-->
<glade-interface> <glade-interface>
<widget class="GtkDialog" id="torrentcreator"> <widget class="GtkDialog" id="torrentcreator">
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
@ -54,7 +54,6 @@
<widget class="GtkFrame" id="frame2"> <widget class="GtkFrame" id="frame2">
<property name="visible">True</property> <property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label_xalign">0</property>
<property name="shadow_type">GTK_SHADOW_NONE</property> <property name="shadow_type">GTK_SHADOW_NONE</property>
<child> <child>
<widget class="GtkAlignment" id="alignment2"> <widget class="GtkAlignment" id="alignment2">
@ -151,7 +150,6 @@
<widget class="GtkFrame" id="frame1"> <widget class="GtkFrame" id="frame1">
<property name="visible">True</property> <property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label_xalign">0</property>
<property name="shadow_type">GTK_SHADOW_NONE</property> <property name="shadow_type">GTK_SHADOW_NONE</property>
<child> <child>
<widget class="GtkAlignment" id="alignment3"> <widget class="GtkAlignment" id="alignment3">
@ -271,7 +269,6 @@
<widget class="GtkFrame" id="frame3"> <widget class="GtkFrame" id="frame3">
<property name="visible">True</property> <property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label_xalign">0</property>
<property name="shadow_type">GTK_SHADOW_NONE</property> <property name="shadow_type">GTK_SHADOW_NONE</property>
<child> <child>
<widget class="GtkAlignment" id="alignment4"> <widget class="GtkAlignment" id="alignment4">
@ -318,7 +315,6 @@
<widget class="GtkFrame" id="frame4"> <widget class="GtkFrame" id="frame4">
<property name="visible">True</property> <property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label_xalign">0</property>
<property name="shadow_type">GTK_SHADOW_NONE</property> <property name="shadow_type">GTK_SHADOW_NONE</property>
<child> <child>
<widget class="GtkAlignment" id="alignment5"> <widget class="GtkAlignment" id="alignment5">
@ -365,7 +361,6 @@
<widget class="GtkFrame" id="frame5"> <widget class="GtkFrame" id="frame5">
<property name="visible">True</property> <property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label_xalign">0</property>
<property name="shadow_type">GTK_SHADOW_NONE</property> <property name="shadow_type">GTK_SHADOW_NONE</property>
<child> <child>
<widget class="GtkAlignment" id="alignment6"> <widget class="GtkAlignment" id="alignment6">
@ -417,6 +412,9 @@
<property name="n_rows">2</property> <property name="n_rows">2</property>
<property name="n_columns">2</property> <property name="n_columns">2</property>
<property name="column_spacing">10</property> <property name="column_spacing">10</property>
<child>
<placeholder/>
</child>
<child> <child>
<widget class="GtkCheckButton" id="chk_set_priv"> <widget class="GtkCheckButton" id="chk_set_priv">
<property name="visible">True</property> <property name="visible">True</property>
@ -431,9 +429,6 @@
<property name="bottom_attach">2</property> <property name="bottom_attach">2</property>
</packing> </packing>
</child> </child>
<child>
<placeholder/>
</child>
<child> <child>
<widget class="GtkComboBox" id="piece_size_combobox"> <widget class="GtkComboBox" id="piece_size_combobox">
<property name="visible">True</property> <property name="visible">True</property>
@ -445,6 +440,7 @@
256 KiB 256 KiB
512 KiB 512 KiB
1024 KiB 1024 KiB
2048 KiB
</property> </property>
</widget> </widget>
<packing> <packing>

View file

@ -28,10 +28,10 @@
# but you are not obligated to do so. If you do not wish to do so, delete # 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 # this exception statement from your version. If you delete this exception
plugin_name = "Web User Interface" plugin_name = _("Web User Interface")
plugin_author = "Martijn Voncken" plugin_author = "Martijn Voncken"
plugin_version = "rev." plugin_version = "rev."
plugin_description = """A Web based User Interface plugin_description = _("""A Web based User Interface
Firefox greasemonkey script: http://userscripts.org/scripts/show/12639 Firefox greasemonkey script: http://userscripts.org/scripts/show/12639
@ -41,8 +41,8 @@ There is support for multiple templates, but just one is included.
Other contributors: Other contributors:
*somedude : template enhancements. *somedude : template enhancements.
*markybob : stability : synced with his changes in deluge-svn.
""" """)
import deluge.common import deluge.common
import deluge.pref import deluge.pref
@ -80,9 +80,10 @@ class plugin_WebUi(object):
self.web_server = None self.web_server = None
if not deluge.common.windows_check(): if not deluge.common.windows_check():
import commands import commands
status = commands.getstatusoutput('ps x |grep -v grep |grep run_webserver') status = commands.getstatusoutput(
'ps x |grep -v grep |grep run_webserver')
if status[0] == 0: if status[0] == 0:
os.kill(status[1].split()[0], 9) os.kill(int(status[1].split()[0]), 9)
time.sleep(1) #safe time to wait for kill to finish. time.sleep(1) #safe time to wait for kill to finish.
self.config_file = deluge.common.CONFIG_DIR + "/webui.conf" self.config_file = deluge.common.CONFIG_DIR + "/webui.conf"
self.config = deluge.pref.Preferences(self.config_file, False) self.config = deluge.pref.Preferences(self.config_file, False)
@ -95,9 +96,6 @@ class plugin_WebUi(object):
if not self.config.get('port'): #ugly way to detect new config file. if not self.config.get('port'): #ugly way to detect new config file.
#set default values: #set default values:
self.config.set("port", 8112) self.config.set("port", 8112)
#future->use deluge-core setting for download_dir (if it is set)
self.config.set("download_dir", os.path.expanduser("~"))
self.config.set("torrent_dir", os.path.expanduser("~"))
self.config.set("button_style", 2) self.config.set("button_style", 2)
self.config.set("auto_refresh", False) self.config.set("auto_refresh", False)
self.config.set("auto_refresh_secs", 4) self.config.set("auto_refresh_secs", 4)
@ -112,11 +110,9 @@ class plugin_WebUi(object):
self.config.set("cache_templates", True) self.config.set("cache_templates", True)
if deluge.common.windows_check(): if deluge.common.windows_check():
if self.config.get("run_in_thread") == None: self.config.set("run_in_thread", True)
self.config.set("run_in_thread", True)
else: else:
if self.config.get("run_in_thread") == None: self.config.set("run_in_thread", False)
self.config.set("run_in_thread", False)
self.dbus_manager = get_dbus_manager(deluge_core, deluge_interface, self.dbus_manager = get_dbus_manager(deluge_core, deluge_interface,
self.config, self.config_file) self.config, self.config_file)
@ -141,8 +137,8 @@ class plugin_WebUi(object):
self.kill_server() self.kill_server()
if self.config.get("run_in_thread"): if self.config.get("run_in_thread"):
print 'start Webui(inside gtk)..' print 'Start Webui(inside gtk)..'
webserver_common.init() #reload changed config. webserver_common.init_gtk_05() #reload changed config.
from deluge_webserver import WebServer #only import in threaded mode from deluge_webserver import WebServer #only import in threaded mode
@ -150,11 +146,9 @@ class plugin_WebUi(object):
self.web_server.start_gtk() self.web_server.start_gtk()
else: else:
print 'start Webui(in process)..' print 'Start Webui(in process)..'
path = os.path.dirname(__file__) server_bin = os.path.dirname(__file__) + '/run_webserver'
server_bin = path + '/run_webserver' self.proc = Popen((server_bin,'env=0.5'))
port = str(self.config.get('port'))
self.proc = Popen((server_bin, port),cwd=path)
def kill_server(self): def kill_server(self):
if self.web_server: if self.web_server:
@ -187,7 +181,8 @@ class ConfigDialog(gtk.Dialog):
template_path = os.path.join(os.path.dirname(__file__), 'templates') template_path = os.path.join(os.path.dirname(__file__), 'templates')
self.templates = [dirname for dirname self.templates = [dirname for dirname
in os.listdir(template_path) in os.listdir(template_path)
if os.path.isdir(os.path.join(template_path, dirname))] if os.path.isdir(os.path.join(template_path, dirname))
and not dirname.startswith('.')]
self.port = self.add_widget(_('Port Number'), gtk.SpinButton()) self.port = self.add_widget(_('Port Number'), gtk.SpinButton())
self.pwd1 = self.add_widget(_('New Password'), gtk.Entry()) self.pwd1 = self.add_widget(_('New Password'), gtk.Entry())
@ -195,16 +190,11 @@ class ConfigDialog(gtk.Dialog):
self.template = self.add_widget(_('Template'), gtk.combo_box_new_text()) self.template = self.add_widget(_('Template'), gtk.combo_box_new_text())
self.button_style = self.add_widget(_('Button Style'), self.button_style = self.add_widget(_('Button Style'),
gtk.combo_box_new_text()) gtk.combo_box_new_text())
self.download_dir = self.add_widget(_('Download Directory'),
gtk.FileChooserButton(_('Download Directory')))
self.torrent_dir = self.add_widget(_('Torrent Directory'),
gtk.FileChooserButton(_('Torrent Directory')))
self.cache_templates = self.add_widget(_('Cache Templates'), self.cache_templates = self.add_widget(_('Cache Templates'),
gtk.CheckButton()) gtk.CheckButton())
self.run_in_thread = self.add_widget(_('Run inside GTK'), gtk.CheckButton()) #self.share_downloads = self.add_widget(_('Share Download Directory'),
# gtk.CheckButton())
self.download_dir.set_action(gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER)
self.torrent_dir.set_action(gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER)
self.port.set_range(80, 65536) self.port.set_range(80, 65536)
self.port.set_increments(1, 10) self.port.set_increments(1, 10)
self.pwd1.set_visibility(False) self.pwd1.set_visibility(False)
@ -218,23 +208,16 @@ class ConfigDialog(gtk.Dialog):
for item in [_('Text and image'), _('Image Only'), _('Text Only')]: for item in [_('Text and image'), _('Image Only'), _('Text Only')]:
self.button_style.append_text(item) self.button_style.append_text(item)
if not self.config.get("button_style"): if self.config.get("button_style") == None:
self.config.set("button_style", 2) self.config.set("button_style", 2)
self.port.set_value(int(self.config.get("port"))) self.port.set_value(int(self.config.get("port")))
self.template.set_active( self.template.set_active(
self.templates.index(self.config.get("template"))) self.templates.index(self.config.get("template")))
self.button_style.set_active(self.config.get("button_style")) self.button_style.set_active(self.config.get("button_style"))
#self.share_downloads.set_active(
# bool(self.config.get("share_downloads")))
self.torrent_dir.set_filename(self.config.get("torrent_dir"))
self.download_dir.set_filename(self.config.get("download_dir"))
if deluge.common.windows_check():
self.run_in_thread.set_active(True)
self.run_in_thread.set_sensitive(False)
else:
self.run_in_thread.set_active(False)
self.run_in_thread.set_sensitive(False)
self.cache_templates.set_active(self.config.get("cache_templates")) self.cache_templates.set_active(self.config.get("cache_templates"))
self.vbox.pack_start(self.vb, True, True, 0) self.vbox.pack_start(self.vb, True, True, 0)
@ -270,9 +253,7 @@ class ConfigDialog(gtk.Dialog):
self.config.set("port", int(self.port.get_value())) self.config.set("port", int(self.port.get_value()))
self.config.set("template", self.template.get_active_text()) self.config.set("template", self.template.get_active_text())
self.config.set("button_style", self.button_style.get_active()) self.config.set("button_style", self.button_style.get_active())
self.config.set("torrent_dir", self.torrent_dir.get_filename())
self.config.set("download_dir",self.download_dir.get_filename())
self.config.set("cache_templates", self.cache_templates.get_active()) self.config.set("cache_templates", self.cache_templates.get_active())
self.config.set("run_in_thread", self.run_in_thread.get_active()) #self.config.set("share_downloads", self.share_downloads.get_active())
self.config.save(self.plugin.config_file) self.config.save(self.plugin.config_file)
self.plugin.start_server() #restarts server self.plugin.start_server() #restarts server

View file

@ -68,7 +68,7 @@ class DbusManager(dbus.service.Object):
@dbus.service.method(dbus_interface=dbus_interface, @dbus.service.method(dbus_interface=dbus_interface,
in_signature="",out_signature="as") in_signature="",out_signature="as")
def get_torrent_state(self): def get_session_state(self):
"""Returns a list of torrent_ids in the session. """Returns a list of torrent_ids in the session.
same as 0.6, but returns type "as" instead of a pickle same as 0.6, but returns type "as" instead of a pickle
""" """
@ -129,18 +129,20 @@ class DbusManager(dbus.service.Object):
return status_subset return status_subset
@dbus.service.method(dbus_interface=dbus_interface, @dbus.service.method(dbus_interface=dbus_interface,
in_signature="s",out_signature="") in_signature="as",out_signature="")
def pause_torrent(self, torrent_id): def pause_torrent(self, torrents):
"""same as 0.6 interface""" """same as 0.6 interface"""
torrent_id = int(torrent_id) for torrent_id in torrents:
self.core.set_user_pause(torrent_id,True) torrent_id = int(torrent_id)
self.core.set_user_pause(torrent_id,True)
@dbus.service.method(dbus_interface=dbus_interface, @dbus.service.method(dbus_interface=dbus_interface,
in_signature="s", out_signature="") in_signature="as", out_signature="")
def resume_torrent(self, torrent_id): def resume_torrent(self, torrents):
"""same as 0.6 interface""" """same as 0.6 interface"""
torrent_id = int(torrent_id) for torrent_id in torrents:
self.core.set_user_pause(torrent_id,False) torrent_id = int(torrent_id)
self.core.set_user_pause(torrent_id,False)
@dbus.service.method(dbus_interface=dbus_interface, @dbus.service.method(dbus_interface=dbus_interface,
in_signature="sbb", out_signature="") in_signature="sbb", out_signature="")
@ -157,7 +159,6 @@ class DbusManager(dbus.service.Object):
@dbus.service.method(dbus_interface=dbus_interface, @dbus.service.method(dbus_interface=dbus_interface,
in_signature="s", out_signature="b") in_signature="s", out_signature="b")
def add_torrent_url(self, url): def add_torrent_url(self, url):
"""not available in deluge 0.6 interface"""
filename = fetch_url(url) filename = fetch_url(url)
self._add_torrent(filename) self._add_torrent(filename)
return True return True
@ -182,8 +183,8 @@ class DbusManager(dbus.service.Object):
#name = fillename without directory #name = fillename without directory
name = name.replace('\\','/') name = name.replace('\\','/')
name = 'deluge_' + str(random.random()) + '_' + name.split('/')[-1] name = 'deluge_' + str(random.random()) + '_' + name.split('/')[-1]
filename = os.path.join(self.core.config.get("default_download_path"), name)
filename = os.path.join(self.config.get("torrent_dir"),name)
filecontent = base64.b64decode(filecontent_b64) filecontent = base64.b64decode(filecontent_b64)
f = open(filename,"wb") #no with statement, that's py 2.5+ f = open(filename,"wb") #no with statement, that's py 2.5+
f.write(filecontent) f.write(filecontent)
@ -192,11 +193,42 @@ class DbusManager(dbus.service.Object):
self._add_torrent(filename) self._add_torrent(filename)
return True return True
@dbus.service.method(dbus_interface=dbus_interface,
in_signature="", out_signature="a{sv}")
def get_config(self):
return self.core.config.mapping
@dbus.service.method(dbus_interface=dbus_interface,
in_signature="s", out_signature="v")
def get_config_value(self,key):
return self.core.config.mapping[pythonize(key)] #ugly!
@dbus.service.method(dbus_interface=dbus_interface,
in_signature="a{sv}", out_signature="")
def set_config(self, config):
"""Set the config with values from dictionary"""
config = deluge.common.pythonize(config)
# Load all the values into the configuration
for key in self.core.config.keys():
self.core.config[key] = config[key]
self.core.apply_prefs()
@dbus.service.method(dbus_interface=dbus_interface,
in_signature="", out_signature="v")
def get_download_rate(self):
return self.core.get_state()['download_rate']
@dbus.service.method(dbus_interface=dbus_interface,
in_signature="", out_signature="v")
def get_upload_rate(self):
return self.core.get_state()['upload_rate']
#internal #internal
def _add_torrent(self, filename): def _add_torrent(self, filename):
#dbus types break pickle, again.....
filename = unicode(filename) filename = unicode(filename)
target = self.config.get("download_dir") target = self.core.config.get("default_download_path")
torrent_id = self.core.add_torrent(filename, target, torrent_id = self.core.add_torrent(filename, target,
self.interface.config.get("use_compact_storage")) self.interface.config.get("use_compact_storage"))

View file

@ -31,67 +31,15 @@
# this exception statement from your version. If you delete this exception # this exception statement from your version. If you delete this exception
# statement from all source files in the program, then also delete it here. # statement from all source files in the program, then also delete it here.
from webserver_common import TORRENT_KEYS, STATE_MESSAGES
import webserver_common as ws import webserver_common as ws
from webserver_framework import * from webserver_framework import *
import webpy022 as web import webpy022 as web
from webpy022.http import seeother, url from webpy022.http import seeother, url
from webpy022.utils import Storage
from md5 import md5
import base64 import base64
from deluge.common import fsize
from operator import attrgetter from operator import attrgetter
import os
#utils:
def check_pwd(pwd):
m = md5()
m.update(ws.config.get('pwd_salt'))
m.update(pwd)
return (m.digest() == ws.config.get('pwd_md5'))
def get_torrent_status(torrent_id):
"""
helper method.
enhance ws.proxy.get_torrent_status with some extra data
"""
status = ws.proxy.get_torrent_status(torrent_id,TORRENT_KEYS)
status["id"] = torrent_id
#for naming the status-images
status["calc_state_str"] = "downloading"
if status["paused"]:
status["calc_state_str"] = "inactive"
elif status["is_seed"]:
status["calc_state_str"] = "seeding"
#action for torrent_pause
if status["calc_state_str"] == "inactive":
status["action"] = "start"
else:
status["action"] = "stop"
if status["paused"]:
status["message"] = _("Paused %s%%") % status['progress']
else:
status["message"] = "%s %i%%" % (STATE_MESSAGES[status["state"]]
, status['progress'])
#add some pre-calculated values
status.update({
"calc_total_downloaded" : (fsize(status["total_done"])
+ " (" + fsize(status["total_download"]) + ")"),
"calc_total_uploaded": (fsize(status['uploaded_memory']
+ status["total_payload_upload"]) + " ("
+ fsize(status["total_upload"]) + ")"),
})
return Storage(status) #Storage for easy templating.
#/utils
#routing: #routing:
urls = ( urls = (
@ -107,15 +55,21 @@ urls = (
"/resume_all(.*)", "resume_all", "/resume_all(.*)", "resume_all",
"/refresh/set(.*)", "refresh_set", "/refresh/set(.*)", "refresh_set",
"/refresh/(.*)", "refresh", "/refresh/(.*)", "refresh",
"/config(.*)","config",
"/home(.*)", "home", "/home(.*)", "home",
"/about(.*)", "about", "/about(.*)", "about",
"/logout(.*)", "logout",
#default-pages #default-pages
"/", "login", "/", "home",
"", "login", "", "home",
#remote-api: #remote-api:
"/remote/torrent/add(.*)", "remote_torrent_add" "/remote/torrent/add(.*)", "remote_torrent_add",
) #static:
"/static/(.*)","static",
"/template/static/(.*)","template_static",
#"/downloads/(.*)","downloads" disabled until it can handle large downloads.
)
#/routing #/routing
#pages: #pages:
@ -133,15 +87,10 @@ class login:
start_session() start_session()
do_redirect() do_redirect()
elif vars.redir: elif vars.redir:
seeother(url('/login',error=1,redir=vars.redir)) seeother(url('/login',error=1, redir=vars.redir))
else: else:
seeother('/login?error=1') seeother('/login?error=1')
class home:
@check_session
def GET(self, name):
do_redirect()
class index: class index:
"page containing the torrent list." "page containing the torrent list."
@auto_refreshed @auto_refreshed
@ -150,7 +99,7 @@ class index:
vars = web.input(sort=None, order=None) vars = web.input(sort=None, order=None)
status_rows = [get_torrent_status(torrent_id) status_rows = [get_torrent_status(torrent_id)
for torrent_id in ws.proxy.get_torrent_state()] for torrent_id in ws.proxy.get_session_state()]
#sorting: #sorting:
if vars.sort: if vars.sort:
@ -164,21 +113,19 @@ class index:
return ws.render.index(status_rows) return ws.render.index(status_rows)
class torrent_info: class torrent_info:
"torrent details"
@auto_refreshed @auto_refreshed
@deluge_page @deluge_page
def GET(self, torrent_id): def GET(self, torrent_id):
return ws.render.torrent_info(get_torrent_status(torrent_id)) return ws.render.torrent_info(get_torrent_status(torrent_id))
class torrent_pause: class torrent_pause:
"start/stop a torrent"
@check_session @check_session
def POST(self, name): def POST(self, name):
vars = web.input(stop = None, start = None, redir = None) vars = web.input(stop = None, start = None, redir = None)
if vars.stop: if vars.stop:
ws.proxy.pause_torrent(vars.stop) ws.proxy.pause_torrent([vars.stop])
elif vars.start: elif vars.start:
ws.proxy.resume_torrent(vars.start) ws.proxy.resume_torrent([vars.start])
do_redirect() do_redirect()
@ -194,7 +141,7 @@ class torrent_add:
if vars.url and vars.torrent.filename: if vars.url and vars.torrent.filename:
error_page(_("Choose an url or a torrent, not both.")) error_page(_("Choose an url or a torrent, not both."))
if vars.url: if vars.url:
ws.proxy.add_torrent_url(vars.url) ws.proxy.add_torrent_url(vars.url )
do_redirect() do_redirect()
elif vars.torrent.filename: elif vars.torrent.filename:
data = vars.torrent.file.read() data = vars.torrent.file.read()
@ -227,8 +174,7 @@ class torrent_delete:
return ws.render.torrent_delete(get_torrent_status(torrent_id)) return ws.render.torrent_delete(get_torrent_status(torrent_id))
@check_session @check_session
def POST(self, name): def POST(self, torrent_id):
torrent_id = name
vars = web.input(data_also = None, torrent_also = None) vars = web.input(data_also = None, torrent_also = None)
data_also = bool(vars.data_also) data_also = bool(vars.data_also)
torrent_also = bool(vars.torrent_also) torrent_also = bool(vars.torrent_also)
@ -238,30 +184,26 @@ class torrent_delete:
class torrent_queue_up: class torrent_queue_up:
@check_session @check_session
def POST(self, name): def POST(self, torrent_id):
torrent_id = name
ws.proxy.queue_up(torrent_id) ws.proxy.queue_up(torrent_id)
do_redirect() do_redirect()
class torrent_queue_down: class torrent_queue_down:
@check_session @check_session
def POST(self, name): def POST(self, torrent_id):
torrent_id = name
ws.proxy.queue_down(torrent_id) ws.proxy.queue_down(torrent_id)
do_redirect() do_redirect()
class pause_all: class pause_all:
@check_session @check_session
def POST(self, name): def POST(self, name):
for torrent_id in ws.proxy.get_torrent_state(): ws.proxy.pause_torrent(ws.proxy.get_session_state())
ws.proxy.pause_torrent(torrent_id)
do_redirect() do_redirect()
class resume_all: class resume_all:
@check_session @check_session
def POST(self, name): def POST(self, name):
for torrent_id in ws.proxy.get_torrent_state(): ws.proxy.resume_torrent(ws.proxy.get_session_state())
ws.proxy.resume_torrent(torrent_id)
do_redirect() do_redirect()
class refresh: class refresh:
@ -287,13 +229,61 @@ class refresh_set:
else: else:
error_page(_('refresh must be > 0')) error_page(_('refresh must be > 0'))
class config:
"""core config
TODO:good validation.
"""
cfg_form = web.form.Form(
web.form.Dropdown('max_download', ws.SPEED_VALUES,
description=_('Download Speed Limit'),
post='%s Kib/sec' % ws.proxy.get_config_value('max_download_speed')
)
,web.form.Dropdown('max_upload', ws.SPEED_VALUES,
description=_('Upload Speed Limit'),
post='%s Kib/sec' % ws.proxy.get_config_value('max_upload_speed')
)
)
@deluge_page
def GET(self, name):
return ws.render.config(self.cfg_form())
def POST(self, name):
vars = web.input(max_download=None, max_upload=None)
#self.config.set("max_download_speed", float(str_bwdown))
raise NotImplementedError('todo')
class home:
@check_session
def GET(self, name):
do_redirect()
class about: class about:
@deluge_page_noauth @deluge_page_noauth
def GET(self, name): def GET(self, name):
return ws.render.about() return ws.render.about()
#/pages class logout:
def POST(self, name):
end_session()
seeother('/login')
class static(static_handler):
base_dir = os.path.join(os.path.dirname(__file__),'static')
class template_static(static_handler):
def get_base_dir(self):
return os.path.join(os.path.dirname(__file__),
'templates/%s/static' % ws.config.get('template'))
class downloads(static_handler):
def GET(self, name):
self.base_dir = ws.proxy.get_config_value('default_download_path')
if not ws.config.get('share_downloads'):
raise Exception('Access to downloads is forbidden.')
return static_handler.GET(self, name)
#/pages
def WebServer(): def WebServer():
return create_webserver(urls, globals()) return create_webserver(urls, globals())
@ -307,4 +297,3 @@ def run():
if __name__ == "__main__": if __name__ == "__main__":
run() run()

View file

@ -1 +1 @@
87 112

View file

@ -1,3 +1,3 @@
#!/usr/bin/env python #!/usr/bin/env python
from deluge_webserver import * import deluge_webserver
web.run(urls, globals()) deluge_webserver.run()

View file

@ -0,0 +1,10 @@
#!/bin/bash
pwd=deluge
url=http://localhost:8112
for arg in "$@"
do
curl -F torrent=@"$arg" -F pwd=$pwd $url/remote/torrent/add
done

Binary file not shown.

After

Width:  |  Height:  |  Size: 799 B

View file

@ -0,0 +1,136 @@
#!/usr/bin/env python
#(c) Martijn Voncken, mvoncken@gmail.com
#Same Licence as web.py 0.22 ->Public Domain
#
"""
static fileserving for web.py
without the need for wsgi wrapper magic.
"""
import webpy022 as web
from webpy022.http import seeother, url
import posixpath
import urlparse
import urllib
import mimetypes
import os
import datetime
import cgi
from StringIO import StringIO
mimetypes.init() # try to read system mime.types
class static_handler:
"""
mostly c&p from SimpleHttpServer
serves relative from start location
"""
base_dir = './'
extensions_map = mimetypes.types_map
def get_base_dir(self):
#override this if you have a config that changes the base dir at runtime
#deluge on windows :(
return self.base_dir
def GET(self, path):
path = self.translate_path(path)
if os.path.isdir(path):
if not path.endswith('/'):
path += "/"
return self.list_directory(path)
ctype = self.guess_type(path)
try:
f = open(path, 'rb')
except IOError:
raise Exception('file not found:%s' % path)
#web.header("404", "File not found")
#return
web.header("Content-type", ctype)
fs = os.fstat(f.fileno())
web.header("Content-Length", str(fs[6]))
web.lastmodified(datetime.datetime.fromtimestamp(fs.st_mtime))
print f.read()
def translate_path(self, path):
"""Translate a /-separated PATH to the local filename syntax.
Components that mean special things to the local file system
(e.g. drive or directory names) are ignored. (XXX They should
probably be diagnosed.)
"""
# abandon query parameters
path = urlparse.urlparse(path)[2]
path = posixpath.normpath(urllib.unquote(path))
words = path.split('/')
words = filter(None, words)
path = self.get_base_dir()
for word in words:
drive, word = os.path.splitdrive(word)
head, word = os.path.split(word)
if word in (os.curdir, os.pardir): continue
path = os.path.join(path, word)
return path
def guess_type(self, path):
base, ext = posixpath.splitext(path)
if ext in self.extensions_map:
return self.extensions_map[ext]
ext = ext.lower()
if ext in self.extensions_map:
return self.extensions_map[ext]
else:
return 'application/octet-stream'
def list_directory(self, path):
"""Helper to produce a directory listing (absent index.html).
Return value is either a file object, or None (indicating an
error). In either case, the headers are sent, making the
interface the same as for send_head().
#TODO ->use web.py +template!
"""
try:
list = os.listdir(path)
except os.error:
web.header('404', "No permission to list directory")
return None
list.sort(key=lambda a: a.lower())
f = StringIO()
displaypath = cgi.escape(urllib.unquote(path))
f.write("<title>Directory listing for %s</title>\n" % displaypath)
f.write("<h2>Directory listing for %s</h2>\n" % displaypath)
f.write("<hr>\n<ul>\n")
for name in list:
fullname = os.path.join(path, name)
displayname = linkname = name
# Append / for directories or @ for symbolic links
if os.path.isdir(fullname):
displayname = name + "/"
linkname = name + "/"
if os.path.islink(fullname):
displayname = name + "@"
# Note: a link to a directory displays with @ and links with /
f.write('<li><a href="%s">%s</a>\n'
% (urllib.quote(linkname), cgi.escape(displayname)))
f.write("</ul>\n<hr>\n")
length = f.tell()
f.seek(0)
web.header("Content-type", "text/html")
web.header("Content-Length", str(length))
print f.read()
if __name__ == '__main__':
#example:
class usr_static(static_handler):
base_dir = os.path.expanduser('~')
urls = ('/relative/(.*)','static_handler',
'/(.*)','usr_static')
web.run(urls,globals())

View file

@ -7,6 +7,7 @@ $:render.header(_('About'))
<li><a href="http://deluge-torrent.org">Deluge</a></li> <li><a href="http://deluge-torrent.org">Deluge</a></li>
<li><a href="http://forum.deluge-torrent.org/viewtopic.php?f=9&t=425"> <li><a href="http://forum.deluge-torrent.org/viewtopic.php?f=9&t=425">
WebUi forum Thread</a> WebUi forum Thread</a>
</li> </li>
<li><a href="http://www.gnu.org/licenses/old-licenses/gpl-2.0.html">GPL v2 <li><a href="http://www.gnu.org/licenses/old-licenses/gpl-2.0.html">GPL v2
</a></li> </a></li>
@ -18,6 +19,7 @@ $:render.header(_('About'))
<ul> <ul>
<li>Martijn Voncken</li> <li>Martijn Voncken</li>
</ul> </ul>
<h3>Template</h3> <h3>Template</h3>
<ul> <ul>
<li>Martijn Voncken</li> <li>Martijn Voncken</li>
@ -27,6 +29,7 @@ $:render.header(_('About'))
<ul> <ul>
<li>Zach Tibbitts</li> <li>Zach Tibbitts</li>
<li>Alon Zakai</li> <li>Alon Zakai</li>
<li>Alon Zakai</li> <li>Alon Zakai</li>
<li>Marcos Pinto</li> <li>Marcos Pinto</li>
<li>Andrew Resch</li> <li>Andrew Resch</li>

View file

@ -0,0 +1,10 @@
$def with (form)
$:render.header(_('Config'))
<div class="error">Not Implemented!</div>
<form method="POST">
$:form.render()
<input type="submit" value="$_('Apply')"/>
</form>
$:render.footer()

View file

@ -51,8 +51,10 @@ $for torrent in torrent_list:
$:render.part_button('GET', '/torrent/add', _('Add torrent'), 'tango/list-add.png') $:render.part_button('GET', '/torrent/add', _('Add torrent'), 'tango/list-add.png')
$:render.part_button('POST', '/pause_all', _('Pause all'), 'tango/media-playback-pause.png') $:render.part_button('POST', '/pause_all', _('Pause all'), 'tango/media-playback-pause.png')
$:render.part_button('POST', '/resume_all', _('Resume all'), 'tango/media-playback-start.png') $:render.part_button('POST', '/resume_all', _('Resume all'), 'tango/media-playback-start.png')
$:render.part_button('POST', '/logout', _('Logout'), 'tango/system-log-out.png')
</div> </div>
$:render.part_refresh() $:part_stats()
$:render.footer() $:render.footer()

View file

@ -2,21 +2,25 @@ $def with (method, url, title, image='')
<div class="deluge_button"> <div class="deluge_button">
<form method="$method" action="$url" class="deluge_button"> <form method="$method" action="$url" class="deluge_button">
<input type="hidden" name="redir" value="$self_url()"> <input type="hidden" name="redir" value="$self_url()">
<button type="submit" class="deluge_button">
$if (get_config('button_style') == 0): $if (get_config('button_style') == 0):
<button type="submit" class="deluge_button" alt="$title">
$title $title
$if image: $if image:
<image src="/static/images/$image" class="button" alt="$title"/> <image src="/static/images/$image" class="button" alt="$title"/>
</button>
$if (get_config('button_style') == 1): $if (get_config('button_style') == 1):
$if image: $if image:
<image src="/static/images/$image" class="button" alt="$title"/> <input type="image" image src="/static/images/$image" class="img_button" alt="$title"/>
$else: $else:
<button type="submit" class="deluge_button" alt="$title">
$title $title
</button>
$if (get_config('button_style') == 2): $if (get_config('button_style') == 2):
<button type="submit" class="deluge_button" alt="$title">
$title $title
</button>
</button>
</form> </form>
</div> </div>

View file

@ -0,0 +1,30 @@
$def with (stats)
<div class="panel" id='refresh_panel'>
$_('Auto refresh:')
$if getcookie('auto_refresh') == '1':
($getcookie('auto_refresh_secs')) $_('seconds') &nbsp;
$:render.part_button('GET', '/refresh/set', _('Set'), 'tango/preferences-system.png')
$:render.part_button('POST', '/refresh/off', _('Disable'), 'tango/process-stop.png')
$else:
$_('Off') &nbsp;
$:render.part_button('POST', '/refresh/on', _('Enable'), 'tango/view-refresh.png')
$#end
</div>
<div class="panel" id='refresh_panel'>
<a href='/config'>
$_('Down Speed') : $stats.download_rate ($stats.max_download)
$_('Up Speed') : $stats.upload_rate ($stats.max_upload)
</a>
(<a href='/about'>$_('About')</a>)
</div>

View file

@ -84,6 +84,7 @@ $:render.part_button('GET', '/torrent/delete/' + str(torrent.id), _('Remove'), '
<br> <br>
<!--
[<a onclick="javascript:toggle_dump()">$_('Debug:Data Dump')</a>] [<a onclick="javascript:toggle_dump()">$_('Debug:Data Dump')</a>]
<pre style="background-color:white;color:black;display:none" id="data_dump"> <pre style="background-color:white;color:black;display:none" id="data_dump">
@ -102,6 +103,8 @@ function toggle_dump(){
} }
} }
</script> </script>
-->
</div> </div>
@ -114,6 +117,6 @@ $:render.part_button('POST', '/torrent/queue/up/' + str(torrent.id), _('Queue Up
$:render.part_button('POST', '/torrent/queue/down/' + str(torrent.id), _('Queue Down'), 'tango/down.png') $:render.part_button('POST', '/torrent/queue/down/' + str(torrent.id), _('Queue Down'), 'tango/down.png')
</div> </div>
$:render.part_refresh() $:part_stats()
$:render.footer() $:render.footer()

View file

@ -0,0 +1,3 @@
</body>
</html>

View file

@ -0,0 +1,12 @@
$def with (title)
<html>
<head>
<title>Deluge(example) : $title</title>
<link rel="icon" href="/static/images/deluge_icon.gif" type="image/gif" />
<link rel="shortcut icon" href="/static/images/deluge_icon.gif" type="image/gif" />
</head>
<body>
<img src="/template/static/example.png">
<a href=/home>[HOME]</a>
<h1>$title</h1>

View file

@ -0,0 +1,42 @@
$def with (torrent_list)
$:render.header(_('Torrent list'))
<form action="/torrent/pause" method="POST">
<table class="torrent_list" border=1>
<tr>
$:(sort_head('calc_state_str', 'S'))
$:(sort_head('queue_pos', '#'))
$:(sort_head('name', _('Name')))
$:(sort_head('progress', _('Progress')))
</tr>
$#4-space indentation is mandatory for for-loops in templetor!
$for torrent in torrent_list:
<tr>
<td><input type="image"
src="/static/images/$(torrent.calc_state_str)16.png"
name="$torrent.action" value="$torrent.id">
</td>
<td>$torrent.queue_pos</td>
<td style="width:100px; overflow:hidden;white-space: nowrap">
<a href="/torrent/info/$torrent.id">$(crop(torrent.name, 40))</a></td>
<td class="progress_bar">
<div class="progress_bar_outer">
<div class="progress_bar" style="width:$(torrent.progress)%">
$torrent.message
</div>
</div>
</td>
</tr>
</table>
</form>
<div class="panel" bgcolor="5555AA">
$:render.part_button('GET', '/torrent/add', _('Add torrent'), 'tango/list-add.png')
$:render.part_button('POST', '/pause_all', _('Pause all'), 'tango/media-playback-pause.png')
$:render.part_button('POST', '/resume_all', _('Resume all'), 'tango/media-playback-start.png')
$:render.part_button('POST', '/logout', _('Logout'), 'tango/system-log-out.png')
</div>
$:render.footer()

View file

@ -1,5 +1,5 @@
revision-id: mvoncken@gmail.com-20070930083408-sv8mo0mi1rbjnfvk revision-id: mvoncken@gmail.com-20070930083408-sv8mo0mi1rbjnfvk
date: 2007-10-23 15:10:08 +0200 date: 2007-10-23 15:10:08 +0200
build-date: 2007-10-23 15:34:50 +0200 build-date: 2007-10-23 15:34:50 +0200
revno: 87 revno: 112
branch-nick: WebUi branch-nick: WebUi

View file

@ -29,40 +29,96 @@
# this exception statement from your version. If you delete this exception # this exception statement from your version. If you delete this exception
# statement from all source files in the program, then also delete it here. # statement from all source files in the program, then also delete it here.
"""
initializes config,render and proxy.
contains all hacks to support running in process0.5 ,run inside-gtk0.5 and
run in process0.6
"""
import os import os
import deluge import deluge
from deluge.common import INSTALL_PREFIX
import random import random
import pickle import pickle
import sys
from webpy022 import template from webpy022 import template
random.seed() random.seed()
path = os.path.dirname(__file__)
config_file = deluge.common.CONFIG_DIR + "/webui.conf" try:
_('translate something')
except:
import gettext
gettext.install('~/') #no translations :(
#a bit hacky way of detecting i'm in the deluge gui or in a process :( try:
if not hasattr(deluge,'pref'): config_dir = deluge.common.CONFIG_DIR
except:
config_dir = os.path.expanduser("~/.config/deluge")
config_file = os.path.join(config_dir,'webui.conf')
session_file = os.path.join(config_dir,'webui.sessions')
class subclassed_render(object):
"""
try to use the html template in configured dir.
not available : use template in /deluge/
"""
def __init__(self, template_dirname, cache=False):
self.base_template = template.render(
os.path.join(path, 'templates/deluge/'),
cache=cache)
self.sub_template = template.render(
os.path.join(path, 'templates/%s/' % template_dirname),
cache=cache)
def __getattr__(self, attr):
if hasattr(self.sub_template, attr):
return getattr(self.sub_template, attr)
else:
return getattr(self.base_template, attr)
def init_process():
globals()['config'] = pickle.load(open(config_file))
globals()['render'] = subclassed_render(config.get('template'),
config.get('cache_templates'))
def init_06():
import deluge.ui.client as proxy
proxy.set_core_uri('http://localhost:58846') #How to configure this?
init_process()
globals()['proxy'] = proxy
def init_05():
import dbus import dbus
init_process()
bus = dbus.SessionBus() bus = dbus.SessionBus()
proxy = bus.get_object("org.deluge_torrent.dbusplugin" proxy = bus.get_object("org.deluge_torrent.dbusplugin"
, "/org/deluge_torrent/DelugeDbusPlugin") , "/org/deluge_torrent/DelugeDbusPlugin")
config = pickle.load(open(config_file)) globals()['proxy'] = proxy
render = template.render('templates/%s/' % config.get('template'),
cache=config.get('cache_templates'))
def init(): def init_gtk_05():
#appy possibly changed config-vars, only called in when runing inside gtk. #appy possibly changed config-vars, only called in when runing inside gtk.
path = os.path.dirname(__file__)
from dbus_interface import get_dbus_manager from dbus_interface import get_dbus_manager
globals()['proxy'] = get_dbus_manager() globals()['proxy'] = get_dbus_manager()
globals()['config'] = deluge.pref.Preferences(config_file, False) globals()['config'] = deluge.pref.Preferences(config_file, False)
globals()['render'] = template.render(os.path.join(path, 'templates/%s/' % globals()['render'] = subclassed_render(config.get('template'),
config.get('template')), cache=config.get('cache_templates')) config.get('cache_templates'))
#hacks to determine environment, TODO: clean up.
if 'env=0.5' in sys.argv:
init_05()
elif not hasattr(deluge, 'common'):
init_06()
elif not hasattr(deluge,'pref'):
init_05()
REVNO = '0.56.stable.' + open(os.path.join(os.path.dirname(__file__),'revno')).read() #constants
REVNO = open(os.path.join(os.path.dirname(__file__),'revno')).read()
VERSION = open(os.path.join(os.path.dirname(__file__),'version')).read() VERSION = open(os.path.join(os.path.dirname(__file__),'version')).read()
TORRENT_KEYS = ['distributed_copies', 'download_payload_rate', TORRENT_KEYS = ['distributed_copies', 'download_payload_rate',
@ -82,3 +138,30 @@ STATE_MESSAGES = (_("Queued"),
_("Finished"), _("Finished"),
_("Seeding"), _("Seeding"),
_("Allocating")) _("Allocating"))
SPEED_VALUES = [
(-1, 'Unlimited'),
(5, '5.0 Kib/sec'),
(10, '10.0 Kib/sec'),
(15, '15.0 Kib/sec'),
(25, '25.0 Kib/sec'),
(30, '30.0 Kib/sec'),
(50, '50.0 Kib/sec'),
(80, '80.0 Kib/sec'),
(300, '300.0 Kib/sec'),
(500, '500.0 Kib/sec')
]
COOKIE_DEFAULTS = {
'auto_refresh_secs':'10'
}
try:
SESSIONS = pickle.load(open(session_file))
except:
SESSIONS = []

View file

@ -45,15 +45,21 @@ import webpy022 as web
from webpy022.webapi import cookies, setcookie as w_setcookie from webpy022.webapi import cookies, setcookie as w_setcookie
from webpy022.http import seeother, url from webpy022.http import seeother, url
from webpy022 import template,changequery as self_url from webpy022 import template,changequery as self_url
from webpy022.utils import Storage
from static_handler import static_handler
from deluge.common import fsize,fspeed
import traceback import traceback
import random import random
from operator import attrgetter from operator import attrgetter
import datetime
import pickle
from md5 import md5
from deluge import common from deluge import common
from webserver_common import REVNO, VERSION from webserver_common import REVNO, VERSION, COOKIE_DEFAULTS
import webserver_common as ws import webserver_common as ws
from debugerror import deluge_debugerror from debugerror import deluge_debugerror
#init: #init:
@ -65,14 +71,22 @@ def setcookie(key, val):
"""add 30 days expires header for persistent cookies""" """add 30 days expires header for persistent cookies"""
return w_setcookie(key, val , expires=2592000) return w_setcookie(key, val , expires=2592000)
SESSIONS = [] #dumb sessions. #really simple sessions, to bad i had to implement them myself.
def start_session(): def start_session():
session_id = str(random.random()) session_id = str(random.random())
SESSIONS.append(session_id) ws.SESSIONS.append(session_id)
if len(ws.SESSIONS) > 20: #save max 20 sessions?
ws.SESSIONS = ws.SESSIONS[-20:]
#not thread safe! , but a verry rare bug.
pickle.dump(ws.SESSIONS, open(ws.session_file,'wb'))
setcookie("session_id", session_id) setcookie("session_id", session_id)
if getcookie('auto_refresh_secs') == None: def end_session():
setcookie('auto_refresh_secs','10') session_id = getcookie("session_id")
if session_id in ws.SESSIONS:
ws.SESSIONS.remove(session_id)
#not thread safe! , but a verry rare bug.
pickle.dump(ws.SESSIONS, open(ws.session_file,'wb'))
def do_redirect(): def do_redirect():
"""for redirects after a POST""" """for redirects after a POST"""
@ -92,7 +106,6 @@ def error_page(error):
print ws.render.error(error) print ws.render.error(error)
def getcookie(key, default=None): def getcookie(key, default=None):
COOKIE_DEFAULTS = {'auto_refresh_secs':'10'}
key = str(key).strip() key = str(key).strip()
ck = cookies() ck = cookies()
val = ck.get(key, default) val = ck.get(key, default)
@ -120,9 +133,8 @@ def check_session(func):
""" """
def deco(self, name): def deco(self, name):
vars = web.input(redir_after_login=None) vars = web.input(redir_after_login=None)
ck = cookies() ck = cookies()
if ck.has_key("session_id") and ck["session_id"] in SESSIONS: if ck.has_key("session_id") and ck["session_id"] in ws.SESSIONS:
return func(self, name) #ok, continue.. return func(self, name) #ok, continue..
elif vars.redir_after_login: elif vars.redir_after_login:
seeother(url("/login",redir=self_url())) seeother(url("/login",redir=self_url()))
@ -154,6 +166,77 @@ def remote(func):
print traceback.format_exc() print traceback.format_exc()
return deco return deco
#utils:
def check_pwd(pwd):
m = md5()
m.update(ws.config.get('pwd_salt'))
m.update(pwd)
return (m.digest() == ws.config.get('pwd_md5'))
def get_stats():
stats = Storage({
'download_rate':fspeed(ws.proxy.get_download_rate()),
'upload_rate':fspeed(ws.proxy.get_upload_rate()),
'max_download':ws.proxy.get_config_value('max_download_speed_bps'),
'max_upload':ws.proxy.get_config_value('max_upload_speed_bps'),
})
if stats.max_upload < 0:
stats.max_upload = _("Unlimited")
else:
stats.max_upload = fspeed(stats.max_upload)
if stats.max_download < 0:
stats.max_download = _("Unlimited")
else:
stats.max_download = fspeed(stats.max_download)
return stats
def get_torrent_status(torrent_id):
"""
helper method.
enhance ws.proxy.get_torrent_status with some extra data
"""
status = Storage(ws.proxy.get_torrent_status(torrent_id,ws.TORRENT_KEYS))
#add missing values for deluge 0.6:
for key in ws.TORRENT_KEYS:
if not key in status:
status[key] = 0
status["id"] = torrent_id
#for naming the status-images
status["calc_state_str"] = "downloading"
if status["paused"]:
status["calc_state_str"] = "inactive"
elif status["is_seed"]:
status["calc_state_str"] = "seeding"
#action for torrent_pause
if status["calc_state_str"] == "inactive":
status["action"] = "start"
else:
status["action"] = "stop"
if status["paused"]:
status["message"] = _("Paused %s%%") % status['progress']
else:
status["message"] = "%s %i%%" % (ws.STATE_MESSAGES[status["state"]]
, status['progress'])
#add some pre-calculated values
status.update({
"calc_total_downloaded" : (fsize(status["total_done"])
+ " (" + fsize(status["total_download"]) + ")"),
"calc_total_uploaded": (fsize(status['uploaded_memory']
+ status["total_payload_upload"]) + " ("
+ fsize(status["total_upload"]) + ")"),
})
return status
#/utils
#template-defs: #template-defs:
def template_crop(text, end): def template_crop(text, end):
if len(text) > end: if len(text) > end:
@ -176,12 +259,15 @@ def template_sort_head(id,name):
return ws.render.sort_column_head(id, name, order, active_up, active_down) return ws.render.sort_column_head(id, name, order, active_up, active_down)
def template_part_stats():
return ws.render.part_stats(get_stats())
def get_config(var): def get_config(var):
return ws.config.get(var) return ws.config.get(var)
template.Template.globals.update({ template.Template.globals.update({
'sort_head': template_sort_head, 'sort_head': template_sort_head,
'part_stats':template_part_stats,
'crop': template_crop, 'crop': template_crop,
'_': _ , #gettext/translations '_': _ , #gettext/translations
'str': str, #because % in templetor is broken. 'str': str, #because % in templetor is broken.
@ -198,145 +284,20 @@ template.Template.globals.update({
}) })
#/template-defs #/template-defs
def create_webserver(urls, methods):
from webpy022.request import webpyfunc
from webpy022 import webapi
from gtk_cherrypy_wsgiserver import CherryPyWSGIServer
func = webapi.wsgifunc(webpyfunc(urls, methods, False))
#------------------------------------------------------------------------------ server_address=("0.0.0.0", int(ws.config.get('port')))
#Some copy and paste from web.py
#mostly caused by /static
#TODO : FIX THIS.
#static-files serving should be moved to the normal webserver!
from SimpleHTTPServer import SimpleHTTPRequestHandler
from BaseHTTPServer import BaseHTTPRequestHandler
from gtk_cherrypy_wsgiserver import CherryPyWSGIServer
from BaseHTTPServer import BaseHTTPRequestHandler
from webpy022.request import webpyfunc
from webpy022 import webapi
import os
import posixpath
import urllib
import urlparse
class RelativeHandler(SimpleHTTPRequestHandler):
def translate_path(self, path):
"""Translate a /-separated PATH to the local filename syntax.
Components that mean special things to the local file system
(e.g. drive or directory names) are ignored. (XXX They should
probably be diagnosed.)
"""
# abandon query parameters
path = urlparse.urlparse(path)[2]
path = posixpath.normpath(urllib.unquote(path))
words = path.split('/')
words = filter(None, words)
path = os.path.dirname(__file__)
for word in words:
drive, word = os.path.splitdrive(word)
head, word = os.path.split(word)
if word in (os.curdir, os.pardir): continue
path = os.path.join(path, word)
return path
class StaticApp(RelativeHandler):
"""WSGI application for serving static files."""
def __init__(self, environ, start_response):
self.headers = []
self.environ = environ
self.start_response = start_response
def send_response(self, status, msg=""):
self.status = str(status) + " " + msg
def send_header(self, name, value):
self.headers.append((name, value))
def end_headers(self):
pass
def log_message(*a): pass
def __iter__(self):
environ = self.environ
self.path = environ.get('PATH_INFO', '')
self.client_address = environ.get('REMOTE_ADDR','-'), \
environ.get('REMOTE_PORT','-')
self.command = environ.get('REQUEST_METHOD', '-')
from cStringIO import StringIO
self.wfile = StringIO() # for capturing error
f = self.send_head()
self.start_response(self.status, self.headers)
if f:
block_size = 16 * 1024
while True:
buf = f.read(block_size)
if not buf:
break
yield buf
f.close()
else:
value = self.wfile.getvalue()
yield value
class WSGIWrapper(BaseHTTPRequestHandler):
"""WSGI wrapper for logging the status and serving static files."""
def __init__(self, app):
self.app = app
self.format = '%s - - [%s] "%s %s %s" - %s'
def __call__(self, environ, start_response):
def xstart_response(status, response_headers, *args):
write = start_response(status, response_headers, *args)
self.log(status, environ)
return write
path = environ.get('PATH_INFO', '')
if path.startswith('/static/'):
return StaticApp(environ, xstart_response)
else:
return self.app(environ, xstart_response)
def log(self, status, environ):
#mvoncken,no logging..
return
outfile = environ.get('wsgi.errors', web.debug)
req = environ.get('PATH_INFO', '_')
protocol = environ.get('ACTUAL_SERVER_PROTOCOL', '-')
method = environ.get('REQUEST_METHOD', '-')
host = "%s:%s" % (environ.get('REMOTE_ADDR','-'),
environ.get('REMOTE_PORT','-'))
#@@ It is really bad to extend from
#@@ BaseHTTPRequestHandler just for this method
time = self.log_date_time_string()
print >> outfile, self.format % (host, time, protocol,
method, req, status)
def create_webserver(urls,methods):
func = webapi.wsgifunc(webpyfunc(urls,methods, False))
server_address=("0.0.0.0",ws.config.get('port'))
func = WSGIWrapper(func)
server = CherryPyWSGIServer(server_address, func, server_name="localhost") server = CherryPyWSGIServer(server_address, func, server_name="localhost")
print "http://%s:%d/" % server_address
print "(created) http://%s:%d/" % server_address
return server return server
#------ #------
__all__ = ['deluge_page_noauth', 'deluge_page', 'remote', __all__ = ['deluge_page_noauth', 'deluge_page', 'remote',
'auto_refreshed', 'check_session', 'auto_refreshed', 'check_session',
'do_redirect', 'error_page','start_session','getcookie' 'do_redirect', 'error_page','start_session','getcookie'
,'create_webserver','setcookie'] ,'setcookie','create_webserver','end_session',
'get_torrent_status', 'check_pwd','static_handler']

View file

@ -54,3 +54,7 @@ plugins/WebSeed/__init__.py
plugins/WebSeed/webseed.glade plugins/WebSeed/webseed.glade
plugins/Scheduler/__init__.py plugins/Scheduler/__init__.py
plugins/Scheduler/plugin.py plugins/Scheduler/plugin.py
plugins/WebUi/__init__.py
plugins/WebUi/webserver_common.py
plugins/WebUi/deluge_webserver.py
plugins/WebUi/scripts/template_strings.py

View file

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2007-10-22 22:35-0500\n" "POT-Creation-Date: 2007-10-27 20:34-0500\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@ -88,7 +88,7 @@ msgstr ""
msgid "<b>Torrent Info</b>" msgid "<b>Torrent Info</b>"
msgstr "" msgstr ""
#: glade/delugegtk.glade:750 #: glade/delugegtk.glade:750 plugins/WebUi/scripts/template_strings.py:8
msgid "Details" msgid "Details"
msgstr "" msgstr ""
@ -140,7 +140,7 @@ msgstr ""
msgid "_Columns" msgid "_Columns"
msgstr "" msgstr ""
#: glade/delugegtk.glade:947 src/interface.py:596 src/files.py:79 #: glade/delugegtk.glade:947 src/interface.py:596 src/files.py:80
msgid "Size" msgid "Size"
msgstr "" msgstr ""
@ -149,11 +149,13 @@ msgid "Status"
msgstr "" msgstr ""
#: glade/delugegtk.glade:965 src/interface.py:602 #: glade/delugegtk.glade:965 src/interface.py:602
#: plugins/WebUi/scripts/template_strings.py:19
msgid "Seeders" msgid "Seeders"
msgstr "" msgstr ""
#: glade/delugegtk.glade:974 src/interface.py:605 #: glade/delugegtk.glade:974 src/interface.py:605
#: plugins/TorrentPeers/__init__.py:72 #: plugins/TorrentPeers/__init__.py:72
#: plugins/WebUi/scripts/template_strings.py:15
msgid "Peers" msgid "Peers"
msgstr "" msgstr ""
@ -171,11 +173,11 @@ msgstr ""
msgid "Time Remaining" msgid "Time Remaining"
msgstr "" msgstr ""
#: glade/delugegtk.glade:1010 #: glade/delugegtk.glade:1010 plugins/WebUi/scripts/template_strings.py:4
msgid "Availability" msgid "Availability"
msgstr "" msgstr ""
#: glade/delugegtk.glade:1019 #: glade/delugegtk.glade:1019 plugins/WebUi/scripts/template_strings.py:21
msgid "Share Ratio" msgid "Share Ratio"
msgstr "" msgstr ""
@ -211,7 +213,7 @@ msgstr ""
msgid "Remove Torrent" msgid "Remove Torrent"
msgstr "" msgstr ""
#: glade/delugegtk.glade:1157 #: glade/delugegtk.glade:1157 plugins/WebUi/scripts/template_strings.py:18
msgid "Remove" msgid "Remove"
msgstr "" msgstr ""
@ -278,7 +280,7 @@ msgstr ""
msgid "Delete downloaded files" msgid "Delete downloaded files"
msgstr "" msgstr ""
#: glade/dgtkpopups.glade:88 #: glade/dgtkpopups.glade:88 plugins/WebUi/scripts/template_strings.py:6
msgid "Delete .torrent file" msgid "Delete .torrent file"
msgstr "" msgstr ""
@ -294,7 +296,7 @@ msgstr ""
msgid "Clear Finished" msgid "Clear Finished"
msgstr "" msgstr ""
#: glade/dgtkpopups.glade:241 #: glade/dgtkpopups.glade:241 plugins/WebUi/scripts/template_strings.py:22
msgid "Speed" msgid "Speed"
msgstr "" msgstr ""
@ -542,6 +544,7 @@ msgid "<b>Seeding</b>"
msgstr "" msgstr ""
#: glade/preferences_dialog.glade:964 src/core.py:91 #: glade/preferences_dialog.glade:964 src/core.py:91
#: plugins/WebUi/webserver_common.py:139
msgid "Seeding" msgid "Seeding"
msgstr "" msgstr ""
@ -646,6 +649,7 @@ msgstr ""
#: glade/preferences_dialog.glade:1478 glade/preferences_dialog.glade:1672 #: glade/preferences_dialog.glade:1478 glade/preferences_dialog.glade:1672
#: glade/preferences_dialog.glade:1866 glade/preferences_dialog.glade:2060 #: glade/preferences_dialog.glade:1866 glade/preferences_dialog.glade:2060
#: plugins/WebUi/scripts/template_strings.py:13
msgid "Password" msgid "Password"
msgstr "" msgstr ""
@ -1004,7 +1008,7 @@ msgstr ""
msgid "Name" msgid "Name"
msgstr "" msgstr ""
#: src/interface.py:614 #: src/interface.py:614 plugins/WebUi/scripts/template_strings.py:10
msgid "ETA" msgid "ETA"
msgstr "" msgstr ""
@ -1088,31 +1092,32 @@ msgstr ""
msgid "Are you sure that you want to remove all seeding torrents?" msgid "Are you sure that you want to remove all seeding torrents?"
msgstr "" msgstr ""
#: src/core.py:85 #: src/core.py:85 plugins/WebUi/webserver_common.py:133
msgid "Queued" msgid "Queued"
msgstr "" msgstr ""
#: src/core.py:86 #: src/core.py:86 plugins/WebUi/webserver_common.py:134
msgid "Checking" msgid "Checking"
msgstr "" msgstr ""
#: src/core.py:87 #: src/core.py:87 plugins/WebUi/webserver_common.py:135
msgid "Connecting" msgid "Connecting"
msgstr "" msgstr ""
#: src/core.py:88 #: src/core.py:88 plugins/WebUi/webserver_common.py:136
msgid "Downloading Metadata" msgid "Downloading Metadata"
msgstr "" msgstr ""
#: src/core.py:89 plugins/BlocklistImport/ui.py:117 #: src/core.py:89 plugins/BlocklistImport/ui.py:117
#: plugins/WebUi/webserver_common.py:137
msgid "Downloading" msgid "Downloading"
msgstr "" msgstr ""
#: src/core.py:90 #: src/core.py:90 plugins/WebUi/webserver_common.py:138
msgid "Finished" msgid "Finished"
msgstr "" msgstr ""
#: src/core.py:92 #: src/core.py:92 plugins/WebUi/webserver_common.py:140
msgid "Allocating" msgid "Allocating"
msgstr "" msgstr ""
@ -1156,11 +1161,11 @@ msgstr ""
msgid "Filename" msgid "Filename"
msgstr "" msgstr ""
#: src/files.py:81 #: src/files.py:82
msgid "Priority" msgid "Priority"
msgstr "" msgstr ""
#: src/files.py:103 #: src/files.py:104
msgid "" msgid ""
"File priority can only be set when using full allocation.\n" "File priority can only be set when using full allocation.\n"
"Please change your preference to disable compact allocation, then remove and " "Please change your preference to disable compact allocation, then remove and "
@ -1175,11 +1180,11 @@ msgstr ""
msgid "Enabled" msgid "Enabled"
msgstr "" msgstr ""
#: src/dialogs.py:433 #: src/dialogs.py:435
msgid "translator-credits" msgid "translator-credits"
msgstr "" msgstr ""
#: src/dialogs.py:434 #: src/dialogs.py:436
msgid "" msgid ""
"Deluge is free software, you can redistribute it and/or\n" "Deluge is free software, you can redistribute it and/or\n"
"modify it under the terms of the GNU General Public\n" "modify it under the terms of the GNU General Public\n"
@ -1196,15 +1201,15 @@ msgid ""
"1301 USA" "1301 USA"
msgstr "" msgstr ""
#: src/dialogs.py:474 #: src/dialogs.py:476
msgid "Choose a .torrent file" msgid "Choose a .torrent file"
msgstr "" msgstr ""
#: src/dialogs.py:479 #: src/dialogs.py:481
msgid "Torrent files" msgid "Torrent files"
msgstr "" msgstr ""
#: src/dialogs.py:483 #: src/dialogs.py:485
msgid "All files" msgid "All files"
msgstr "" msgstr ""
@ -1390,66 +1395,66 @@ msgstr ""
msgid "Torrent Creator" msgid "Torrent Creator"
msgstr "" msgstr ""
#: plugins/TorrentCreator/torrentcreator.glade:103 #: plugins/TorrentCreator/torrentcreator.glade:102
msgid "This torrent will be made from a single file" msgid "This torrent will be made from a single file"
msgstr "" msgstr ""
#: plugins/TorrentCreator/torrentcreator.glade:104 #: plugins/TorrentCreator/torrentcreator.glade:103
msgid "File:" msgid "File:"
msgstr "" msgstr ""
#: plugins/TorrentCreator/torrentcreator.glade:120 #: plugins/TorrentCreator/torrentcreator.glade:119
msgid "This torrent will be made from a directory" msgid "This torrent will be made from a directory"
msgstr "" msgstr ""
#: plugins/TorrentCreator/torrentcreator.glade:121 #: plugins/TorrentCreator/torrentcreator.glade:120
msgid "Folder:" msgid "Folder:"
msgstr "" msgstr ""
#: plugins/TorrentCreator/torrentcreator.glade:138 #: plugins/TorrentCreator/torrentcreator.glade:137
msgid "<b>Source</b>" msgid "<b>Source</b>"
msgstr "" msgstr ""
#: plugins/TorrentCreator/torrentcreator.glade:184 #: plugins/TorrentCreator/torrentcreator.glade:182
msgid "Save Torrent File As:" msgid "Save Torrent File As:"
msgstr "" msgstr ""
#: plugins/TorrentCreator/torrentcreator.glade:237 #: plugins/TorrentCreator/torrentcreator.glade:235
msgid "Load this torrent into Deluge for seeding" msgid "Load this torrent into Deluge for seeding"
msgstr "" msgstr ""
#: plugins/TorrentCreator/torrentcreator.glade:238 #: plugins/TorrentCreator/torrentcreator.glade:236
msgid "Add new torrent to queue" msgid "Add new torrent to queue"
msgstr "" msgstr ""
#: plugins/TorrentCreator/torrentcreator.glade:257 #: plugins/TorrentCreator/torrentcreator.glade:255
msgid "<b>Torrent File</b>" msgid "<b>Torrent File</b>"
msgstr "" msgstr ""
#: plugins/TorrentCreator/torrentcreator.glade:305 #: plugins/TorrentCreator/torrentcreator.glade:302
msgid "<b>Trackers</b>" msgid "<b>Trackers</b>"
msgstr "" msgstr ""
#: plugins/TorrentCreator/torrentcreator.glade:352 #: plugins/TorrentCreator/torrentcreator.glade:348
msgid "<b>Comments</b>" msgid "<b>Comments</b>"
msgstr "" msgstr ""
#: plugins/TorrentCreator/torrentcreator.glade:389 #: plugins/TorrentCreator/torrentcreator.glade:384
msgid "<b>Author</b>" msgid "<b>Author</b>"
msgstr "" msgstr ""
#: plugins/TorrentCreator/torrentcreator.glade:425 #: plugins/TorrentCreator/torrentcreator.glade:423
msgid "Set Private Flag" msgid "Set Private Flag"
msgstr "" msgstr ""
#: plugins/TorrentCreator/torrentcreator.glade:441 #: plugins/TorrentCreator/torrentcreator.glade:436
#: plugins/TorrentCreator/torrentcreator.glade:461 #: plugins/TorrentCreator/torrentcreator.glade:457
msgid "" msgid ""
"The smaller the piece sizes, the more efficient the transfers will be, but " "The smaller the piece sizes, the more efficient the transfers will be, but "
"the actual \".torrent\" file will be larger" "the actual \".torrent\" file will be larger"
msgstr "" msgstr ""
#: plugins/TorrentCreator/torrentcreator.glade:442 #: plugins/TorrentCreator/torrentcreator.glade:437
msgid "" msgid ""
"32 KiB\n" "32 KiB\n"
"64 KiB\n" "64 KiB\n"
@ -1457,13 +1462,14 @@ msgid ""
"256 KiB\n" "256 KiB\n"
"512 KiB\n" "512 KiB\n"
"1024 KiB\n" "1024 KiB\n"
"2048 KiB\n"
msgstr "" msgstr ""
#: plugins/TorrentCreator/torrentcreator.glade:463 #: plugins/TorrentCreator/torrentcreator.glade:459
msgid "Piece Size:" msgid "Piece Size:"
msgstr "" msgstr ""
#: plugins/TorrentCreator/torrentcreator.glade:477 #: plugins/TorrentCreator/torrentcreator.glade:473
msgid "<b>Advanced</b>" msgid "<b>Advanced</b>"
msgstr "" msgstr ""
@ -2102,3 +2108,171 @@ msgid ""
"When set to -1 (unlimited), the global limits in Deluge's preferences will " "When set to -1 (unlimited), the global limits in Deluge's preferences will "
"be obeyed." "be obeyed."
msgstr "" msgstr ""
#: plugins/WebUi/__init__.py:31
msgid "Web User Interface"
msgstr ""
#: plugins/WebUi/__init__.py:34
msgid ""
"A Web based User Interface\n"
"\n"
"Firefox greasemonkey script: http://userscripts.org/scripts/show/12639\n"
"\n"
"Remotely add a file: \"curl -F torrent=@./test1.torrent -F pwd=deluge http://"
"localhost:8112/remote/torrent/add\"\n"
"\n"
"There is support for multiple templates, but just one is included.\n"
"\n"
"Other contributors:\n"
"*somedude : template enhancements.\n"
"*markybob : stability : synced with his changes in deluge-svn.\n"
msgstr ""
#: plugins/WebUi/__init__.py:179
msgid "WebUi Config"
msgstr ""
#: plugins/WebUi/__init__.py:187
msgid "Port Number"
msgstr ""
#: plugins/WebUi/__init__.py:188
msgid "New Password"
msgstr ""
#: plugins/WebUi/__init__.py:189
msgid "New Password(confirm)"
msgstr ""
#: plugins/WebUi/__init__.py:190
msgid "Template"
msgstr ""
#: plugins/WebUi/__init__.py:191
msgid "Button Style"
msgstr ""
#: plugins/WebUi/__init__.py:193
msgid "Cache Templates"
msgstr ""
#: plugins/WebUi/__init__.py:209
msgid "Text and image"
msgstr ""
#: plugins/WebUi/__init__.py:209
msgid "Image Only"
msgstr ""
#: plugins/WebUi/__init__.py:209
msgid "Text Only"
msgstr ""
#: plugins/WebUi/__init__.py:243
msgid "Confirmed Password <> New Password\n"
msgstr ""
#: plugins/WebUi/webserver_common.py:48
msgid "translate something"
msgstr ""
#: plugins/WebUi/deluge_webserver.py:142
msgid "Choose an url or a torrent, not both."
msgstr ""
#: plugins/WebUi/deluge_webserver.py:153
msgid "no data."
msgstr ""
#: plugins/WebUi/deluge_webserver.py:230
msgid "refresh must be > 0"
msgstr ""
#: plugins/WebUi/deluge_webserver.py:238
msgid "Download Speed Limit"
msgstr ""
#: plugins/WebUi/deluge_webserver.py:242
msgid "Upload Speed Limit"
msgstr ""
#: plugins/WebUi/scripts/template_strings.py:1
msgid "# Of Files"
msgstr ""
#: plugins/WebUi/scripts/template_strings.py:2
msgid "About"
msgstr ""
#: plugins/WebUi/scripts/template_strings.py:3
msgid "Auto refresh:"
msgstr ""
#: plugins/WebUi/scripts/template_strings.py:5
msgid "Debug:Data Dump"
msgstr ""
#: plugins/WebUi/scripts/template_strings.py:7
msgid "Delete downloaded files."
msgstr ""
#: plugins/WebUi/scripts/template_strings.py:9
msgid "Downloaded"
msgstr ""
#: plugins/WebUi/scripts/template_strings.py:11
msgid "Next Announce"
msgstr ""
#: plugins/WebUi/scripts/template_strings.py:12
msgid "Off"
msgstr ""
#: plugins/WebUi/scripts/template_strings.py:14
msgid "Password is invalid,try again"
msgstr ""
#: plugins/WebUi/scripts/template_strings.py:16
msgid "Pieces"
msgstr ""
#: plugins/WebUi/scripts/template_strings.py:17
msgid "Refresh page every:"
msgstr ""
#: plugins/WebUi/scripts/template_strings.py:20
msgid "Set"
msgstr ""
#: plugins/WebUi/scripts/template_strings.py:23
msgid "Submit"
msgstr ""
#: plugins/WebUi/scripts/template_strings.py:24
msgid "Total Size"
msgstr ""
#: plugins/WebUi/scripts/template_strings.py:25
msgid "Tracker"
msgstr ""
#: plugins/WebUi/scripts/template_strings.py:26
msgid "Tracker Status"
msgstr ""
#: plugins/WebUi/scripts/template_strings.py:27
msgid "Upload torrent"
msgstr ""
#: plugins/WebUi/scripts/template_strings.py:28
msgid "Uploaded"
msgstr ""
#: plugins/WebUi/scripts/template_strings.py:29
msgid "Url"
msgstr ""
#: plugins/WebUi/scripts/template_strings.py:30
msgid "seconds"
msgstr ""

2107
po/eo.po Normal file

File diff suppressed because it is too large Load diff

2146
po/ka.po Normal file

File diff suppressed because it is too large Load diff

2105
po/si.po Normal file

File diff suppressed because it is too large Load diff

2111
po/vi.po Normal file

File diff suppressed because it is too large Load diff

View file

@ -28,7 +28,7 @@
NAME = "deluge" NAME = "deluge"
FULLNAME = "Deluge BitTorrent Client" FULLNAME = "Deluge BitTorrent Client"
VERSION = "0.5.5.95" VERSION = "0.5.6.1"
AUTHOR = "Zach Tibbitts, Alon Zakai, Marcos Pinto, Andrew Resch, Alex Dedul" AUTHOR = "Zach Tibbitts, Alon Zakai, Marcos Pinto, Andrew Resch, Alex Dedul"
EMAIL = "zach@collegegeek.org, kripkensteiner@gmail.com, marcospinto@dipconsultants.com, alonzakai@gmail.com, rotmer@gmail.com" EMAIL = "zach@collegegeek.org, kripkensteiner@gmail.com, marcospinto@dipconsultants.com, alonzakai@gmail.com, rotmer@gmail.com"
DESCRIPTION = "A bittorrent client written in PyGTK" DESCRIPTION = "A bittorrent client written in PyGTK"

View file

@ -32,7 +32,7 @@ import os
import xdg.BaseDirectory import xdg.BaseDirectory
PROGRAM_NAME = "Deluge" PROGRAM_NAME = "Deluge"
PROGRAM_VERSION = "0.5.5.95" PROGRAM_VERSION = "0.5.6.1"
CLIENT_CODE = "DE" CLIENT_CODE = "DE"
CLIENT_VERSION = "".join(PROGRAM_VERSION.split('.'))+"0"*(4 - len(PROGRAM_VERSION.split('.'))) CLIENT_VERSION = "".join(PROGRAM_VERSION.split('.'))+"0"*(4 - len(PROGRAM_VERSION.split('.')))

View file

@ -646,7 +646,6 @@ static PyObject *torrent_set_max_connections_global(PyObject *self, PyObject *ar
// printf("Setting max connections: %d\r\n", max_conn); // printf("Setting max connections: %d\r\n", max_conn);
M_ses->set_max_connections(max_conn); M_ses->set_max_connections(max_conn);
Py_INCREF(Py_None); return Py_None; Py_INCREF(Py_None); return Py_None;
} }
@ -1217,11 +1216,12 @@ static PyObject *torrent_get_session_info(PyObject *self, PyObject *args)
{ {
session_status s = M_ses->status(); session_status s = M_ses->status();
return Py_BuildValue("{s:l,s:f,s:f,s:l,s:f,s:f}", return Py_BuildValue("{s:l,s:f,s:f,s:ls:l,s:f,s:f}",
"has_incoming_connections", long(s.has_incoming_connections), "has_incoming_connections", long(s.has_incoming_connections),
"upload_rate", float(s.payload_upload_rate), "upload_rate", float(s.payload_upload_rate),
"download_rate", float(s.payload_download_rate), "download_rate", float(s.payload_download_rate),
"num_peers", long(s.num_peers), "num_peers", long(s.num_peers),
"num_connections", long(M_ses->num_connections()),
"total_downloaded", float(s.total_payload_download), "total_downloaded", float(s.total_payload_download),
"total_uploaded", float(s.total_payload_upload)); "total_uploaded", float(s.total_payload_upload));
} }
@ -1778,8 +1778,7 @@ static PyObject *torrent_replace_trackers(PyObject *self, PyObject *args)
if (PyErr_Occurred()) if (PyErr_Occurred())
return NULL; return NULL;
torrent_handle& h = M_torrents->at(index).handle; if (M_torrents->at(index).handle.is_valid()){
if (h.is_valid()){
std::vector<libtorrent::announce_entry> trackerlist; std::vector<libtorrent::announce_entry> trackerlist;
std::istringstream trackers(tracker); std::istringstream trackers(tracker);
std::string line; std::string line;
@ -1787,8 +1786,8 @@ static PyObject *torrent_replace_trackers(PyObject *self, PyObject *args)
libtorrent::announce_entry a_entry(line); libtorrent::announce_entry a_entry(line);
trackerlist.push_back(a_entry); trackerlist.push_back(a_entry);
} }
h.replace_trackers(trackerlist); M_torrents->at(index).handle.replace_trackers(trackerlist);
h.force_reannounce(); M_torrents->at(index).handle.force_reannounce();
} }
Py_INCREF(Py_None); return Py_None; Py_INCREF(Py_None); return Py_None;
} }

View file

@ -43,7 +43,8 @@ import gtk
def cell_data_speed(column, cell, model, iter, data): def cell_data_speed(column, cell, model, iter, data):
speed = int(model.get_value(iter, data)) speed = int(model.get_value(iter, data))
speed_str = common.fspeed(speed) if speed<100: speed_str = ""
else: speed_str = common.fspeed(speed)
cell.set_property('text', speed_str) cell.set_property('text', speed_str)
def cell_data_size(column, cell, model, iter, data): def cell_data_size(column, cell, model, iter, data):

View file

@ -428,6 +428,8 @@ def show_about_dialog(window):
abt.set_transient_for(window) abt.set_transient_for(window)
abt.set_name(common.PROGRAM_NAME) abt.set_name(common.PROGRAM_NAME)
abt.set_version(common.PROGRAM_VERSION) abt.set_version(common.PROGRAM_VERSION)
abt.set_comments("Deluge is a full-featured BitTorrent\nclient for Linux, Mac OS X and Windows")
abt.set_documenters(["Marcos Pinto"])
abt.set_authors(["Zach Tibbitts", "Alon Zakai", "Marcos Pinto", "Andrew Resch", "Alex Dedul"]) abt.set_authors(["Zach Tibbitts", "Alon Zakai", "Marcos Pinto", "Andrew Resch", "Alex Dedul"])
abt.set_artists(["Andrew Wedderburn"]) abt.set_artists(["Andrew Wedderburn"])
abt.set_translator_credits(_("translator-credits")) abt.set_translator_credits(_("translator-credits"))

View file

@ -947,11 +947,17 @@ window, please enter your password"))
unique_id = self.manager.get_torrent_unique_id(torrent) unique_id = self.manager.get_torrent_unique_id(torrent)
try: try:
if self.manager.unique_IDs[unique_id].trackers: if self.manager.unique_IDs[unique_id].trackers:
self.manager.replace_trackers(unique_id, \ try:
self.manager.replace_trackers(unique_id, \
self.manager.unique_IDs[unique_id].trackers) self.manager.unique_IDs[unique_id].trackers)
except:
pass
if self.manager.unique_IDs[unique_id].uploaded_memory: if self.manager.unique_IDs[unique_id].uploaded_memory:
self.manager.unique_IDs[unique_id].initial_uploaded_memory \ try:
self.manager.unique_IDs[unique_id].initial_uploaded_memory \
= self.manager.unique_IDs[unique_id].uploaded_memory = self.manager.unique_IDs[unique_id].uploaded_memory
except:
pass
except AttributeError: except AttributeError:
pass pass
@ -1091,7 +1097,7 @@ window, please enter your password"))
def update_statusbar_and_tray(self): def update_statusbar_and_tray(self):
plugin_messages = self.plugins.get_plugin_tray_messages() plugin_messages = self.plugins.get_plugin_tray_messages()
core_state = self.manager.get_state() core_state = self.manager.get_state()
connections = core_state['num_peers'] connections = core_state['num_connections']
if self.config.get("max_connections_global") < 0 : if self.config.get("max_connections_global") < 0 :
max_connections = _("Unlimited") max_connections = _("Unlimited")
else: else: