major lt upgrade to trunk and turn on options for upnp, natpmp and utpex
This commit is contained in:
parent
cdf34e92e9
commit
55e5b75e54
|
@ -315,108 +315,24 @@
|
|||
<property name="n_rows">4</property>
|
||||
<property name="n_columns">2</property>
|
||||
<child>
|
||||
<widget class="GtkSpinButton" id="spin_max_upload">
|
||||
<widget class="GtkAlignment" id="alignment18">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="tooltip" translatable="yes">The maximum upload rate for all torrents. Set -1 for unlimited.</property>
|
||||
<property name="xalign">1</property>
|
||||
<property name="adjustment">-1 -1 9000 1 10 10</property>
|
||||
<property name="climb_rate">1</property>
|
||||
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
|
||||
<property name="right_padding">10</property>
|
||||
<child>
|
||||
<widget class="GtkLabel" id="label55">
|
||||
<property name="visible">True</property>
|
||||
<property name="xalign">0</property>
|
||||
<property name="label" translatable="yes">Maximum Upload Rate (KB/s):</property>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="right_attach">2</property>
|
||||
<property name="top_attach">3</property>
|
||||
<property name="bottom_attach">4</property>
|
||||
<property name="x_options">GTK_FILL</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<widget class="GtkSpinButton" id="spin_max_download">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">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="tooltip" translatable="yes">The maximum download rate for all torrents. Set -1 for unlimited.</property>
|
||||
<property name="xalign">1</property>
|
||||
<property name="adjustment">-1 -1 9000 1 10 10</property>
|
||||
<property name="climb_rate">1</property>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="right_attach">2</property>
|
||||
<property name="top_attach">2</property>
|
||||
<property name="bottom_attach">3</property>
|
||||
<property name="x_options">GTK_FILL</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<widget class="GtkSpinButton" id="spin_num_upload">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="tooltip" translatable="yes">The maximum number of upload slots. Set -1 for unlimited.</property>
|
||||
<property name="xalign">1</property>
|
||||
<property name="adjustment">-1 -1 1000 1 10 10</property>
|
||||
<property name="climb_rate">1</property>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="right_attach">2</property>
|
||||
<property name="top_attach">1</property>
|
||||
<property name="bottom_attach">2</property>
|
||||
<property name="x_options">GTK_FILL</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<widget class="GtkSpinButton" id="spin_num_download">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="tooltip" translatable="yes">The maximum number of connections allowed. Set -1 for unlimited.</property>
|
||||
<property name="xalign">1</property>
|
||||
<property name="adjustment">-1 -1 1000 1 10 10</property>
|
||||
<property name="climb_rate">1</property>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="right_attach">2</property>
|
||||
<property name="x_options">GTK_FILL</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<widget class="GtkAlignment" id="alignment4">
|
||||
<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="right_padding">10</property>
|
||||
<child>
|
||||
<widget class="GtkLabel" id="label54">
|
||||
<property name="visible">True</property>
|
||||
<property name="xalign">0</property>
|
||||
<property name="label" translatable="yes">Maximum Connections:</property>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="x_options">GTK_FILL</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<widget class="GtkAlignment" id="alignment5">
|
||||
<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="right_padding">10</property>
|
||||
<child>
|
||||
<widget class="GtkLabel" id="label53">
|
||||
<property name="visible">True</property>
|
||||
<property name="xalign">0</property>
|
||||
<property name="label" translatable="yes">Upload Slots:</property>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="top_attach">1</property>
|
||||
<property name="bottom_attach">2</property>
|
||||
<property name="x_options">GTK_FILL</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<widget class="GtkAlignment" id="alignment14">
|
||||
<property name="visible">True</property>
|
||||
|
@ -437,19 +353,103 @@
|
|||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<widget class="GtkAlignment" id="alignment18">
|
||||
<widget class="GtkAlignment" id="alignment5">
|
||||
<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="right_padding">10</property>
|
||||
<child>
|
||||
<widget class="GtkLabel" id="label55">
|
||||
<widget class="GtkLabel" id="label53">
|
||||
<property name="visible">True</property>
|
||||
<property name="xalign">0</property>
|
||||
<property name="label" translatable="yes">Maximum Upload Rate (KB/s):</property>
|
||||
<property name="label" translatable="yes">Upload Slots:</property>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="top_attach">1</property>
|
||||
<property name="bottom_attach">2</property>
|
||||
<property name="x_options">GTK_FILL</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<widget class="GtkAlignment" id="alignment4">
|
||||
<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="right_padding">10</property>
|
||||
<child>
|
||||
<widget class="GtkLabel" id="label54">
|
||||
<property name="visible">True</property>
|
||||
<property name="xalign">0</property>
|
||||
<property name="label" translatable="yes">Maximum Connections:</property>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="x_options">GTK_FILL</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<widget class="GtkSpinButton" id="spin_num_download">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="tooltip" translatable="yes">The maximum number of connections allowed. Set -1 for unlimited.</property>
|
||||
<property name="xalign">1</property>
|
||||
<property name="adjustment">-1 -1 1000 1 10 10</property>
|
||||
<property name="climb_rate">1</property>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="right_attach">2</property>
|
||||
<property name="x_options">GTK_FILL</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<widget class="GtkSpinButton" id="spin_num_upload">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="tooltip" translatable="yes">The maximum number of upload slots. Set -1 for unlimited.</property>
|
||||
<property name="xalign">1</property>
|
||||
<property name="adjustment">-1 -1 1000 1 10 10</property>
|
||||
<property name="climb_rate">1</property>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="right_attach">2</property>
|
||||
<property name="top_attach">1</property>
|
||||
<property name="bottom_attach">2</property>
|
||||
<property name="x_options">GTK_FILL</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<widget class="GtkSpinButton" id="spin_max_download">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">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="tooltip" translatable="yes">The maximum download rate for all torrents. Set -1 for unlimited.</property>
|
||||
<property name="xalign">1</property>
|
||||
<property name="adjustment">-1 -1 9000 1 10 10</property>
|
||||
<property name="climb_rate">1</property>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="right_attach">2</property>
|
||||
<property name="top_attach">2</property>
|
||||
<property name="bottom_attach">3</property>
|
||||
<property name="x_options">GTK_FILL</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<widget class="GtkSpinButton" id="spin_max_upload">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="tooltip" translatable="yes">The maximum upload rate for all torrents. Set -1 for unlimited.</property>
|
||||
<property name="xalign">1</property>
|
||||
<property name="adjustment">-1 -1 9000 1 10 10</property>
|
||||
<property name="climb_rate">1</property>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="right_attach">2</property>
|
||||
<property name="top_attach">3</property>
|
||||
<property name="bottom_attach">4</property>
|
||||
<property name="x_options">GTK_FILL</property>
|
||||
|
@ -758,7 +758,7 @@
|
|||
<child>
|
||||
<widget class="GtkCheckButton" id="chk_upnp">
|
||||
<property name="visible">True</property>
|
||||
<property name="sensitive">False</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="label" translatable="yes">UPnP</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="response_id">0</property>
|
||||
|
@ -772,7 +772,7 @@
|
|||
<child>
|
||||
<widget class="GtkCheckButton" id="chk_natpmp">
|
||||
<property name="visible">True</property>
|
||||
<property name="sensitive">False</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="label" translatable="yes">NAT-PMP</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="response_id">0</property>
|
||||
|
@ -787,7 +787,7 @@
|
|||
<child>
|
||||
<widget class="GtkCheckButton" id="chk_utpex">
|
||||
<property name="visible">True</property>
|
||||
<property name="sensitive">False</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="label" translatable="yes">UT PeX</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="response_id">0</property>
|
||||
|
@ -806,7 +806,7 @@
|
|||
<child>
|
||||
<widget class="GtkLabel" id="label17">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes"><b>Network Extras</b> - Always on</property>
|
||||
<property name="label" translatable="yes"><b>Network Extras</b></property>
|
||||
<property name="use_markup">True</property>
|
||||
</widget>
|
||||
<packing>
|
||||
|
|
|
@ -81,8 +81,6 @@ namespace libtorrent
|
|||
{ return std::auto_ptr<alert>(new tracker_warning_alert(*this)); }
|
||||
};
|
||||
|
||||
|
||||
|
||||
struct TORRENT_EXPORT tracker_reply_alert: torrent_alert
|
||||
{
|
||||
tracker_reply_alert(torrent_handle const& h
|
||||
|
@ -186,6 +184,26 @@ namespace libtorrent
|
|||
{ return std::auto_ptr<alert>(new torrent_finished_alert(*this)); }
|
||||
};
|
||||
|
||||
struct TORRENT_EXPORT storage_moved_alert: torrent_alert
|
||||
{
|
||||
storage_moved_alert(torrent_handle const& h, std::string const& path)
|
||||
: torrent_alert(h, alert::warning, path)
|
||||
{}
|
||||
|
||||
virtual std::auto_ptr<alert> clone() const
|
||||
{ return std::auto_ptr<alert>(new storage_moved_alert(*this)); }
|
||||
};
|
||||
|
||||
struct TORRENT_EXPORT torrent_paused_alert: torrent_alert
|
||||
{
|
||||
torrent_paused_alert(torrent_handle const& h, std::string const& msg)
|
||||
: torrent_alert(h, alert::warning, msg)
|
||||
{}
|
||||
|
||||
virtual std::auto_ptr<alert> clone() const
|
||||
{ return std::auto_ptr<alert>(new torrent_paused_alert(*this)); }
|
||||
};
|
||||
|
||||
struct TORRENT_EXPORT url_seed_alert: torrent_alert
|
||||
{
|
||||
url_seed_alert(
|
||||
|
|
|
@ -68,6 +68,7 @@
|
|||
#include "asio/system_error.hpp"
|
||||
#include "asio/thread.hpp"
|
||||
#include "asio/time_traits.hpp"
|
||||
#include "asio/version.hpp"
|
||||
#include "asio/write.hpp"
|
||||
|
||||
#endif // ASIO_HPP
|
||||
|
|
|
@ -40,8 +40,6 @@ namespace asio {
|
|||
* @e Distinct @e objects: Safe.@n
|
||||
* @e Shared @e objects: Unsafe.
|
||||
*
|
||||
* @sa @ref deadline_timer_reset
|
||||
*
|
||||
* @par Examples
|
||||
* Performing a blocking wait:
|
||||
* @code
|
||||
|
@ -75,6 +73,45 @@ namespace asio {
|
|||
* // Start an asynchronous wait.
|
||||
* timer.async_wait(handler);
|
||||
* @endcode
|
||||
*
|
||||
* @par Changing an active deadline_timer's expiry time
|
||||
*
|
||||
* Changing the expiry time of a timer while there are pending asynchronous
|
||||
* waits causes those wait operations to be cancelled. To ensure that the action
|
||||
* associated with the timer is performed only once, use something like this:
|
||||
* used:
|
||||
*
|
||||
* @code
|
||||
* void on_some_event()
|
||||
* {
|
||||
* if (my_timer.expires_from_now(seconds(5)) > 0)
|
||||
* {
|
||||
* // We managed to cancel the timer. Start new asynchronous wait.
|
||||
* my_timer.async_wait(on_timeout);
|
||||
* }
|
||||
* else
|
||||
* {
|
||||
* // Too late, timer has already expired!
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* void on_timeout(const asio::error_code& e)
|
||||
* {
|
||||
* if (e != asio::error::operation_aborted)
|
||||
* {
|
||||
* // Timer was not cancelled, take necessary action.
|
||||
* }
|
||||
* }
|
||||
* @endcode
|
||||
*
|
||||
* @li The asio::basic_deadline_timer::expires_from_now() function
|
||||
* cancels any pending asynchronous waits, and returns the number of
|
||||
* asynchronous waits that were cancelled. If it returns 0 then you were too
|
||||
* late and the wait handler has already been executed, or will soon be
|
||||
* executed. If it returns 1 then the wait handler was successfully cancelled.
|
||||
*
|
||||
* @li If a wait handler is cancelled, the asio::error_code passed to
|
||||
* it contains the value asio::error::operation_aborted.
|
||||
*/
|
||||
template <typename Time,
|
||||
typename TimeTraits = asio::time_traits<Time>,
|
||||
|
@ -197,9 +234,6 @@ public:
|
|||
* operations will be cancelled. The handler for each cancelled operation will
|
||||
* be invoked with the asio::error::operation_aborted error code.
|
||||
*
|
||||
* See @ref deadline_timer_reset for more information on altering the expiry
|
||||
* time of an active timer.
|
||||
*
|
||||
* @param expiry_time The expiry time to be used for the timer.
|
||||
*
|
||||
* @return The number of asynchronous operations that were cancelled.
|
||||
|
@ -221,9 +255,6 @@ public:
|
|||
* operations will be cancelled. The handler for each cancelled operation will
|
||||
* be invoked with the asio::error::operation_aborted error code.
|
||||
*
|
||||
* See @ref deadline_timer_reset for more information on altering the expiry
|
||||
* time of an active timer.
|
||||
*
|
||||
* @param expiry_time The expiry time to be used for the timer.
|
||||
*
|
||||
* @param ec Set to indicate what error occurred, if any.
|
||||
|
@ -252,9 +283,6 @@ public:
|
|||
* operations will be cancelled. The handler for each cancelled operation will
|
||||
* be invoked with the asio::error::operation_aborted error code.
|
||||
*
|
||||
* See @ref deadline_timer_reset for more information on altering the expiry
|
||||
* time of an active timer.
|
||||
*
|
||||
* @param expiry_time The expiry time to be used for the timer.
|
||||
*
|
||||
* @return The number of asynchronous operations that were cancelled.
|
||||
|
@ -276,9 +304,6 @@ public:
|
|||
* operations will be cancelled. The handler for each cancelled operation will
|
||||
* be invoked with the asio::error::operation_aborted error code.
|
||||
*
|
||||
* See @ref deadline_timer_reset for more information on altering the expiry
|
||||
* time of an active timer.
|
||||
*
|
||||
* @param expiry_time The expiry time to be used for the timer.
|
||||
*
|
||||
* @param ec Set to indicate what error occurred, if any.
|
||||
|
@ -349,49 +374,6 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @page deadline_timer_reset Changing an active deadline_timer's expiry time
|
||||
*
|
||||
* Changing the expiry time of a timer while there are pending asynchronous
|
||||
* waits causes those wait operations to be cancelled. To ensure that the action
|
||||
* associated with the timer is performed only once, use something like this:
|
||||
* used:
|
||||
*
|
||||
* @code
|
||||
* void on_some_event()
|
||||
* {
|
||||
* if (my_timer.expires_from_now(seconds(5)) > 0)
|
||||
* {
|
||||
* // We managed to cancel the timer. Start new asynchronous wait.
|
||||
* my_timer.async_wait(on_timeout);
|
||||
* }
|
||||
* else
|
||||
* {
|
||||
* // Too late, timer has already expired!
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* void on_timeout(const asio::error_code& e)
|
||||
* {
|
||||
* if (e != asio::error::operation_aborted)
|
||||
* {
|
||||
* // Timer was not cancelled, take necessary action.
|
||||
* }
|
||||
* }
|
||||
* @endcode
|
||||
*
|
||||
* @li The asio::basic_deadline_timer::expires_from_now() function
|
||||
* cancels any pending asynchronous waits, and returns the number of
|
||||
* asynchronous waits that were cancelled. If it returns 0 then you were too
|
||||
* late and the wait handler has already been executed, or will soon be
|
||||
* executed. If it returns 1 then the wait handler was successfully cancelled.
|
||||
*
|
||||
* @li If a wait handler is cancelled, the asio::error_code passed to
|
||||
* it contains the value asio::error::operation_aborted.
|
||||
*
|
||||
* @sa asio::basic_deadline_timer
|
||||
*/
|
||||
|
||||
} // namespace asio
|
||||
|
||||
#include "asio/detail/pop_options.hpp"
|
||||
|
|
|
@ -34,6 +34,14 @@
|
|||
# endif // defined(_HAS_ITERATOR_DEBUGGING)
|
||||
#endif // defined(BOOST_MSVC)
|
||||
|
||||
#if defined(__GNUC__)
|
||||
# if defined(_GLIBCXX_DEBUG)
|
||||
# if !defined(ASIO_DISABLE_BUFFER_DEBUGGING)
|
||||
# define ASIO_ENABLE_BUFFER_DEBUGGING
|
||||
# endif // !defined(ASIO_DISABLE_BUFFER_DEBUGGING)
|
||||
# endif // defined(_GLIBCXX_DEBUG)
|
||||
#endif // defined(__GNUC__)
|
||||
|
||||
#if defined(ASIO_ENABLE_BUFFER_DEBUGGING)
|
||||
# include "asio/detail/push_options.hpp"
|
||||
# include <boost/function.hpp>
|
||||
|
|
|
@ -24,6 +24,8 @@
|
|||
#include <linux/version.h>
|
||||
#include "asio/detail/pop_options.hpp"
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION (2,5,45) // Only kernels >= 2.5.45.
|
||||
|
||||
// Define this to indicate that epoll is supported on the target platform.
|
||||
#define ASIO_HAS_EPOLL 1
|
||||
|
||||
|
@ -36,6 +38,7 @@ class epoll_reactor;
|
|||
} // namespace detail
|
||||
} // namespace asio
|
||||
|
||||
#endif // LINUX_VERSION_CODE >= KERNEL_VERSION (2,5,45)
|
||||
#endif // defined(__linux__)
|
||||
#endif // !defined(ASIO_DISABLE_EPOLL)
|
||||
|
||||
|
|
|
@ -164,6 +164,10 @@ struct addrinfo_emulation
|
|||
# define IPPROTO_IPV6 41
|
||||
#endif
|
||||
|
||||
#if !defined(IPV6_UNICAST_HOPS)
|
||||
# define IPV6_UNICAST_HOPS 4
|
||||
#endif
|
||||
|
||||
#if !defined(IPV6_MULTICAST_IF)
|
||||
# define IPV6_MULTICAST_IF 9
|
||||
#endif
|
||||
|
|
|
@ -350,6 +350,22 @@ public:
|
|||
socket_ops::setsockopt(impl.socket_,
|
||||
option.level(impl.protocol_), option.name(impl.protocol_),
|
||||
option.data(impl.protocol_), option.size(impl.protocol_), ec);
|
||||
|
||||
#if defined(__MACH__) && defined(__APPLE__) \
|
||||
|| defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__)
|
||||
// To implement portable behaviour for SO_REUSEADDR with UDP sockets we
|
||||
// need to also set SO_REUSEPORT on BSD-based platforms.
|
||||
if (!ec && impl.protocol_.type() == SOCK_DGRAM
|
||||
&& option.level(impl.protocol_) == SOL_SOCKET
|
||||
&& option.name(impl.protocol_) == SO_REUSEADDR)
|
||||
{
|
||||
asio::error_code ignored_ec;
|
||||
socket_ops::setsockopt(impl.socket_, SOL_SOCKET, SO_REUSEPORT,
|
||||
option.data(impl.protocol_), option.size(impl.protocol_),
|
||||
ignored_ec);
|
||||
}
|
||||
#endif
|
||||
|
||||
return ec;
|
||||
}
|
||||
}
|
||||
|
@ -506,6 +522,18 @@ public:
|
|||
return 0;
|
||||
}
|
||||
|
||||
// Make socket non-blocking if user wants non-blocking.
|
||||
if (impl.flags_ & implementation_type::user_set_non_blocking)
|
||||
{
|
||||
if (!(impl.flags_ & implementation_type::internal_non_blocking))
|
||||
{
|
||||
ioctl_arg_type non_blocking = 1;
|
||||
if (socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking, ec))
|
||||
return 0;
|
||||
impl.flags_ |= implementation_type::internal_non_blocking;
|
||||
}
|
||||
}
|
||||
|
||||
// Send the data.
|
||||
for (;;)
|
||||
{
|
||||
|
@ -668,6 +696,18 @@ public:
|
|||
asio::buffer_size(buffer));
|
||||
}
|
||||
|
||||
// Make socket non-blocking if user wants non-blocking.
|
||||
if (impl.flags_ & implementation_type::user_set_non_blocking)
|
||||
{
|
||||
if (!(impl.flags_ & implementation_type::internal_non_blocking))
|
||||
{
|
||||
ioctl_arg_type non_blocking = 1;
|
||||
if (socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking, ec))
|
||||
return 0;
|
||||
impl.flags_ |= implementation_type::internal_non_blocking;
|
||||
}
|
||||
}
|
||||
|
||||
// Send the data.
|
||||
for (;;)
|
||||
{
|
||||
|
@ -823,6 +863,18 @@ public:
|
|||
return 0;
|
||||
}
|
||||
|
||||
// Make socket non-blocking if user wants non-blocking.
|
||||
if (impl.flags_ & implementation_type::user_set_non_blocking)
|
||||
{
|
||||
if (!(impl.flags_ & implementation_type::internal_non_blocking))
|
||||
{
|
||||
ioctl_arg_type non_blocking = 1;
|
||||
if (socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking, ec))
|
||||
return 0;
|
||||
impl.flags_ |= implementation_type::internal_non_blocking;
|
||||
}
|
||||
}
|
||||
|
||||
// Receive some data.
|
||||
for (;;)
|
||||
{
|
||||
|
@ -1005,6 +1057,18 @@ public:
|
|||
asio::buffer_size(buffer));
|
||||
}
|
||||
|
||||
// Make socket non-blocking if user wants non-blocking.
|
||||
if (impl.flags_ & implementation_type::user_set_non_blocking)
|
||||
{
|
||||
if (!(impl.flags_ & implementation_type::internal_non_blocking))
|
||||
{
|
||||
ioctl_arg_type non_blocking = 1;
|
||||
if (socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking, ec))
|
||||
return 0;
|
||||
impl.flags_ |= implementation_type::internal_non_blocking;
|
||||
}
|
||||
}
|
||||
|
||||
// Receive some data.
|
||||
for (;;)
|
||||
{
|
||||
|
@ -1160,6 +1224,18 @@ public:
|
|||
return ec;
|
||||
}
|
||||
|
||||
// Make socket non-blocking if user wants non-blocking.
|
||||
if (impl.flags_ & implementation_type::user_set_non_blocking)
|
||||
{
|
||||
if (!(impl.flags_ & implementation_type::internal_non_blocking))
|
||||
{
|
||||
ioctl_arg_type non_blocking = 1;
|
||||
if (socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking, ec))
|
||||
return ec;
|
||||
impl.flags_ |= implementation_type::internal_non_blocking;
|
||||
}
|
||||
}
|
||||
|
||||
// Accept a socket.
|
||||
for (;;)
|
||||
{
|
||||
|
@ -1203,6 +1279,14 @@ public:
|
|||
return ec;
|
||||
// Fall through to retry operation.
|
||||
}
|
||||
#if defined(EPROTO)
|
||||
else if (ec.value() == EPROTO)
|
||||
{
|
||||
if (impl.flags_ & implementation_type::enable_connection_aborted)
|
||||
return ec;
|
||||
// Fall through to retry operation.
|
||||
}
|
||||
#endif // defined(EPROTO)
|
||||
else
|
||||
return ec;
|
||||
|
||||
|
@ -1262,6 +1346,10 @@ public:
|
|||
if (ec == asio::error::connection_aborted
|
||||
&& !enable_connection_aborted_)
|
||||
return false;
|
||||
#if defined(EPROTO)
|
||||
if (ec.value() == EPROTO && !enable_connection_aborted_)
|
||||
return false;
|
||||
#endif // defined(EPROTO)
|
||||
|
||||
// Transfer ownership of the new socket to the peer object.
|
||||
if (!ec)
|
||||
|
|
|
@ -63,7 +63,7 @@ inline socket_type accept(socket_type s, socket_addr_type* addr,
|
|||
socket_addr_len_type* addrlen, asio::error_code& ec)
|
||||
{
|
||||
clear_error(ec);
|
||||
#if defined(__MACH__) && defined(__APPLE__)
|
||||
#if defined(__MACH__) && defined(__APPLE__) || defined(__FreeBSD__)
|
||||
socket_type new_s = error_wrapper(::accept(s, addr, addrlen), ec);
|
||||
if (new_s == invalid_socket)
|
||||
return new_s;
|
||||
|
@ -295,7 +295,7 @@ inline socket_type socket(int af, int type, int protocol,
|
|||
}
|
||||
|
||||
return s;
|
||||
#elif defined(__MACH__) && defined(__APPLE__)
|
||||
#elif defined(__MACH__) && defined(__APPLE__) || defined(__FreeBSD__)
|
||||
socket_type s = error_wrapper(::socket(af, type, protocol), ec);
|
||||
if (s == invalid_socket)
|
||||
return s;
|
||||
|
@ -318,11 +318,35 @@ inline socket_type socket(int af, int type, int protocol,
|
|||
inline int setsockopt(socket_type s, int level, int optname,
|
||||
const void* optval, size_t optlen, asio::error_code& ec)
|
||||
{
|
||||
if (level == custom_socket_option_level && optname == always_fail_option)
|
||||
{
|
||||
ec = asio::error::invalid_argument;
|
||||
return -1;
|
||||
}
|
||||
|
||||
#if defined(__BORLANDC__)
|
||||
// Mysteriously, using the getsockopt and setsockopt functions directly with
|
||||
// Borland C++ results in incorrect values being set and read. The bug can be
|
||||
// worked around by using function addresses resolved with GetProcAddress.
|
||||
if (HMODULE winsock_module = ::GetModuleHandleA("ws2_32"))
|
||||
{
|
||||
typedef int (WSAAPI *sso_t)(SOCKET, int, int, const char*, int);
|
||||
if (sso_t sso = (sso_t)::GetProcAddress(winsock_module, "setsockopt"))
|
||||
{
|
||||
clear_error(ec);
|
||||
return error_wrapper(sso(s, level, optname,
|
||||
reinterpret_cast<const char*>(optval),
|
||||
static_cast<int>(optlen)), ec);
|
||||
}
|
||||
}
|
||||
ec = asio::error::fault;
|
||||
return -1;
|
||||
#elif defined(BOOST_WINDOWS) || defined(__CYGWIN__)
|
||||
clear_error(ec);
|
||||
#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
|
||||
return error_wrapper(::setsockopt(s, level, optname,
|
||||
reinterpret_cast<const char*>(optval), static_cast<int>(optlen)), ec);
|
||||
#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
|
||||
clear_error(ec);
|
||||
return error_wrapper(::setsockopt(s, level, optname, optval,
|
||||
static_cast<socklen_t>(optlen)), ec);
|
||||
#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
|
||||
|
@ -331,8 +355,44 @@ inline int setsockopt(socket_type s, int level, int optname,
|
|||
inline int getsockopt(socket_type s, int level, int optname, void* optval,
|
||||
size_t* optlen, asio::error_code& ec)
|
||||
{
|
||||
if (level == custom_socket_option_level && optname == always_fail_option)
|
||||
{
|
||||
ec = asio::error::invalid_argument;
|
||||
return -1;
|
||||
}
|
||||
|
||||
#if defined(__BORLANDC__)
|
||||
// Mysteriously, using the getsockopt and setsockopt functions directly with
|
||||
// Borland C++ results in incorrect values being set and read. The bug can be
|
||||
// worked around by using function addresses resolved with GetProcAddress.
|
||||
if (HMODULE winsock_module = ::GetModuleHandleA("ws2_32"))
|
||||
{
|
||||
typedef int (WSAAPI *gso_t)(SOCKET, int, int, char*, int*);
|
||||
if (gso_t gso = (gso_t)::GetProcAddress(winsock_module, "getsockopt"))
|
||||
{
|
||||
clear_error(ec);
|
||||
int tmp_optlen = static_cast<int>(*optlen);
|
||||
int result = error_wrapper(gso(s, level, optname,
|
||||
reinterpret_cast<char*>(optval), &tmp_optlen), ec);
|
||||
*optlen = static_cast<size_t>(tmp_optlen);
|
||||
if (result != 0 && level == IPPROTO_IPV6 && optname == IPV6_V6ONLY
|
||||
&& ec.value() == WSAENOPROTOOPT && *optlen == sizeof(DWORD))
|
||||
{
|
||||
// Dual-stack IPv4/v6 sockets, and the IPV6_V6ONLY socket option, are
|
||||
// only supported on Windows Vista and later. To simplify program logic
|
||||
// we will fake success of getting this option and specify that the
|
||||
// value is non-zero (i.e. true). This corresponds to the behavior of
|
||||
// IPv6 sockets on Windows platforms pre-Vista.
|
||||
*static_cast<DWORD*>(optval) = 1;
|
||||
clear_error(ec);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
ec = asio::error::fault;
|
||||
return -1;
|
||||
#elif defined(BOOST_WINDOWS) || defined(__CYGWIN__)
|
||||
clear_error(ec);
|
||||
#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
|
||||
int tmp_optlen = static_cast<int>(*optlen);
|
||||
int result = error_wrapper(::getsockopt(s, level, optname,
|
||||
reinterpret_cast<char*>(optval), &tmp_optlen), ec);
|
||||
|
@ -350,6 +410,7 @@ inline int getsockopt(socket_type s, int level, int optname, void* optval,
|
|||
}
|
||||
return result;
|
||||
#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
|
||||
clear_error(ec);
|
||||
socklen_t tmp_optlen = static_cast<socklen_t>(*optlen);
|
||||
int result = error_wrapper(::getsockopt(s, level, optname,
|
||||
optval, &tmp_optlen), ec);
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include "asio/detail/push_options.hpp"
|
||||
|
||||
#include "asio/detail/push_options.hpp"
|
||||
#include <cstdlib>
|
||||
#include <boost/throw_exception.hpp>
|
||||
#include "asio/detail/pop_options.hpp"
|
||||
|
||||
|
@ -49,8 +50,10 @@ public:
|
|||
socket_ops::setsockopt(acceptor.get(),
|
||||
SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt), ec);
|
||||
|
||||
using namespace std; // For memset.
|
||||
sockaddr_in4_type addr;
|
||||
socket_addr_len_type addr_len = sizeof(addr);
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
|
||||
addr.sin_port = 0;
|
||||
|
|
|
@ -169,6 +169,7 @@ const int message_do_not_route = MSG_DONTROUTE;
|
|||
#endif
|
||||
const int custom_socket_option_level = 0xA5100000;
|
||||
const int enable_connection_aborted_option = 1;
|
||||
const int always_fail_option = 2;
|
||||
|
||||
} // namespace detail
|
||||
} // namespace asio
|
||||
|
|
|
@ -309,7 +309,8 @@ private:
|
|||
swap_heap(index, heap_.size() - 1);
|
||||
heap_.pop_back();
|
||||
size_t parent = (index - 1) / 2;
|
||||
if (index > 0 && Time_Traits::less_than(t->time_, heap_[parent]->time_))
|
||||
if (index > 0 && Time_Traits::less_than(
|
||||
heap_[index]->time_, heap_[parent]->time_))
|
||||
up_heap(index);
|
||||
else
|
||||
down_heap(index);
|
||||
|
|
|
@ -137,7 +137,8 @@ public:
|
|||
enum
|
||||
{
|
||||
enable_connection_aborted = 1, // User wants connection_aborted errors.
|
||||
user_set_linger = 2 // The user set the linger option.
|
||||
user_set_linger = 2, // The user set the linger option.
|
||||
user_set_non_blocking = 4 // The user wants a non-blocking socket.
|
||||
};
|
||||
|
||||
// Flags indicating the current state of the socket.
|
||||
|
@ -200,6 +201,7 @@ public:
|
|||
void construct(implementation_type& impl)
|
||||
{
|
||||
impl.socket_ = invalid_socket;
|
||||
impl.flags_ = 0;
|
||||
impl.cancel_token_.reset();
|
||||
impl.safe_cancellation_thread_id_ = 0;
|
||||
|
||||
|
@ -239,6 +241,7 @@ public:
|
|||
asio::error_code ignored_ec;
|
||||
socket_ops::close(impl.socket_, ignored_ec);
|
||||
impl.socket_ = invalid_socket;
|
||||
impl.flags_ = 0;
|
||||
impl.cancel_token_.reset();
|
||||
impl.safe_cancellation_thread_id_ = 0;
|
||||
}
|
||||
|
@ -274,6 +277,7 @@ public:
|
|||
iocp_service_.register_handle(sock_as_handle);
|
||||
|
||||
impl.socket_ = sock.release();
|
||||
impl.flags_ = 0;
|
||||
impl.cancel_token_.reset(static_cast<void*>(0), noop_deleter());
|
||||
impl.protocol_ = protocol;
|
||||
ec = asio::error_code();
|
||||
|
@ -294,6 +298,7 @@ public:
|
|||
iocp_service_.register_handle(native_socket.as_handle());
|
||||
|
||||
impl.socket_ = native_socket;
|
||||
impl.flags_ = 0;
|
||||
impl.cancel_token_.reset(static_cast<void*>(0), noop_deleter());
|
||||
impl.protocol_ = protocol;
|
||||
ec = asio::error_code();
|
||||
|
@ -325,6 +330,7 @@ public:
|
|||
return ec;
|
||||
|
||||
impl.socket_ = invalid_socket;
|
||||
impl.flags_ = 0;
|
||||
impl.cancel_token_.reset();
|
||||
impl.safe_cancellation_thread_id_ = 0;
|
||||
}
|
||||
|
@ -534,6 +540,15 @@ public:
|
|||
|
||||
socket_ops::ioctl(impl.socket_, command.name(),
|
||||
static_cast<ioctl_arg_type*>(command.data()), ec);
|
||||
|
||||
if (!ec && command.name() == static_cast<int>(FIONBIO))
|
||||
{
|
||||
if (command.get())
|
||||
impl.flags_ |= implementation_type::user_set_non_blocking;
|
||||
else
|
||||
impl.flags_ &= ~implementation_type::user_set_non_blocking;
|
||||
}
|
||||
|
||||
return ec;
|
||||
}
|
||||
|
||||
|
@ -1774,11 +1789,12 @@ public:
|
|||
class connect_handler
|
||||
{
|
||||
public:
|
||||
connect_handler(socket_type socket,
|
||||
connect_handler(socket_type socket, bool user_set_non_blocking,
|
||||
boost::shared_ptr<bool> completed,
|
||||
asio::io_service& io_service,
|
||||
reactor_type& reactor, Handler handler)
|
||||
: socket_(socket),
|
||||
user_set_non_blocking_(user_set_non_blocking),
|
||||
completed_(completed),
|
||||
io_service_(io_service),
|
||||
reactor_(reactor),
|
||||
|
@ -1825,13 +1841,16 @@ public:
|
|||
return true;
|
||||
}
|
||||
|
||||
// Make the socket blocking again (the default).
|
||||
// Revert socket to blocking mode unless the user requested otherwise.
|
||||
if (!user_set_non_blocking_)
|
||||
{
|
||||
ioctl_arg_type non_blocking = 0;
|
||||
if (socket_ops::ioctl(socket_, FIONBIO, &non_blocking, ec))
|
||||
{
|
||||
io_service_.post(bind_handler(handler_, ec));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Post the result of the successful connection operation.
|
||||
ec = asio::error_code();
|
||||
|
@ -1841,6 +1860,7 @@ public:
|
|||
|
||||
private:
|
||||
socket_type socket_;
|
||||
bool user_set_non_blocking_;
|
||||
boost::shared_ptr<bool> completed_;
|
||||
asio::io_service& io_service_;
|
||||
reactor_type& reactor_;
|
||||
|
@ -1891,6 +1911,13 @@ public:
|
|||
if (socket_ops::connect(impl.socket_, peer_endpoint.data(),
|
||||
peer_endpoint.size(), ec) == 0)
|
||||
{
|
||||
// Revert socket to blocking mode unless the user requested otherwise.
|
||||
if (!(impl.flags_ & implementation_type::user_set_non_blocking))
|
||||
{
|
||||
non_blocking = 0;
|
||||
socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking, ec);
|
||||
}
|
||||
|
||||
// The connect operation has finished successfully so we need to post the
|
||||
// handler immediately.
|
||||
this->io_service().post(bind_handler(handler, ec));
|
||||
|
@ -1903,10 +1930,20 @@ public:
|
|||
boost::shared_ptr<bool> completed(new bool(false));
|
||||
reactor->start_write_and_except_ops(impl.socket_,
|
||||
connect_handler<Handler>(
|
||||
impl.socket_, completed, this->io_service(), *reactor, handler));
|
||||
impl.socket_,
|
||||
(impl.flags_ & implementation_type::user_set_non_blocking) != 0,
|
||||
completed, this->io_service(), *reactor, handler));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Revert socket to blocking mode unless the user requested otherwise.
|
||||
if (!(impl.flags_ & implementation_type::user_set_non_blocking))
|
||||
{
|
||||
non_blocking = 0;
|
||||
asio::error_code ignored_ec;
|
||||
socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking, ignored_ec);
|
||||
}
|
||||
|
||||
// The connect operation has failed, so post the handler immediately.
|
||||
this->io_service().post(bind_handler(handler, ec));
|
||||
}
|
||||
|
|
|
@ -60,7 +60,40 @@ namespace asio {
|
|||
* @par Concepts:
|
||||
* Dispatcher.
|
||||
*
|
||||
* @sa @ref io_service_handler_exception
|
||||
* @par Effect of exceptions thrown from handlers
|
||||
*
|
||||
* If an exception is thrown from a handler, the exception is allowed to
|
||||
* propagate through the throwing thread's invocation of
|
||||
* asio::io_service::run(), asio::io_service::run_one(),
|
||||
* asio::io_service::poll() or asio::io_service::poll_one().
|
||||
* No other threads that are calling any of these functions are affected. It is
|
||||
* then the responsibility of the application to catch the exception.
|
||||
*
|
||||
* After the exception has been caught, the
|
||||
* asio::io_service::run(), asio::io_service::run_one(),
|
||||
* asio::io_service::poll() or asio::io_service::poll_one()
|
||||
* call may be restarted @em without the need for an intervening call to
|
||||
* asio::io_service::reset(). This allows the thread to rejoin the
|
||||
* io_service's thread pool without impacting any other threads in the pool.
|
||||
*
|
||||
* For example:
|
||||
*
|
||||
* @code
|
||||
* asio::io_service io_service;
|
||||
* ...
|
||||
* for (;;)
|
||||
* {
|
||||
* try
|
||||
* {
|
||||
* io_service.run();
|
||||
* break; // run() exited normally
|
||||
* }
|
||||
* catch (my_exception& e)
|
||||
* {
|
||||
* // Deal with exception as appropriate.
|
||||
* }
|
||||
* }
|
||||
* @endcode
|
||||
*/
|
||||
class io_service
|
||||
: private noncopyable
|
||||
|
@ -108,7 +141,9 @@ public:
|
|||
* more handlers to be dispatched, or until the io_service has been stopped.
|
||||
*
|
||||
* Multiple threads may call the run() function to set up a pool of threads
|
||||
* from which the io_service may execute handlers.
|
||||
* from which the io_service may execute handlers. All threads that are
|
||||
* waiting in the pool are equivalent and the io_service may choose any one
|
||||
* of them to invoke a handler.
|
||||
*
|
||||
* The run() function may be safely called again once it has completed only
|
||||
* after a call to reset().
|
||||
|
@ -125,7 +160,9 @@ public:
|
|||
* more handlers to be dispatched, or until the io_service has been stopped.
|
||||
*
|
||||
* Multiple threads may call the run() function to set up a pool of threads
|
||||
* from which the io_service may execute handlers.
|
||||
* from which the io_service may execute handlers. All threads that are
|
||||
* waiting in the pool are equivalent and the io_service may choose any one
|
||||
* of them to invoke a handler.
|
||||
*
|
||||
* The run() function may be safely called again once it has completed only
|
||||
* after a call to reset().
|
||||
|
@ -456,42 +493,6 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @page io_service_handler_exception Effect of exceptions thrown from handlers
|
||||
*
|
||||
* If an exception is thrown from a handler, the exception is allowed to
|
||||
* propagate through the throwing thread's invocation of
|
||||
* asio::io_service::run(), asio::io_service::run_one(),
|
||||
* asio::io_service::poll() or asio::io_service::poll_one().
|
||||
* No other threads that are calling any of these functions are affected. It is
|
||||
* then the responsibility of the application to catch the exception.
|
||||
*
|
||||
* After the exception has been caught, the
|
||||
* asio::io_service::run(), asio::io_service::run_one(),
|
||||
* asio::io_service::poll() or asio::io_service::poll_one()
|
||||
* call may be restarted @em without the need for an intervening call to
|
||||
* asio::io_service::reset(). This allows the thread to rejoin the
|
||||
* io_service's thread pool without impacting any other threads in the pool.
|
||||
*
|
||||
* @par Example
|
||||
* @code
|
||||
* asio::io_service io_service;
|
||||
* ...
|
||||
* for (;;)
|
||||
* {
|
||||
* try
|
||||
* {
|
||||
* io_service.run();
|
||||
* break; // run() exited normally
|
||||
* }
|
||||
* catch (my_exception& e)
|
||||
* {
|
||||
* // Deal with exception as appropriate.
|
||||
* }
|
||||
* }
|
||||
* @endcode
|
||||
*/
|
||||
|
||||
} // namespace asio
|
||||
|
||||
#include "asio/impl/io_service.ipp"
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
#include "asio/detail/push_options.hpp"
|
||||
#include <boost/iterator/iterator_facade.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
|
@ -118,12 +119,12 @@ private:
|
|||
|
||||
void increment()
|
||||
{
|
||||
if (++iter_ == values_->end())
|
||||
if (++*iter_ == values_->end())
|
||||
{
|
||||
// Reset state to match a default constructed end iterator.
|
||||
values_.reset();
|
||||
typedef typename values_type::const_iterator values_iterator_type;
|
||||
iter_ = values_iterator_type();
|
||||
iter_.reset();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -133,17 +134,18 @@ private:
|
|||
return true;
|
||||
if (values_ != other.values_)
|
||||
return false;
|
||||
return iter_ == other.iter_;
|
||||
return *iter_ == *other.iter_;
|
||||
}
|
||||
|
||||
const basic_resolver_entry<InternetProtocol>& dereference() const
|
||||
{
|
||||
return *iter_;
|
||||
return **iter_;
|
||||
}
|
||||
|
||||
typedef std::vector<basic_resolver_entry<InternetProtocol> > values_type;
|
||||
typedef typename values_type::const_iterator values_iter_type;
|
||||
boost::shared_ptr<values_type> values_;
|
||||
typename values_type::const_iterator iter_;
|
||||
boost::optional<values_iter_type> iter_;
|
||||
};
|
||||
|
||||
} // namespace ip
|
||||
|
|
|
@ -37,6 +37,12 @@ template <int IPv4_Level, int IPv4_Name, int IPv6_Level, int IPv6_Name>
|
|||
class boolean
|
||||
{
|
||||
public:
|
||||
#if defined(__sun)
|
||||
typedef unsigned char value_type;
|
||||
#else
|
||||
typedef int value_type;
|
||||
#endif
|
||||
|
||||
// Default constructor.
|
||||
boolean()
|
||||
: value_(0)
|
||||
|
@ -94,14 +100,14 @@ public:
|
|||
|
||||
// Get the address of the boolean data.
|
||||
template <typename Protocol>
|
||||
int* data(const Protocol&)
|
||||
value_type* data(const Protocol&)
|
||||
{
|
||||
return &value_;
|
||||
}
|
||||
|
||||
// Get the address of the boolean data.
|
||||
template <typename Protocol>
|
||||
const int* data(const Protocol&) const
|
||||
const value_type* data(const Protocol&) const
|
||||
{
|
||||
return &value_;
|
||||
}
|
||||
|
@ -122,7 +128,7 @@ public:
|
|||
}
|
||||
|
||||
private:
|
||||
int value_;
|
||||
value_type value_;
|
||||
};
|
||||
|
||||
// Helper template for implementing unicast hops options.
|
||||
|
|
|
@ -59,7 +59,7 @@ public:
|
|||
return tcp(PF_INET);
|
||||
}
|
||||
|
||||
/// Construct to represent the IPv4 TCP protocol.
|
||||
/// Construct to represent the IPv6 TCP protocol.
|
||||
static tcp v6()
|
||||
{
|
||||
return tcp(PF_INET6);
|
||||
|
|
|
@ -56,7 +56,7 @@ public:
|
|||
return udp(PF_INET);
|
||||
}
|
||||
|
||||
/// Construct to represent the IPv4 UDP protocol.
|
||||
/// Construct to represent the IPv6 UDP protocol.
|
||||
static udp v6()
|
||||
{
|
||||
return udp(PF_INET6);
|
||||
|
@ -80,7 +80,7 @@ public:
|
|||
return family_;
|
||||
}
|
||||
|
||||
/// The IPv4 UDP socket type.
|
||||
/// The UDP socket type.
|
||||
typedef basic_datagram_socket<udp> socket;
|
||||
|
||||
/// The UDP resolver type.
|
||||
|
|
|
@ -54,6 +54,12 @@ public:
|
|||
}
|
||||
|
||||
/// Destructor.
|
||||
/**
|
||||
* Destroys a strand.
|
||||
*
|
||||
* Handlers posted through the strand that have not yet been invoked will
|
||||
* still be dispatched in a way that meets the guarantee of non-concurrency.
|
||||
*/
|
||||
~strand()
|
||||
{
|
||||
service_.destroy(impl_);
|
||||
|
|
|
@ -82,10 +82,13 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
#include "libtorrent/lsd.hpp"
|
||||
#include "libtorrent/socket_type.hpp"
|
||||
#include "libtorrent/connection_queue.hpp"
|
||||
#include "libtorrent/disk_io_thread.hpp"
|
||||
|
||||
namespace libtorrent
|
||||
{
|
||||
|
||||
namespace fs = boost::filesystem;
|
||||
|
||||
namespace aux
|
||||
{
|
||||
struct session_impl;
|
||||
|
@ -98,7 +101,7 @@ namespace libtorrent
|
|||
: processing(false), progress(0.f), abort(false) {}
|
||||
|
||||
boost::shared_ptr<torrent> torrent_ptr;
|
||||
boost::filesystem::path save_path;
|
||||
fs::path save_path;
|
||||
|
||||
sha1_hash info_hash;
|
||||
|
||||
|
@ -229,6 +232,7 @@ namespace libtorrent
|
|||
bool is_aborted() const { return m_abort; }
|
||||
|
||||
void set_ip_filter(ip_filter const& f);
|
||||
void set_port_filter(port_filter const& f);
|
||||
|
||||
bool listen_on(
|
||||
std::pair<int, int> const& port_range
|
||||
|
@ -237,7 +241,7 @@ namespace libtorrent
|
|||
|
||||
torrent_handle add_torrent(
|
||||
torrent_info const& ti
|
||||
, boost::filesystem::path const& save_path
|
||||
, fs::path const& save_path
|
||||
, entry const& resume_data
|
||||
, bool compact_mode
|
||||
, int block_size
|
||||
|
@ -247,7 +251,7 @@ namespace libtorrent
|
|||
char const* tracker_url
|
||||
, sha1_hash const& info_hash
|
||||
, char const* name
|
||||
, boost::filesystem::path const& save_path
|
||||
, fs::path const& save_path
|
||||
, entry const& resume_data
|
||||
, bool compact_mode
|
||||
, int block_size
|
||||
|
@ -304,6 +308,14 @@ namespace libtorrent
|
|||
{ return m_dht_proxy; }
|
||||
#endif
|
||||
|
||||
void start_lsd();
|
||||
void start_natpmp();
|
||||
void start_upnp();
|
||||
|
||||
void stop_lsd();
|
||||
void stop_natpmp();
|
||||
void stop_upnp();
|
||||
|
||||
// handles delayed alerts
|
||||
alert_manager m_alerts;
|
||||
|
||||
|
@ -325,6 +337,9 @@ namespace libtorrent
|
|||
// when they are destructed.
|
||||
file_pool m_files;
|
||||
|
||||
// handles disk io requests asynchronously
|
||||
disk_io_thread m_disk_thread;
|
||||
|
||||
// this is a list of half-open tcp connections
|
||||
// (only outgoing connections)
|
||||
// this has to be one of the last
|
||||
|
@ -349,6 +364,9 @@ namespace libtorrent
|
|||
// filters incoming connections
|
||||
ip_filter m_ip_filter;
|
||||
|
||||
// filters outgoing connections
|
||||
port_filter m_port_filter;
|
||||
|
||||
// the peer id that is generated at the start of the session
|
||||
peer_id m_peer_id;
|
||||
|
||||
|
@ -427,9 +445,9 @@ namespace libtorrent
|
|||
pe_settings m_pe_settings;
|
||||
#endif
|
||||
|
||||
natpmp m_natpmp;
|
||||
upnp m_upnp;
|
||||
lsd m_lsd;
|
||||
boost::shared_ptr<natpmp> m_natpmp;
|
||||
boost::shared_ptr<upnp> m_upnp;
|
||||
boost::shared_ptr<lsd> m_lsd;
|
||||
|
||||
// the timer used to fire the second_tick
|
||||
deadline_timer m_timer;
|
||||
|
|
|
@ -192,7 +192,7 @@ namespace libtorrent
|
|||
void write_cancel(peer_request const& r);
|
||||
void write_bitfield(std::vector<bool> const& bitfield);
|
||||
void write_have(int index);
|
||||
void write_piece(peer_request const& r);
|
||||
void write_piece(peer_request const& r, char const* buffer);
|
||||
void write_handshake();
|
||||
#ifndef TORRENT_DISABLE_EXTENSIONS
|
||||
void write_extensions();
|
||||
|
|
|
@ -54,14 +54,15 @@ namespace libtorrent
|
|||
{
|
||||
// DEBUG API
|
||||
|
||||
namespace fs = boost::filesystem;
|
||||
|
||||
struct logger
|
||||
{
|
||||
logger(boost::filesystem::path const& filename, int instance, bool append = true)
|
||||
logger(fs::path const& filename, int instance, bool append = true)
|
||||
{
|
||||
using namespace boost::filesystem;
|
||||
path dir(complete("libtorrent_logs" + boost::lexical_cast<std::string>(instance)));
|
||||
if (!exists(dir)) create_directories(dir);
|
||||
m_file.open(dir / filename, std::ios_base::out | (append ? std::ios_base::app : std::ios_base::out));
|
||||
fs::path dir(fs::complete("libtorrent_logs" + boost::lexical_cast<std::string>(instance)));
|
||||
if (!fs::exists(dir)) fs::create_directories(dir);
|
||||
m_file.open((dir / filename).string().c_str(), std::ios_base::out | (append ? std::ios_base::app : std::ios_base::out));
|
||||
*this << "\n\n\n*** starting log ***\n";
|
||||
}
|
||||
|
||||
|
@ -73,7 +74,7 @@ namespace libtorrent
|
|||
return *this;
|
||||
}
|
||||
|
||||
boost::filesystem::ofstream m_file;
|
||||
std::ofstream m_file;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -155,6 +155,8 @@ namespace libtorrent
|
|||
dictionary_type& dict();
|
||||
const dictionary_type& dict() const;
|
||||
|
||||
void swap(entry& e);
|
||||
|
||||
// these functions requires that the entry
|
||||
// is a dictionary, otherwise they will throw
|
||||
entry& operator[](char const* key);
|
||||
|
|
|
@ -52,6 +52,7 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
|
||||
namespace libtorrent
|
||||
{
|
||||
namespace fs = boost::filesystem;
|
||||
|
||||
struct TORRENT_EXPORT file_error: std::runtime_error
|
||||
{
|
||||
|
@ -105,10 +106,10 @@ namespace libtorrent
|
|||
static const open_mode out;
|
||||
|
||||
file();
|
||||
file(boost::filesystem::path const& p, open_mode m);
|
||||
file(fs::path const& p, open_mode m);
|
||||
~file();
|
||||
|
||||
void open(boost::filesystem::path const& p, open_mode m);
|
||||
void open(fs::path const& p, open_mode m);
|
||||
void close();
|
||||
void set_size(size_type size);
|
||||
|
||||
|
|
|
@ -104,3 +104,4 @@ namespace libtorrent
|
|||
}
|
||||
|
||||
#endif // TORRENT_HASHER_HPP_INCLUDED
|
||||
|
||||
|
|
|
@ -1,36 +1,53 @@
|
|||
#include "libtorrent/io.hpp"
|
||||
#include "libtorrent/socket.hpp"
|
||||
#include <boost/bind.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <boost/function.hpp>
|
||||
#include <asio/read.hpp>
|
||||
#include <asio/write.hpp>
|
||||
/*
|
||||
|
||||
Copyright (c) 2007, Arvid Norberg
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the author nor the names of its
|
||||
contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef TORRENT_HTTP_STREAM_HPP_INCLUDED
|
||||
#define TORRENT_HTTP_STREAM_HPP_INCLUDED
|
||||
|
||||
#include "libtorrent/proxy_base.hpp"
|
||||
|
||||
namespace libtorrent {
|
||||
|
||||
class http_stream : boost::noncopyable
|
||||
class http_stream : public proxy_base
|
||||
{
|
||||
public:
|
||||
|
||||
typedef stream_socket::lowest_layer_type lowest_layer_type;
|
||||
typedef stream_socket::endpoint_type endpoint_type;
|
||||
typedef stream_socket::protocol_type protocol_type;
|
||||
|
||||
explicit http_stream(asio::io_service& io_service)
|
||||
: m_sock(io_service)
|
||||
, m_resolver(io_service)
|
||||
: proxy_base(io_service)
|
||||
, m_no_connect(false)
|
||||
{}
|
||||
|
||||
void set_no_connect(bool c) { m_no_connect = c; }
|
||||
|
||||
void set_proxy(std::string hostname, int port)
|
||||
{
|
||||
m_hostname = hostname;
|
||||
m_port = port;
|
||||
}
|
||||
|
||||
void set_username(std::string const& user
|
||||
, std::string const& password)
|
||||
{
|
||||
|
@ -38,108 +55,6 @@ public:
|
|||
m_password = password;
|
||||
}
|
||||
|
||||
template <class Mutable_Buffers, class Handler>
|
||||
void async_read_some(Mutable_Buffers const& buffers, Handler const& handler)
|
||||
{
|
||||
m_sock.async_read_some(buffers, handler);
|
||||
}
|
||||
|
||||
template <class Mutable_Buffers>
|
||||
std::size_t read_some(Mutable_Buffers const& buffers, asio::error_code& ec)
|
||||
{
|
||||
return m_sock.read_some(buffers, ec);
|
||||
}
|
||||
|
||||
template <class Mutable_Buffers>
|
||||
std::size_t read_some(Mutable_Buffers const& buffers)
|
||||
{
|
||||
return m_sock.read_some(buffers);
|
||||
}
|
||||
|
||||
template <class IO_Control_Command>
|
||||
void io_control(IO_Control_Command& ioc)
|
||||
{
|
||||
m_sock.io_control(ioc);
|
||||
}
|
||||
|
||||
template <class IO_Control_Command>
|
||||
void io_control(IO_Control_Command& ioc, asio::error_code& ec)
|
||||
{
|
||||
m_sock.io_control(ioc, ec);
|
||||
}
|
||||
|
||||
template <class Const_Buffers, class Handler>
|
||||
void async_write_some(Const_Buffers const& buffers, Handler const& handler)
|
||||
{
|
||||
m_sock.async_write_some(buffers, handler);
|
||||
}
|
||||
|
||||
void bind(endpoint_type const& endpoint)
|
||||
{
|
||||
m_sock.bind(endpoint);
|
||||
}
|
||||
|
||||
template <class Error_Handler>
|
||||
void bind(endpoint_type const& endpoint, Error_Handler const& error_handler)
|
||||
{
|
||||
m_sock.bind(endpoint, error_handler);
|
||||
}
|
||||
|
||||
void open(protocol_type const& p)
|
||||
{
|
||||
m_sock.open(p);
|
||||
}
|
||||
|
||||
template <class Error_Handler>
|
||||
void open(protocol_type const& p, Error_Handler const& error_handler)
|
||||
{
|
||||
m_sock.open(p, error_handler);
|
||||
}
|
||||
|
||||
void close()
|
||||
{
|
||||
m_remote_endpoint = endpoint_type();
|
||||
m_sock.close();
|
||||
}
|
||||
|
||||
template <class Error_Handler>
|
||||
void close(Error_Handler const& error_handler)
|
||||
{
|
||||
m_sock.close(error_handler);
|
||||
}
|
||||
|
||||
endpoint_type remote_endpoint()
|
||||
{
|
||||
return m_remote_endpoint;
|
||||
}
|
||||
|
||||
template <class Error_Handler>
|
||||
endpoint_type remote_endpoint(Error_Handler const& error_handler)
|
||||
{
|
||||
return m_remote_endpoint;
|
||||
}
|
||||
|
||||
endpoint_type local_endpoint()
|
||||
{
|
||||
return m_sock.local_endpoint();
|
||||
}
|
||||
|
||||
template <class Error_Handler>
|
||||
endpoint_type local_endpoint(Error_Handler const& error_handler)
|
||||
{
|
||||
return m_sock.local_endpoint(error_handler);
|
||||
}
|
||||
|
||||
asio::io_service& io_service()
|
||||
{
|
||||
return m_sock.io_service();
|
||||
}
|
||||
|
||||
lowest_layer_type& lowest_layer()
|
||||
{
|
||||
return m_sock.lowest_layer();
|
||||
}
|
||||
|
||||
typedef boost::function<void(asio::error_code const&)> handler_type;
|
||||
|
||||
template <class Handler>
|
||||
|
@ -171,19 +86,12 @@ private:
|
|||
void handshake1(asio::error_code const& e, boost::shared_ptr<handler_type> h);
|
||||
void handshake2(asio::error_code const& e, boost::shared_ptr<handler_type> h);
|
||||
|
||||
stream_socket m_sock;
|
||||
// the http proxy
|
||||
std::string m_hostname;
|
||||
int m_port;
|
||||
// send and receive buffer
|
||||
std::vector<char> m_buffer;
|
||||
// proxy authentication
|
||||
std::string m_user;
|
||||
std::string m_password;
|
||||
|
||||
endpoint_type m_remote_endpoint;
|
||||
|
||||
tcp::resolver m_resolver;
|
||||
// this is true if the connection is HTTP based and
|
||||
// want to talk directly to the proxy
|
||||
bool m_no_connect;
|
||||
|
@ -191,3 +99,4 @@ private:
|
|||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -71,20 +71,82 @@ struct ip_range
|
|||
namespace detail
|
||||
{
|
||||
|
||||
template<class Addr>
|
||||
Addr zero()
|
||||
{
|
||||
typename Addr::bytes_type zero;
|
||||
std::fill(zero.begin(), zero.end(), 0);
|
||||
return Addr(zero);
|
||||
}
|
||||
|
||||
template<>
|
||||
inline boost::uint16_t zero<boost::uint16_t>() { return 0; }
|
||||
|
||||
template<class Addr>
|
||||
Addr plus_one(Addr const& a)
|
||||
{
|
||||
typename Addr::bytes_type tmp(a.to_bytes());
|
||||
typedef typename Addr::bytes_type::reverse_iterator iter;
|
||||
for (iter i = tmp.rbegin()
|
||||
, end(tmp.rend()); i != end; ++i)
|
||||
{
|
||||
if (*i < (std::numeric_limits<typename iter::value_type>::max)())
|
||||
{
|
||||
*i += 1;
|
||||
break;
|
||||
}
|
||||
*i = 0;
|
||||
}
|
||||
return Addr(tmp);
|
||||
}
|
||||
|
||||
inline boost::uint16_t plus_one(boost::uint16_t val) { return val + 1; }
|
||||
|
||||
template<class Addr>
|
||||
Addr minus_one(Addr const& a)
|
||||
{
|
||||
typename Addr::bytes_type tmp(a.to_bytes());
|
||||
typedef typename Addr::bytes_type::reverse_iterator iter;
|
||||
for (iter i = tmp.rbegin()
|
||||
, end(tmp.rend()); i != end; ++i)
|
||||
{
|
||||
if (*i > 0)
|
||||
{
|
||||
*i -= 1;
|
||||
break;
|
||||
}
|
||||
*i = (std::numeric_limits<typename iter::value_type>::max)();
|
||||
}
|
||||
return Addr(tmp);
|
||||
}
|
||||
|
||||
inline boost::uint16_t minus_one(boost::uint16_t val) { return val - 1; }
|
||||
|
||||
template<class Addr>
|
||||
Addr max_addr()
|
||||
{
|
||||
typename Addr::bytes_type tmp;
|
||||
std::fill(tmp.begin(), tmp.end()
|
||||
, (std::numeric_limits<typename Addr::bytes_type::value_type>::max)());
|
||||
return Addr(tmp);
|
||||
}
|
||||
|
||||
template<>
|
||||
inline boost::uint16_t max_addr<boost::uint16_t>()
|
||||
{ return (std::numeric_limits<boost::uint16_t>::max)(); }
|
||||
|
||||
// this is the generic implementation of
|
||||
// a filter for a specific address type.
|
||||
// it works with IPv4 and IPv6
|
||||
template<class Addr>
|
||||
class TORRENT_EXPORT filter_impl
|
||||
class filter_impl
|
||||
{
|
||||
public:
|
||||
|
||||
filter_impl()
|
||||
{
|
||||
typename Addr::bytes_type zero;
|
||||
std::fill(zero.begin(), zero.end(), 0);
|
||||
// make the entire ip-range non-blocked
|
||||
m_access_list.insert(range(Addr(zero), 0));
|
||||
m_access_list.insert(range(zero<Addr>(), 0));
|
||||
}
|
||||
|
||||
void add_rule(Addr first, Addr last, int flags)
|
||||
|
@ -134,7 +196,7 @@ namespace detail
|
|||
if ((j != m_access_list.end()
|
||||
&& minus_one(j->start) != last)
|
||||
|| (j == m_access_list.end()
|
||||
&& last != max_addr()))
|
||||
&& last != max_addr<Addr>()))
|
||||
{
|
||||
assert(j == m_access_list.end() || last < minus_one(j->start));
|
||||
if (last_access != flags)
|
||||
|
@ -170,7 +232,7 @@ namespace detail
|
|||
|
||||
++i;
|
||||
if (i == end)
|
||||
r.last = max_addr();
|
||||
r.last = max_addr<Addr>();
|
||||
else
|
||||
r.last = minus_one(i->start);
|
||||
|
||||
|
@ -181,48 +243,6 @@ namespace detail
|
|||
|
||||
private:
|
||||
|
||||
Addr plus_one(Addr const& a) const
|
||||
{
|
||||
typename Addr::bytes_type tmp(a.to_bytes());
|
||||
typedef typename Addr::bytes_type::reverse_iterator iter;
|
||||
for (iter i = tmp.rbegin()
|
||||
, end(tmp.rend()); i != end; ++i)
|
||||
{
|
||||
if (*i < (std::numeric_limits<typename iter::value_type>::max)())
|
||||
{
|
||||
*i += 1;
|
||||
break;
|
||||
}
|
||||
*i = 0;
|
||||
}
|
||||
return Addr(tmp);
|
||||
}
|
||||
|
||||
Addr minus_one(Addr const& a) const
|
||||
{
|
||||
typename Addr::bytes_type tmp(a.to_bytes());
|
||||
typedef typename Addr::bytes_type::reverse_iterator iter;
|
||||
for (iter i = tmp.rbegin()
|
||||
, end(tmp.rend()); i != end; ++i)
|
||||
{
|
||||
if (*i > 0)
|
||||
{
|
||||
*i -= 1;
|
||||
break;
|
||||
}
|
||||
*i = (std::numeric_limits<typename iter::value_type>::max)();
|
||||
}
|
||||
return Addr(tmp);
|
||||
}
|
||||
|
||||
Addr max_addr() const
|
||||
{
|
||||
typename Addr::bytes_type tmp;
|
||||
std::fill(tmp.begin(), tmp.end()
|
||||
, (std::numeric_limits<typename Addr::bytes_type::value_type>::max)());
|
||||
return Addr(tmp);
|
||||
}
|
||||
|
||||
struct range
|
||||
{
|
||||
range(Addr addr, int access = 0): start(addr), access(access) {}
|
||||
|
@ -270,6 +290,24 @@ private:
|
|||
detail::filter_impl<address_v6> m_filter6;
|
||||
};
|
||||
|
||||
class TORRENT_EXPORT port_filter
|
||||
{
|
||||
public:
|
||||
|
||||
enum access_flags
|
||||
{
|
||||
blocked = 1
|
||||
};
|
||||
|
||||
void add_rule(boost::uint16_t first, boost::uint16_t last, int flags);
|
||||
int access(boost::uint16_t port) const;
|
||||
|
||||
private:
|
||||
|
||||
detail::filter_impl<boost::uint16_t> m_filter;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -51,7 +51,6 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
#include <boost/array.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
#include <boost/cstdint.hpp>
|
||||
#include <boost/detail/atomic_count.hpp>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
|
@ -73,6 +72,7 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
#include "libtorrent/bandwidth_manager.hpp"
|
||||
#include "libtorrent/policy.hpp"
|
||||
#include "libtorrent/socket_type.hpp"
|
||||
#include "libtorrent/intrusive_ptr_base.hpp"
|
||||
|
||||
// TODO: each time a block is 'taken over'
|
||||
// from another peer. That peer must be given
|
||||
|
@ -88,20 +88,16 @@ namespace libtorrent
|
|||
struct session_impl;
|
||||
}
|
||||
|
||||
TORRENT_EXPORT void intrusive_ptr_add_ref(peer_connection const*);
|
||||
TORRENT_EXPORT void intrusive_ptr_release(peer_connection const*);
|
||||
|
||||
struct TORRENT_EXPORT protocol_error: std::runtime_error
|
||||
{
|
||||
protocol_error(const std::string& msg): std::runtime_error(msg) {};
|
||||
};
|
||||
|
||||
class TORRENT_EXPORT peer_connection
|
||||
: public boost::noncopyable
|
||||
: public intrusive_ptr_base<peer_connection>
|
||||
, public boost::noncopyable
|
||||
{
|
||||
friend class invariant_access;
|
||||
friend TORRENT_EXPORT void intrusive_ptr_add_ref(peer_connection const*);
|
||||
friend TORRENT_EXPORT void intrusive_ptr_release(peer_connection const*);
|
||||
public:
|
||||
|
||||
enum channels
|
||||
|
@ -378,7 +374,7 @@ namespace libtorrent
|
|||
virtual void write_cancel(peer_request const& r) = 0;
|
||||
virtual void write_have(int index) = 0;
|
||||
virtual void write_keepalive() = 0;
|
||||
virtual void write_piece(peer_request const& r) = 0;
|
||||
virtual void write_piece(peer_request const& r, char const* buffer) = 0;
|
||||
|
||||
virtual void on_connected() = 0;
|
||||
virtual void on_tick() {}
|
||||
|
@ -474,6 +470,9 @@ namespace libtorrent
|
|||
private:
|
||||
|
||||
void fill_send_buffer();
|
||||
void on_disk_read_complete(int ret, disk_io_job const& j, peer_request r);
|
||||
void on_disk_write_complete(int ret, disk_io_job const& j
|
||||
, peer_request r, boost::shared_ptr<torrent> t);
|
||||
|
||||
// the timeout in seconds
|
||||
int m_timeout;
|
||||
|
@ -504,6 +503,11 @@ namespace libtorrent
|
|||
// buffer we're currently waiting for.
|
||||
int m_current_send_buffer;
|
||||
|
||||
// the number of bytes we are currently reading
|
||||
// from disk, that will be added to the send
|
||||
// buffer as soon as they complete
|
||||
int m_reading_bytes;
|
||||
|
||||
// if the sending buffer doesn't finish in one send
|
||||
// operation, this is the position within that buffer
|
||||
// where the next operation should continue
|
||||
|
@ -660,9 +664,6 @@ namespace libtorrent
|
|||
// the left-over bandwidth (suitable for web seeds).
|
||||
bool m_non_prioritized;
|
||||
|
||||
// reference counter for intrusive_ptr
|
||||
mutable boost::detail::atomic_count m_refs;
|
||||
|
||||
int m_upload_limit;
|
||||
int m_download_limit;
|
||||
|
||||
|
@ -681,6 +682,19 @@ namespace libtorrent
|
|||
// so that it can be removed from the queue
|
||||
// once the connection completes
|
||||
int m_connection_ticket;
|
||||
|
||||
// bytes downloaded since last second
|
||||
// timer timeout; used for determining
|
||||
// approx download rate
|
||||
int m_remote_bytes_dled;
|
||||
|
||||
// approximate peer download rate
|
||||
int m_remote_dl_rate;
|
||||
|
||||
// a timestamp when the remote download rate
|
||||
// was last updated
|
||||
ptime m_remote_dl_update;
|
||||
|
||||
#ifndef NDEBUG
|
||||
public:
|
||||
bool m_in_constructor;
|
||||
|
|
|
@ -58,6 +58,7 @@ namespace libtorrent
|
|||
|
||||
big_number(std::string const& s)
|
||||
{
|
||||
assert(s.size() >= 20);
|
||||
int sl = int(s.size()) < size ? int(s.size()) : size;
|
||||
std::memcpy(m_number, &s[0], sl);
|
||||
}
|
||||
|
|
|
@ -58,8 +58,8 @@ namespace libtorrent
|
|||
on_parole = 0x200,
|
||||
seed = 0x400
|
||||
#ifndef TORRENT_DISABLE_ENCRYPTION
|
||||
, rc4_encrypted = 0x200,
|
||||
plaintext_encrypted = 0x400
|
||||
, rc4_encrypted = 0x800,
|
||||
plaintext_encrypted = 0x1000
|
||||
#endif
|
||||
};
|
||||
|
||||
|
@ -141,6 +141,9 @@ namespace libtorrent
|
|||
web_seed = 1
|
||||
};
|
||||
int connection_type;
|
||||
|
||||
// approximate peer download rate
|
||||
int remote_dl_rate;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -90,14 +90,14 @@ namespace libtorrent
|
|||
|
||||
struct block_info
|
||||
{
|
||||
block_info(): num_downloads(0), requested(0), finished(0) {}
|
||||
block_info(): num_downloads(0), state(state_none) {}
|
||||
// the peer this block was requested or
|
||||
// downloaded from
|
||||
tcp::endpoint peer;
|
||||
// the number of times this block has been downloaded
|
||||
unsigned num_downloads:14;
|
||||
unsigned requested:1;
|
||||
unsigned finished:1;
|
||||
enum { state_none, state_requested, state_writing, state_finished };
|
||||
unsigned state:2;
|
||||
};
|
||||
|
||||
// the peers that are downloading this piece
|
||||
|
@ -109,7 +109,7 @@ namespace libtorrent
|
|||
|
||||
struct downloading_piece
|
||||
{
|
||||
downloading_piece(): finished(0), requested(0) {}
|
||||
downloading_piece(): finished(0), writing(0), requested(0) {}
|
||||
piece_state_t state;
|
||||
|
||||
// the index of the piece
|
||||
|
@ -118,20 +118,27 @@ namespace libtorrent
|
|||
// this is a pointer into the m_block_info
|
||||
// vector owned by the piece_picker
|
||||
block_info* info;
|
||||
boost::uint16_t finished;
|
||||
boost::uint16_t requested;
|
||||
// the number of blocks in the finished state
|
||||
boost::int16_t finished;
|
||||
// the number of blocks in the writing state
|
||||
boost::int16_t writing;
|
||||
// the number of blocks in the requested state
|
||||
boost::int16_t requested;
|
||||
};
|
||||
|
||||
piece_picker(int blocks_per_piece
|
||||
, int total_num_blocks);
|
||||
|
||||
void get_availability(std::vector<int>& avail) const;
|
||||
|
||||
void set_sequenced_download_threshold(int sequenced_download_threshold);
|
||||
|
||||
// the vector tells which pieces we already have
|
||||
// and which we don't have.
|
||||
void files_checked(
|
||||
const std::vector<bool>& pieces
|
||||
, const std::vector<downloading_piece>& unfinished);
|
||||
std::vector<bool> const& pieces
|
||||
, std::vector<downloading_piece> const& unfinished
|
||||
, std::vector<int>& verify_pieces);
|
||||
|
||||
// increases the peer count for the given piece
|
||||
// (is used when a HAVE or BITFIELD message is received)
|
||||
|
@ -188,12 +195,16 @@ namespace libtorrent
|
|||
// returns true if any client is currently downloading this
|
||||
// piece-block, or if it's queued for downloading by some client
|
||||
// or if it already has been successfully downloaded
|
||||
bool is_downloading(piece_block block) const;
|
||||
bool is_requested(piece_block block) const;
|
||||
// returns true if the block has been downloaded
|
||||
bool is_downloaded(piece_block block) const;
|
||||
// returns true if the block has been downloaded and written to disk
|
||||
bool is_finished(piece_block block) const;
|
||||
|
||||
// marks this piece-block as queued for downloading
|
||||
void mark_as_downloading(piece_block block, tcp::endpoint const& peer
|
||||
, piece_state_t s);
|
||||
void mark_as_writing(piece_block block, tcp::endpoint const& peer);
|
||||
void mark_as_finished(piece_block block, tcp::endpoint const& peer);
|
||||
|
||||
// if a piece had a hash-failure, it must be restored and
|
||||
|
|
|
@ -99,11 +99,11 @@ namespace libtorrent
|
|||
|
||||
void piece_finished(int index, bool successfully_verified);
|
||||
|
||||
void block_finished(peer_connection& c, piece_block b);
|
||||
|
||||
// the peer choked us
|
||||
void choked(peer_connection& c);
|
||||
|
||||
int count_choked() const;
|
||||
|
||||
// the peer unchoked us
|
||||
void unchoked(peer_connection& c);
|
||||
|
||||
|
|
|
@ -181,4 +181,3 @@ protected:
|
|||
|
||||
#endif
|
||||
|
||||
|
||||
|
|
|
@ -72,8 +72,11 @@ namespace libtorrent
|
|||
struct torrent_plugin;
|
||||
class torrent;
|
||||
class ip_filter;
|
||||
class port_filter;
|
||||
class connection_queue;
|
||||
|
||||
namespace fs = boost::filesystem;
|
||||
|
||||
namespace aux
|
||||
{
|
||||
// workaround for microsofts
|
||||
|
@ -135,7 +138,7 @@ namespace libtorrent
|
|||
// all torrent_handles must be destructed before the session is destructed!
|
||||
torrent_handle add_torrent(
|
||||
torrent_info const& ti
|
||||
, boost::filesystem::path const& save_path
|
||||
, fs::path const& save_path
|
||||
, entry const& resume_data = entry()
|
||||
, bool compact_mode = true
|
||||
, int block_size = 16 * 1024
|
||||
|
@ -144,7 +147,7 @@ namespace libtorrent
|
|||
// TODO: deprecated, this is for backwards compatibility only
|
||||
torrent_handle add_torrent(
|
||||
entry const& e
|
||||
, boost::filesystem::path const& save_path
|
||||
, fs::path const& save_path
|
||||
, entry const& resume_data = entry()
|
||||
, bool compact_mode = true
|
||||
, int block_size = 16 * 1024
|
||||
|
@ -158,7 +161,7 @@ namespace libtorrent
|
|||
char const* tracker_url
|
||||
, sha1_hash const& info_hash
|
||||
, char const* name
|
||||
, boost::filesystem::path const& save_path
|
||||
, fs::path const& save_path
|
||||
, entry const& resume_data = entry()
|
||||
, bool compact_mode = true
|
||||
, int block_size = 16 * 1024
|
||||
|
@ -187,8 +190,10 @@ namespace libtorrent
|
|||
#endif
|
||||
|
||||
void set_ip_filter(ip_filter const& f);
|
||||
void set_port_filter(port_filter const& f);
|
||||
void set_peer_id(peer_id const& pid);
|
||||
void set_key(int key);
|
||||
peer_id id() const;
|
||||
|
||||
bool is_listening() const;
|
||||
|
||||
|
@ -249,6 +254,16 @@ namespace libtorrent
|
|||
|
||||
connection_queue& get_connection_queue();
|
||||
|
||||
// starts/stops UPnP, NATPMP or LSD port mappers
|
||||
// they are stopped by default
|
||||
void start_lsd();
|
||||
void start_natpmp();
|
||||
void start_upnp();
|
||||
|
||||
void stop_lsd();
|
||||
void stop_natpmp();
|
||||
void stop_upnp();
|
||||
|
||||
// Resource management used for global limits.
|
||||
resource_request m_ul_bandwidth_quota;
|
||||
resource_request m_dl_bandwidth_quota;
|
||||
|
|
|
@ -54,6 +54,8 @@ namespace libtorrent
|
|||
// a plain tcp socket is used, and
|
||||
// the other settings are ignored.
|
||||
none,
|
||||
// socks4 server, requires username.
|
||||
socks4,
|
||||
// the hostname and port settings are
|
||||
// used to connect to the proxy. No
|
||||
// username or password is sent.
|
||||
|
@ -102,6 +104,8 @@ namespace libtorrent
|
|||
, connection_speed(20)
|
||||
, send_redundant_have(false)
|
||||
, lazy_bitfields(true)
|
||||
, inactivity_timeout(600)
|
||||
, unchoke_interval(20)
|
||||
#ifndef TORRENT_DISABLE_DHT
|
||||
, use_dht_as_fallback(true)
|
||||
#endif
|
||||
|
@ -226,6 +230,18 @@ namespace libtorrent
|
|||
// from stopping people from seeding.
|
||||
bool lazy_bitfields;
|
||||
|
||||
// if a peer is uninteresting and uninterested for longer
|
||||
// than this number of seconds, it will be disconnected.
|
||||
// default is 10 minutes
|
||||
int inactivity_timeout;
|
||||
|
||||
// the number of seconds between chokes/unchokes
|
||||
int unchoke_interval;
|
||||
|
||||
// if this is set, this IP will be reported do the
|
||||
// tracker in the ip= parameter.
|
||||
address announce_ip;
|
||||
|
||||
#ifndef TORRENT_DISABLE_DHT
|
||||
// while this is true, the dht will note be used unless the
|
||||
// tracker is online
|
||||
|
|
|
@ -34,12 +34,17 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
#define TORRENT_SOCKET_TYPE
|
||||
|
||||
#include "libtorrent/socks5_stream.hpp"
|
||||
#include "libtorrent/socks4_stream.hpp"
|
||||
#include "libtorrent/http_stream.hpp"
|
||||
#include "libtorrent/variant_stream.hpp"
|
||||
|
||||
namespace libtorrent
|
||||
{
|
||||
typedef variant_stream<stream_socket, socks5_stream, http_stream> socket_type;
|
||||
typedef variant_stream<
|
||||
stream_socket
|
||||
, socks5_stream
|
||||
, socks4_stream
|
||||
, http_stream> socket_type;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,33 +1,50 @@
|
|||
#include "libtorrent/io.hpp"
|
||||
#include "libtorrent/socket.hpp"
|
||||
#include <boost/bind.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <boost/function.hpp>
|
||||
#include <asio/read.hpp>
|
||||
#include <asio/write.hpp>
|
||||
/*
|
||||
|
||||
Copyright (c) 2007, Arvid Norberg
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the author nor the names of its
|
||||
contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef TORRENT_SOCKS5_STREAM_HPP_INCLUDED
|
||||
#define TORRENT_SOCKS5_STREAM_HPP_INCLUDED
|
||||
|
||||
#include "libtorrent/proxy_base.hpp"
|
||||
|
||||
namespace libtorrent {
|
||||
|
||||
class socks5_stream : boost::noncopyable
|
||||
class socks5_stream : public proxy_base
|
||||
{
|
||||
public:
|
||||
|
||||
typedef stream_socket::lowest_layer_type lowest_layer_type;
|
||||
typedef stream_socket::endpoint_type endpoint_type;
|
||||
typedef stream_socket::protocol_type protocol_type;
|
||||
|
||||
explicit socks5_stream(asio::io_service& io_service)
|
||||
: m_sock(io_service)
|
||||
, m_resolver(io_service)
|
||||
: proxy_base(io_service)
|
||||
{}
|
||||
|
||||
void set_proxy(std::string hostname, int port)
|
||||
{
|
||||
m_hostname = hostname;
|
||||
m_port = port;
|
||||
}
|
||||
|
||||
void set_username(std::string const& user
|
||||
, std::string const& password)
|
||||
{
|
||||
|
@ -35,108 +52,6 @@ public:
|
|||
m_password = password;
|
||||
}
|
||||
|
||||
template <class Mutable_Buffers, class Handler>
|
||||
void async_read_some(Mutable_Buffers const& buffers, Handler const& handler)
|
||||
{
|
||||
m_sock.async_read_some(buffers, handler);
|
||||
}
|
||||
|
||||
template <class Mutable_Buffers>
|
||||
std::size_t read_some(Mutable_Buffers const& buffers, asio::error_code& ec)
|
||||
{
|
||||
return m_sock.read_some(buffers, ec);
|
||||
}
|
||||
|
||||
template <class Mutable_Buffers>
|
||||
std::size_t read_some(Mutable_Buffers const& buffers)
|
||||
{
|
||||
return m_sock.read_some(buffers);
|
||||
}
|
||||
|
||||
template <class IO_Control_Command>
|
||||
void io_control(IO_Control_Command& ioc)
|
||||
{
|
||||
m_sock.io_control(ioc);
|
||||
}
|
||||
|
||||
template <class IO_Control_Command>
|
||||
void io_control(IO_Control_Command& ioc, asio::error_code& ec)
|
||||
{
|
||||
m_sock.io_control(ioc, ec);
|
||||
}
|
||||
|
||||
template <class Const_Buffers, class Handler>
|
||||
void async_write_some(Const_Buffers const& buffers, Handler const& handler)
|
||||
{
|
||||
m_sock.async_write_some(buffers, handler);
|
||||
}
|
||||
|
||||
void bind(endpoint_type const& endpoint)
|
||||
{
|
||||
m_sock.bind(endpoint);
|
||||
}
|
||||
|
||||
template <class Error_Handler>
|
||||
void bind(endpoint_type const& endpoint, Error_Handler const& error_handler)
|
||||
{
|
||||
m_sock.bind(endpoint, error_handler);
|
||||
}
|
||||
|
||||
void open(protocol_type const& p)
|
||||
{
|
||||
m_sock.open(p);
|
||||
}
|
||||
|
||||
template <class Error_Handler>
|
||||
void open(protocol_type const& p, Error_Handler const& error_handler)
|
||||
{
|
||||
m_sock.open(p, error_handler);
|
||||
}
|
||||
|
||||
void close()
|
||||
{
|
||||
m_remote_endpoint = endpoint_type();
|
||||
m_sock.close();
|
||||
}
|
||||
|
||||
template <class Error_Handler>
|
||||
void close(Error_Handler const& error_handler)
|
||||
{
|
||||
m_sock.close(error_handler);
|
||||
}
|
||||
|
||||
endpoint_type remote_endpoint()
|
||||
{
|
||||
return m_remote_endpoint;
|
||||
}
|
||||
|
||||
template <class Error_Handler>
|
||||
endpoint_type remote_endpoint(Error_Handler const& error_handler)
|
||||
{
|
||||
return m_remote_endpoint;
|
||||
}
|
||||
|
||||
endpoint_type local_endpoint()
|
||||
{
|
||||
return m_sock.local_endpoint();
|
||||
}
|
||||
|
||||
template <class Error_Handler>
|
||||
endpoint_type local_endpoint(Error_Handler const& error_handler)
|
||||
{
|
||||
return m_sock.local_endpoint(error_handler);
|
||||
}
|
||||
|
||||
asio::io_service& io_service()
|
||||
{
|
||||
return m_sock.io_service();
|
||||
}
|
||||
|
||||
lowest_layer_type& lowest_layer()
|
||||
{
|
||||
return m_sock.lowest_layer();
|
||||
}
|
||||
|
||||
typedef boost::function<void(asio::error_code const&)> handler_type;
|
||||
|
||||
template <class Handler>
|
||||
|
@ -176,20 +91,13 @@ private:
|
|||
void connect2(asio::error_code const& e, boost::shared_ptr<handler_type> h);
|
||||
void connect3(asio::error_code const& e, boost::shared_ptr<handler_type> h);
|
||||
|
||||
stream_socket m_sock;
|
||||
// the socks5 proxy
|
||||
std::string m_hostname;
|
||||
int m_port;
|
||||
// send and receive buffer
|
||||
std::vector<char> m_buffer;
|
||||
// proxy authentication
|
||||
std::string m_user;
|
||||
std::string m_password;
|
||||
|
||||
endpoint_type m_remote_endpoint;
|
||||
|
||||
tcp::resolver m_resolver;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -41,9 +41,9 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
#endif
|
||||
|
||||
#include <boost/limits.hpp>
|
||||
#include <boost/filesystem/path.hpp>
|
||||
#include <boost/thread.hpp>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <boost/filesystem/path.hpp>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
|
@ -52,6 +52,9 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
|
||||
#include "libtorrent/torrent_info.hpp"
|
||||
#include "libtorrent/piece_picker.hpp"
|
||||
#include "libtorrent/intrusive_ptr_base.hpp"
|
||||
#include "libtorrent/peer_request.hpp"
|
||||
#include "libtorrent/hasher.hpp"
|
||||
#include "libtorrent/config.hpp"
|
||||
|
||||
namespace libtorrent
|
||||
|
@ -61,8 +64,11 @@ namespace libtorrent
|
|||
struct piece_checker_data;
|
||||
}
|
||||
|
||||
namespace fs = boost::filesystem;
|
||||
|
||||
class session;
|
||||
struct file_pool;
|
||||
struct disk_io_job;
|
||||
|
||||
#if defined(_WIN32) && defined(UNICODE)
|
||||
|
||||
|
@ -72,11 +78,11 @@ namespace libtorrent
|
|||
|
||||
TORRENT_EXPORT std::vector<std::pair<size_type, std::time_t> > get_filesizes(
|
||||
torrent_info const& t
|
||||
, boost::filesystem::path p);
|
||||
, fs::path p);
|
||||
|
||||
TORRENT_EXPORT bool match_filesizes(
|
||||
torrent_info const& t
|
||||
, boost::filesystem::path p
|
||||
, fs::path p
|
||||
, std::vector<std::pair<size_type, std::time_t> > const& sizes
|
||||
, bool compact_mode
|
||||
, std::string* error = 0);
|
||||
|
@ -89,6 +95,15 @@ namespace libtorrent
|
|||
std::string m_msg;
|
||||
};
|
||||
|
||||
struct TORRENT_EXPORT partial_hash
|
||||
{
|
||||
partial_hash(): offset(0) {}
|
||||
// the number of bytes in the piece that has been hashed
|
||||
int offset;
|
||||
// the sha-1 context
|
||||
hasher h;
|
||||
};
|
||||
|
||||
struct TORRENT_EXPORT storage_interface
|
||||
{
|
||||
// create directories and set file sizes
|
||||
|
@ -103,7 +118,7 @@ namespace libtorrent
|
|||
// may throw file_error if storage for slot hasn't been allocated
|
||||
virtual void write(const char* buf, int slot, int offset, int size) = 0;
|
||||
|
||||
virtual bool move_storage(boost::filesystem::path save_path) = 0;
|
||||
virtual bool move_storage(fs::path save_path) = 0;
|
||||
|
||||
// verify storage dependent fast resume entries
|
||||
virtual bool verify_resume_data(entry& rd, std::string& error) = 0;
|
||||
|
@ -121,6 +136,9 @@ namespace libtorrent
|
|||
// in slot3 and the data in slot3 in slot1
|
||||
virtual void swap_slots3(int slot1, int slot2, int slot3) = 0;
|
||||
|
||||
// returns the sha1-hash for the data at the given slot
|
||||
virtual sha1_hash hash_for_slot(int slot, partial_hash& h, int piece_size) = 0;
|
||||
|
||||
// this will close all open files that are opened for
|
||||
// writing. This is called when a torrent has finished
|
||||
// downloading.
|
||||
|
@ -129,24 +147,32 @@ namespace libtorrent
|
|||
};
|
||||
|
||||
typedef storage_interface* (&storage_constructor_type)(
|
||||
torrent_info const&, boost::filesystem::path const&
|
||||
torrent_info const&, fs::path const&
|
||||
, file_pool&);
|
||||
|
||||
TORRENT_EXPORT storage_interface* default_storage_constructor(torrent_info const& ti
|
||||
, boost::filesystem::path const& path, file_pool& fp);
|
||||
, fs::path const& path, file_pool& fp);
|
||||
|
||||
// returns true if the filesystem the path relies on supports
|
||||
// sparse files or automatic zero filling of files.
|
||||
TORRENT_EXPORT bool supports_sparse_files(boost::filesystem::path const& p);
|
||||
TORRENT_EXPORT bool supports_sparse_files(fs::path const& p);
|
||||
|
||||
class TORRENT_EXPORT piece_manager : boost::noncopyable
|
||||
struct disk_io_thread;
|
||||
|
||||
class TORRENT_EXPORT piece_manager
|
||||
: public intrusive_ptr_base<piece_manager>
|
||||
, boost::noncopyable
|
||||
{
|
||||
friend class invariant_access;
|
||||
friend struct disk_io_thread;
|
||||
public:
|
||||
|
||||
piece_manager(
|
||||
const torrent_info& info
|
||||
, const boost::filesystem::path& path
|
||||
boost::shared_ptr<void> const& torrent
|
||||
, torrent_info const& ti
|
||||
, fs::path const& path
|
||||
, file_pool& fp
|
||||
, disk_io_thread& io
|
||||
, storage_constructor_type sc);
|
||||
|
||||
~piece_manager();
|
||||
|
@ -156,35 +182,38 @@ namespace libtorrent
|
|||
std::pair<bool, float> check_files(std::vector<bool>& pieces
|
||||
, int& num_pieces, boost::recursive_mutex& mutex);
|
||||
|
||||
void release_files();
|
||||
|
||||
void write_resume_data(entry& rd) const;
|
||||
bool verify_resume_data(entry& rd, std::string& error);
|
||||
|
||||
bool is_allocating() const;
|
||||
bool allocate_slots(int num_slots, bool abort_on_disk = false);
|
||||
bool is_allocating() const
|
||||
{ return m_state == state_allocating; }
|
||||
|
||||
void mark_failed(int index);
|
||||
|
||||
unsigned long piece_crc(
|
||||
int slot_index
|
||||
, int block_size
|
||||
, piece_picker::block_info const* bi);
|
||||
|
||||
int slot_for_piece(int piece_index) const;
|
||||
|
||||
size_type read(
|
||||
char* buf
|
||||
, int piece_index
|
||||
, int offset
|
||||
, int size);
|
||||
void async_read(
|
||||
peer_request const& r
|
||||
, boost::function<void(int, disk_io_job const&)> const& handler);
|
||||
|
||||
void write(
|
||||
const char* buf
|
||||
, int piece_index
|
||||
, int offset
|
||||
, int size);
|
||||
void async_write(
|
||||
peer_request const& r
|
||||
, char const* buffer
|
||||
, boost::function<void(int, disk_io_job const&)> const& f);
|
||||
|
||||
boost::filesystem::path const& save_path() const;
|
||||
bool move_storage(boost::filesystem::path const&);
|
||||
void async_hash(int piece, boost::function<void(int, disk_io_job const&)> const& f);
|
||||
|
||||
fs::path save_path() const;
|
||||
|
||||
void async_release_files(
|
||||
boost::function<void(int, disk_io_job const&)> const& handler);
|
||||
void async_move_storage(fs::path const& p
|
||||
, boost::function<void(int, disk_io_job const&)> const& handler);
|
||||
|
||||
// fills the vector that maps all allocated
|
||||
// slots to the piece that is stored (or
|
||||
|
@ -192,11 +221,134 @@ namespace libtorrent
|
|||
// of unassigned pieces and -1 is unallocated
|
||||
void export_piece_map(std::vector<int>& pieces) const;
|
||||
|
||||
bool compact_allocation() const;
|
||||
bool compact_allocation() const
|
||||
{ return m_compact_mode; }
|
||||
|
||||
#ifndef NDEBUG
|
||||
std::string name() const { return m_info.name(); }
|
||||
#endif
|
||||
|
||||
private:
|
||||
class impl;
|
||||
std::auto_ptr<impl> m_pimpl;
|
||||
|
||||
bool allocate_slots(int num_slots, bool abort_on_disk = false);
|
||||
|
||||
int identify_data(
|
||||
const std::vector<char>& piece_data
|
||||
, int current_slot
|
||||
, std::vector<bool>& have_pieces
|
||||
, int& num_pieces
|
||||
, const std::multimap<sha1_hash, int>& hash_to_piece
|
||||
, boost::recursive_mutex& mutex);
|
||||
|
||||
size_type read_impl(
|
||||
char* buf
|
||||
, int piece_index
|
||||
, int offset
|
||||
, int size);
|
||||
|
||||
void write_impl(
|
||||
const char* buf
|
||||
, int piece_index
|
||||
, int offset
|
||||
, int size);
|
||||
|
||||
sha1_hash hash_for_piece_impl(int piece);
|
||||
|
||||
void release_files_impl();
|
||||
|
||||
bool move_storage_impl(fs::path const& save_path);
|
||||
|
||||
int allocate_slot_for_piece(int piece_index);
|
||||
#ifndef NDEBUG
|
||||
void check_invariant() const;
|
||||
#ifdef TORRENT_STORAGE_DEBUG
|
||||
void debug_log() const;
|
||||
#endif
|
||||
#endif
|
||||
boost::scoped_ptr<storage_interface> m_storage;
|
||||
|
||||
// if this is true, pieces are always allocated at the
|
||||
// lowest possible slot index. If it is false, pieces
|
||||
// are always written to their final place immediately
|
||||
bool m_compact_mode;
|
||||
|
||||
// if this is true, pieces that haven't been downloaded
|
||||
// will be filled with zeroes. Not filling with zeroes
|
||||
// will not work in some cases (where a seek cannot pass
|
||||
// the end of the file).
|
||||
bool m_fill_mode;
|
||||
|
||||
// a bitmask representing the pieces we have
|
||||
std::vector<bool> m_have_piece;
|
||||
|
||||
torrent_info const& m_info;
|
||||
|
||||
// slots that haven't had any file storage allocated
|
||||
std::vector<int> m_unallocated_slots;
|
||||
// slots that have file storage, but isn't assigned to a piece
|
||||
std::vector<int> m_free_slots;
|
||||
|
||||
enum
|
||||
{
|
||||
has_no_slot = -3 // the piece has no storage
|
||||
};
|
||||
|
||||
// maps piece indices to slots. If a piece doesn't
|
||||
// have any storage, it is set to 'has_no_slot'
|
||||
std::vector<int> m_piece_to_slot;
|
||||
|
||||
enum
|
||||
{
|
||||
unallocated = -1, // the slot is unallocated
|
||||
unassigned = -2 // the slot is allocated but not assigned to a piece
|
||||
};
|
||||
|
||||
// maps slots to piece indices, if a slot doesn't have a piece
|
||||
// it can either be 'unassigned' or 'unallocated'
|
||||
std::vector<int> m_slot_to_piece;
|
||||
|
||||
fs::path m_save_path;
|
||||
|
||||
mutable boost::recursive_mutex m_mutex;
|
||||
|
||||
bool m_allocating;
|
||||
boost::mutex m_allocating_monitor;
|
||||
boost::condition m_allocating_condition;
|
||||
|
||||
// these states are used while checking/allocating the torrent
|
||||
|
||||
enum {
|
||||
// the default initial state
|
||||
state_none,
|
||||
// the file checking is complete
|
||||
state_finished,
|
||||
// creating the directories
|
||||
state_create_files,
|
||||
// checking the files
|
||||
state_full_check,
|
||||
// allocating files (in non-compact mode)
|
||||
state_allocating
|
||||
} m_state;
|
||||
int m_current_slot;
|
||||
|
||||
std::vector<char> m_piece_data;
|
||||
|
||||
// this maps a piece hash to piece index. It will be
|
||||
// build the first time it is used (to save time if it
|
||||
// isn't needed)
|
||||
std::multimap<sha1_hash, int> m_hash_to_piece;
|
||||
|
||||
std::map<int, partial_hash> m_piece_hasher;
|
||||
|
||||
disk_io_thread& m_io_thread;
|
||||
|
||||
// the reason for this to be a void pointer
|
||||
// is to avoid creating a dependency on the
|
||||
// torrent. This shared_ptr is here only
|
||||
// to keep the torrent object alive until
|
||||
// the piece_manager destructs. This is because
|
||||
// the torrent_info object is owned by the torrent.
|
||||
boost::shared_ptr<void> m_torrent;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -51,7 +51,8 @@ namespace libtorrent
|
|||
}
|
||||
}
|
||||
|
||||
#if (!defined (__MACH__) && !defined (_WIN32) && !defined(_POSIX_MONOTONIC_CLOCK) || _POSIX_MONOTONIC_CLOCK < 0) || defined (TORRENT_USE_BOOST_DATE_TIME)
|
||||
#if (!defined (__MACH__) && !defined (_WIN32) && (!defined(_POSIX_MONOTONIC_CLOCK) \
|
||||
|| _POSIX_MONOTONIC_CLOCK < 0)) || defined (TORRENT_USE_BOOST_DATE_TIME)
|
||||
|
||||
#include <boost/date_time/posix_time/posix_time_types.hpp>
|
||||
|
||||
|
@ -99,8 +100,12 @@ namespace libtorrent
|
|||
inline bool is_negative(time_duration dt) { return dt.diff < 0; }
|
||||
inline bool operator<(time_duration lhs, time_duration rhs)
|
||||
{ return lhs.diff < rhs.diff; }
|
||||
inline bool operator<=(time_duration lhs, time_duration rhs)
|
||||
{ return lhs.diff <= rhs.diff; }
|
||||
inline bool operator>(time_duration lhs, time_duration rhs)
|
||||
{ return lhs.diff > rhs.diff; }
|
||||
inline bool operator>=(time_duration lhs, time_duration rhs)
|
||||
{ return lhs.diff >= rhs.diff; }
|
||||
|
||||
// libtorrent time type
|
||||
struct ptime
|
||||
|
@ -381,3 +386,4 @@ namespace libtorrent
|
|||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
|
|
|
@ -68,6 +68,7 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
#include "libtorrent/escape_string.hpp"
|
||||
#include "libtorrent/bandwidth_manager.hpp"
|
||||
#include "libtorrent/storage.hpp"
|
||||
#include "libtorrent/hasher.hpp"
|
||||
|
||||
namespace libtorrent
|
||||
{
|
||||
|
@ -84,6 +85,8 @@ namespace libtorrent
|
|||
struct piece_checker_data;
|
||||
}
|
||||
|
||||
namespace fs = boost::filesystem;
|
||||
|
||||
// a torrent is a class that holds information
|
||||
// for a specific download. It updates itself against
|
||||
// the tracker
|
||||
|
@ -96,7 +99,7 @@ namespace libtorrent
|
|||
aux::session_impl& ses
|
||||
, aux::checker_impl& checker
|
||||
, torrent_info const& tf
|
||||
, boost::filesystem::path const& save_path
|
||||
, fs::path const& save_path
|
||||
, tcp::endpoint const& net_interface
|
||||
, bool compact_mode
|
||||
, int block_size
|
||||
|
@ -111,7 +114,7 @@ namespace libtorrent
|
|||
, char const* tracker_url
|
||||
, sha1_hash const& info_hash
|
||||
, char const* name
|
||||
, boost::filesystem::path const& save_path
|
||||
, fs::path const& save_path
|
||||
, tcp::endpoint const& net_interface
|
||||
, bool compact_mode
|
||||
, int block_size
|
||||
|
@ -184,6 +187,8 @@ namespace libtorrent
|
|||
void filter_files(std::vector<bool> const& files);
|
||||
// ============ end deprecation =============
|
||||
|
||||
void piece_availability(std::vector<int>& avail) const;
|
||||
|
||||
void set_piece_priority(int index, int priority);
|
||||
int piece_priority(int index) const;
|
||||
|
||||
|
@ -422,11 +427,12 @@ namespace libtorrent
|
|||
// completed() is called immediately after it.
|
||||
void finished();
|
||||
|
||||
bool verify_piece(int piece_index);
|
||||
void async_verify_piece(int piece_index, boost::function<void(bool)> const&);
|
||||
|
||||
// this is called from the peer_connection
|
||||
// each time a piece has failed the hash
|
||||
// test
|
||||
void piece_finished(int index, bool passed_hash_check);
|
||||
void piece_failed(int index);
|
||||
void received_redundant_data(int num_bytes)
|
||||
{ assert(num_bytes > 0); m_total_redundant_bytes += num_bytes; }
|
||||
|
@ -446,17 +452,15 @@ namespace libtorrent
|
|||
- m_num_pieces - m_picker->num_filtered() == 0;
|
||||
}
|
||||
|
||||
boost::filesystem::path save_path() const;
|
||||
fs::path save_path() const;
|
||||
alert_manager& alerts() const;
|
||||
piece_picker& picker()
|
||||
{
|
||||
assert(!is_seed());
|
||||
assert(m_picker.get());
|
||||
return *m_picker;
|
||||
}
|
||||
bool has_picker() const
|
||||
{
|
||||
assert((valid_metadata() && !is_seed()) == bool(m_picker.get() != 0));
|
||||
return m_picker.get() != 0;
|
||||
}
|
||||
policy& get_policy()
|
||||
|
@ -503,14 +507,14 @@ namespace libtorrent
|
|||
|
||||
void set_max_uploads(int limit);
|
||||
void set_max_connections(int limit);
|
||||
bool move_storage(boost::filesystem::path const& save_path);
|
||||
void move_storage(fs::path const& save_path);
|
||||
|
||||
// unless this returns true, new connections must wait
|
||||
// with their initialization.
|
||||
bool ready_for_connections() const
|
||||
{ return m_connections_initialized; }
|
||||
bool valid_metadata() const
|
||||
{ return m_storage.get() != 0; }
|
||||
{ return m_torrent_file.is_valid(); }
|
||||
|
||||
// parses the info section from the given
|
||||
// bencoded tree and moves the torrent
|
||||
|
@ -520,6 +524,12 @@ namespace libtorrent
|
|||
|
||||
private:
|
||||
|
||||
void on_files_released(int ret, disk_io_job const& j);
|
||||
void on_storage_moved(int ret, disk_io_job const& j);
|
||||
|
||||
void on_piece_verified(int ret, disk_io_job const& j
|
||||
, boost::function<void(bool)> f);
|
||||
|
||||
void try_next_tracker();
|
||||
int prioritize_tracker(int tracker_index);
|
||||
void on_country_lookup(asio::error_code const& error, tcp::resolver::iterator i
|
||||
|
@ -552,7 +562,27 @@ namespace libtorrent
|
|||
// if this pointer is 0, the torrent is in
|
||||
// a state where the metadata hasn't been
|
||||
// received yet.
|
||||
boost::scoped_ptr<piece_manager> m_storage;
|
||||
// the piece_manager keeps the torrent object
|
||||
// alive by holding a shared_ptr to it and
|
||||
// the torrent keeps the piece manager alive
|
||||
// with this intrusive_ptr. This cycle is
|
||||
// broken when torrent::abort() is called
|
||||
// Then the torrent releases the piece_manager
|
||||
// and when the piece_manager is complete with all
|
||||
// outstanding disk io jobs (that keeps
|
||||
// the piece_manager alive) it will destruct
|
||||
// and release the torrent file. The reason for
|
||||
// this is that the torrent_info is used by
|
||||
// the piece_manager, and stored in the
|
||||
// torrent, so the torrent cannot destruct
|
||||
// before the piece_manager.
|
||||
boost::intrusive_ptr<piece_manager> m_owning_storage;
|
||||
|
||||
// this is a weak (non owninig) pointer to
|
||||
// the piece_manager. This is used after the torrent
|
||||
// has been aborted, and it can no longer own
|
||||
// the object.
|
||||
piece_manager* m_storage;
|
||||
|
||||
// the time of next tracker request
|
||||
ptime m_next_request;
|
||||
|
@ -689,7 +719,7 @@ namespace libtorrent
|
|||
// are opened through
|
||||
tcp::endpoint m_net_interface;
|
||||
|
||||
boost::filesystem::path m_save_path;
|
||||
fs::path m_save_path;
|
||||
|
||||
// determines the storage state for this torrent.
|
||||
const bool m_compact_mode;
|
||||
|
|
|
@ -54,6 +54,8 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
|
||||
namespace libtorrent
|
||||
{
|
||||
namespace fs = boost::filesystem;
|
||||
|
||||
namespace aux
|
||||
{
|
||||
struct session_impl;
|
||||
|
@ -202,15 +204,22 @@ namespace libtorrent
|
|||
int block_size;
|
||||
};
|
||||
|
||||
struct TORRENT_EXPORT block_info
|
||||
{
|
||||
enum block_state_t
|
||||
{ none, requested, writing, finished };
|
||||
|
||||
tcp::endpoint peer;
|
||||
unsigned state:2;
|
||||
unsigned num_downloads:14;
|
||||
};
|
||||
|
||||
struct TORRENT_EXPORT partial_piece_info
|
||||
{
|
||||
enum { max_blocks_per_piece = 256 };
|
||||
int piece_index;
|
||||
int blocks_in_piece;
|
||||
std::bitset<max_blocks_per_piece> requested_blocks;
|
||||
std::bitset<max_blocks_per_piece> finished_blocks;
|
||||
tcp::endpoint peer[max_blocks_per_piece];
|
||||
int num_downloads[max_blocks_per_piece];
|
||||
block_info blocks[max_blocks_per_piece];
|
||||
enum state_t { none, slow, medium, fast };
|
||||
state_t piece_state;
|
||||
};
|
||||
|
@ -269,6 +278,8 @@ namespace libtorrent
|
|||
|
||||
// ================ end deprecation ============
|
||||
|
||||
void piece_availability(std::vector<int>& avail) const;
|
||||
|
||||
// priority must be within the range [0, 7]
|
||||
void piece_priority(int index, int priority) const;
|
||||
int piece_priority(int index) const;
|
||||
|
@ -321,7 +332,7 @@ namespace libtorrent
|
|||
// the ratio is uploaded / downloaded. less than 1 is not allowed
|
||||
void set_ratio(float up_down_ratio) const;
|
||||
|
||||
boost::filesystem::path save_path() const;
|
||||
fs::path save_path() const;
|
||||
|
||||
// -1 means unlimited unchokes
|
||||
void set_max_uploads(int max_uploads) const;
|
||||
|
@ -333,7 +344,7 @@ namespace libtorrent
|
|||
, std::string const& password) const;
|
||||
|
||||
// post condition: save_path() == save_path if true is returned
|
||||
bool move_storage(boost::filesystem::path const& save_path) const;
|
||||
void move_storage(fs::path const& save_path) const;
|
||||
|
||||
const sha1_hash& info_hash() const
|
||||
{ return m_info_hash; }
|
||||
|
|
|
@ -62,16 +62,18 @@ namespace libtorrent
|
|||
namespace pt = boost::posix_time;
|
||||
namespace gr = boost::gregorian;
|
||||
|
||||
namespace fs = boost::filesystem;
|
||||
|
||||
struct TORRENT_EXPORT file_entry
|
||||
{
|
||||
boost::filesystem::path path;
|
||||
fs::path path;
|
||||
size_type offset; // the offset of this file inside the torrent
|
||||
size_type size; // the size of this file
|
||||
// if the path was incorrectly encoded, this is
|
||||
// the origianal corrupt encoded string. It is
|
||||
// preserved in order to be able to reproduce
|
||||
// the correct info-hash
|
||||
boost::shared_ptr<const boost::filesystem::path> orig_path;
|
||||
boost::shared_ptr<const fs::path> orig_path;
|
||||
};
|
||||
|
||||
struct TORRENT_EXPORT file_slice
|
||||
|
@ -109,7 +111,7 @@ namespace libtorrent
|
|||
void set_piece_size(int size);
|
||||
void set_hash(int index, sha1_hash const& h);
|
||||
void add_tracker(std::string const& url, int tier = 0);
|
||||
void add_file(boost::filesystem::path file, size_type size);
|
||||
void add_file(fs::path file, size_type size);
|
||||
void add_url_seed(std::string const& url);
|
||||
|
||||
std::vector<file_slice> map_block(int piece, size_type offset, int size) const;
|
||||
|
@ -192,6 +194,8 @@ namespace libtorrent
|
|||
// used by seeds
|
||||
void seed_free();
|
||||
|
||||
void swap(torrent_info& ti);
|
||||
|
||||
private:
|
||||
|
||||
void read_torrent_info(const entry& libtorrent);
|
||||
|
|
|
@ -62,6 +62,7 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
#include "libtorrent/config.hpp"
|
||||
#include "libtorrent/time.hpp"
|
||||
#include "libtorrent/connection_queue.hpp"
|
||||
#include "libtorrent/intrusive_ptr_base.hpp"
|
||||
|
||||
namespace libtorrent
|
||||
{
|
||||
|
@ -148,15 +149,10 @@ namespace libtorrent
|
|||
, request_callback* requester
|
||||
, int maximum_tracker_response_length);
|
||||
|
||||
TORRENT_EXPORT void intrusive_ptr_add_ref(timeout_handler const*);
|
||||
TORRENT_EXPORT void intrusive_ptr_release(timeout_handler const*);
|
||||
|
||||
struct TORRENT_EXPORT timeout_handler
|
||||
: boost::noncopyable
|
||||
: intrusive_ptr_base<timeout_handler>
|
||||
, boost::noncopyable
|
||||
{
|
||||
friend TORRENT_EXPORT void intrusive_ptr_add_ref(timeout_handler const*);
|
||||
friend TORRENT_EXPORT void intrusive_ptr_release(timeout_handler const*);
|
||||
|
||||
timeout_handler(asio::strand& str);
|
||||
|
||||
void set_timeout(int completion_timeout, int read_timeout);
|
||||
|
@ -187,7 +183,6 @@ namespace libtorrent
|
|||
|
||||
typedef boost::mutex mutex_t;
|
||||
mutable mutex_t m_mutex;
|
||||
mutable int m_refs;
|
||||
};
|
||||
|
||||
struct TORRENT_EXPORT tracker_connection
|
||||
|
|
|
@ -123,7 +123,7 @@ namespace libtorrent
|
|||
void write_request(peer_request const& r);
|
||||
void write_cancel(peer_request const& r) {}
|
||||
void write_have(int index) {}
|
||||
void write_piece(peer_request const& r) {}
|
||||
void write_piece(peer_request const& r, char const* buffer) { assert(false); }
|
||||
void write_keepalive() {}
|
||||
void on_connected();
|
||||
|
||||
|
|
|
@ -21,7 +21,9 @@ torrent_info.cpp tracker_manager.cpp http_connection.cpp \
|
|||
http_tracker_connection.cpp udp_tracker_connection.cpp \
|
||||
alert.cpp identify_client.cpp ip_filter.cpp file.cpp metadata_transfer.cpp \
|
||||
logger.cpp file_pool.cpp ut_pex.cpp lsd.cpp upnp.cpp instantiate_connection.cpp \
|
||||
socks5_stream.cpp http_stream.cpp connection_queue.cpp $(kademlia_sources)
|
||||
socks5_stream.cpp socks4_stream.cpp http_stream.cpp connection_queue.cpp \
|
||||
disk_io_thread.cpp \
|
||||
$(kademlia_sources)
|
||||
|
||||
noinst_HEADERS = \
|
||||
$(top_srcdir)/include/libtorrent/alert.hpp \
|
||||
|
@ -34,6 +36,7 @@ $(top_srcdir)/include/libtorrent/bencode.hpp \
|
|||
$(top_srcdir)/include/libtorrent/buffer.hpp \
|
||||
$(top_srcdir)/include/libtorrent/connection_queue.hpp \
|
||||
$(top_srcdir)/include/libtorrent/debug.hpp \
|
||||
$(top_srcdir)/include/libtorrent/disk_io_thread.hpp \
|
||||
$(top_srcdir)/include/libtorrent/entry.hpp \
|
||||
$(top_srcdir)/include/libtorrent/escape_string.hpp \
|
||||
$(top_srcdir)/include/libtorrent/extensions.hpp \
|
||||
|
@ -50,6 +53,7 @@ $(top_srcdir)/include/libtorrent/session_settings.hpp \
|
|||
$(top_srcdir)/include/libtorrent/http_tracker_connection.hpp \
|
||||
$(top_srcdir)/include/libtorrent/identify_client.hpp \
|
||||
$(top_srcdir)/include/libtorrent/instantiate_connection.hpp \
|
||||
$(top_srcdir)/include/libtorrent/intrusive_ptr_base.hpp \
|
||||
$(top_srcdir)/include/libtorrent/invariant_check.hpp \
|
||||
$(top_srcdir)/include/libtorrent/io.hpp \
|
||||
$(top_srcdir)/include/libtorrent/ip_filter.hpp \
|
||||
|
@ -72,6 +76,7 @@ $(top_srcdir)/include/libtorrent/session.hpp \
|
|||
$(top_srcdir)/include/libtorrent/size_type.hpp \
|
||||
$(top_srcdir)/include/libtorrent/socket.hpp \
|
||||
$(top_srcdir)/include/libtorrent/socket_type.hpp \
|
||||
$(top_srcdir)/include/libtorrent/socks4_stream.hpp \
|
||||
$(top_srcdir)/include/libtorrent/socks5_stream.hpp \
|
||||
$(top_srcdir)/include/libtorrent/stat.hpp \
|
||||
$(top_srcdir)/include/libtorrent/storage.hpp \
|
||||
|
|
|
@ -277,6 +277,8 @@ namespace libtorrent
|
|||
|
||||
void bt_peer_connection::write_pe1_2_dhkey()
|
||||
{
|
||||
INVARIANT_CHECK;
|
||||
|
||||
assert(!m_encrypted);
|
||||
assert(!m_rc4_encrypted);
|
||||
assert(!m_DH_key_exchange.get());
|
||||
|
@ -305,6 +307,8 @@ namespace libtorrent
|
|||
|
||||
void bt_peer_connection::write_pe3_sync()
|
||||
{
|
||||
INVARIANT_CHECK;
|
||||
|
||||
assert (!m_encrypted);
|
||||
assert (!m_rc4_encrypted);
|
||||
assert (is_local());
|
||||
|
@ -383,6 +387,8 @@ namespace libtorrent
|
|||
|
||||
void bt_peer_connection::write_pe4_sync(int crypto_select)
|
||||
{
|
||||
INVARIANT_CHECK;
|
||||
|
||||
assert(!is_local());
|
||||
assert(!m_encrypted);
|
||||
assert(!m_rc4_encrypted);
|
||||
|
@ -415,6 +421,8 @@ namespace libtorrent
|
|||
void bt_peer_connection::write_pe_vc_cryptofield(buffer::interval& write_buf,
|
||||
int crypto_field, int pad_size)
|
||||
{
|
||||
INVARIANT_CHECK;
|
||||
|
||||
assert(crypto_field <= 0x03 && crypto_field > 0);
|
||||
assert(pad_size == 0); // pad not used yet
|
||||
// vc,crypto_field,len(pad),pad, (len(ia))
|
||||
|
@ -444,6 +452,8 @@ namespace libtorrent
|
|||
|
||||
void bt_peer_connection::init_pe_RC4_handler(char const* secret, sha1_hash const& stream_key)
|
||||
{
|
||||
INVARIANT_CHECK;
|
||||
|
||||
assert(secret);
|
||||
|
||||
hasher h;
|
||||
|
@ -483,8 +493,9 @@ namespace libtorrent
|
|||
assert (begin);
|
||||
assert (end);
|
||||
assert (end > begin);
|
||||
assert (!m_rc4_encrypted || m_encrypted);
|
||||
|
||||
if (m_rc4_encrypted && m_encrypted)
|
||||
if (m_rc4_encrypted)
|
||||
m_RC4_handler->encrypt(begin, end - begin);
|
||||
|
||||
peer_connection::send_buffer(begin, end);
|
||||
|
@ -492,7 +503,9 @@ namespace libtorrent
|
|||
|
||||
buffer::interval bt_peer_connection::allocate_send_buffer(int size)
|
||||
{
|
||||
if (m_rc4_encrypted && m_encrypted)
|
||||
assert(!m_rc4_encrypted || m_encrypted);
|
||||
|
||||
if (m_rc4_encrypted)
|
||||
{
|
||||
m_enc_send_buffer = peer_connection::allocate_send_buffer(size);
|
||||
return m_enc_send_buffer;
|
||||
|
@ -506,7 +519,9 @@ namespace libtorrent
|
|||
|
||||
void bt_peer_connection::setup_send()
|
||||
{
|
||||
if (m_rc4_encrypted && m_encrypted)
|
||||
assert(!m_rc4_encrypted || m_encrypted);
|
||||
|
||||
if (m_rc4_encrypted)
|
||||
{
|
||||
assert (m_enc_send_buffer.begin);
|
||||
assert (m_enc_send_buffer.end);
|
||||
|
@ -1129,6 +1144,7 @@ namespace libtorrent
|
|||
{
|
||||
INVARIANT_CHECK;
|
||||
|
||||
assert(!in_handshake());
|
||||
boost::shared_ptr<torrent> t = associated_torrent().lock();
|
||||
assert(t);
|
||||
assert(m_sent_bitfield == false);
|
||||
|
@ -1319,7 +1335,7 @@ namespace libtorrent
|
|||
send_buffer(msg, msg + packet_size);
|
||||
}
|
||||
|
||||
void bt_peer_connection::write_piece(peer_request const& r)
|
||||
void bt_peer_connection::write_piece(peer_request const& r, char const* buffer)
|
||||
{
|
||||
INVARIANT_CHECK;
|
||||
|
||||
|
@ -1334,9 +1350,7 @@ namespace libtorrent
|
|||
detail::write_uint8(msg_piece, i.begin);
|
||||
detail::write_int32(r.piece, i.begin);
|
||||
detail::write_int32(r.start, i.begin);
|
||||
|
||||
t->filesystem().read(
|
||||
i.begin, r.piece, r.start, r.length);
|
||||
std::memcpy(i.begin, buffer, r.length);
|
||||
|
||||
assert(i.begin + r.length == i.end);
|
||||
|
||||
|
@ -1357,10 +1371,11 @@ namespace libtorrent
|
|||
return p.connection != m_pc
|
||||
&& p.connection
|
||||
&& p.connection->pid() == m_id
|
||||
&& !p.connection->pid().is_all_zeros();
|
||||
&& !p.connection->pid().is_all_zeros()
|
||||
&& p.ip.address() == m_pc->remote().address();
|
||||
}
|
||||
|
||||
peer_id m_id;
|
||||
peer_id const& m_id;
|
||||
peer_connection const* m_pc;
|
||||
};
|
||||
}
|
||||
|
@ -1382,6 +1397,7 @@ namespace libtorrent
|
|||
m_statistics.received_bytes(0, bytes_transferred);
|
||||
|
||||
#ifndef TORRENT_DISABLE_ENCRYPTION
|
||||
assert(in_handshake() || !m_rc4_encrypted || m_encrypted);
|
||||
if (m_rc4_encrypted && m_encrypted)
|
||||
{
|
||||
buffer::interval wr_buf = wr_recv_buffer();
|
||||
|
@ -1781,6 +1797,9 @@ namespace libtorrent
|
|||
{
|
||||
recv_buffer.begin += pad_size;
|
||||
int len_ia = detail::read_int16(recv_buffer.begin);
|
||||
|
||||
if (len_ia < 0) throw protocol_error("invalid len_ia in handshake");
|
||||
|
||||
#ifdef TORRENT_VERBOSE_LOGGING
|
||||
(*m_logger) << " len(IA) : " << len_ia << "\n";
|
||||
#endif
|
||||
|
@ -1972,8 +1991,6 @@ namespace libtorrent
|
|||
, (char*)info_hash.begin());
|
||||
|
||||
attach_to_torrent(info_hash);
|
||||
t = associated_torrent().lock();
|
||||
assert(t);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -1990,17 +2007,15 @@ namespace libtorrent
|
|||
#ifdef TORRENT_VERBOSE_LOGGING
|
||||
(*m_logger) << " info_hash received\n";
|
||||
#endif
|
||||
t = associated_torrent().lock();
|
||||
assert(t);
|
||||
}
|
||||
|
||||
// Respond with handshake and bitfield
|
||||
t = associated_torrent().lock();
|
||||
assert(t);
|
||||
|
||||
if (!is_local())
|
||||
{
|
||||
write_handshake();
|
||||
}
|
||||
if (t->valid_metadata())
|
||||
write_bitfield(t->pieces());
|
||||
|
||||
assert(t->get_policy().has_connection(this));
|
||||
|
||||
|
@ -2130,6 +2145,8 @@ namespace libtorrent
|
|||
|
||||
m_state = read_packet_size;
|
||||
reset_recv_buffer(4);
|
||||
if (t->valid_metadata())
|
||||
write_bitfield(t->pieces());
|
||||
|
||||
assert(!packet_finished());
|
||||
return;
|
||||
|
@ -2239,8 +2256,10 @@ namespace libtorrent
|
|||
void bt_peer_connection::check_invariant() const
|
||||
{
|
||||
#ifndef TORRENT_DISABLE_ENCRYPTION
|
||||
assert(bool(m_state == read_pe_dhkey) == bool(m_DH_key_exchange)
|
||||
assert( (bool(m_state != read_pe_dhkey) || m_DH_key_exchange.get())
|
||||
|| !is_local());
|
||||
|
||||
assert(!m_rc4_encrypted || m_RC4_handler.get());
|
||||
#endif
|
||||
if (!m_in_constructor)
|
||||
peer_connection::check_invariant();
|
||||
|
@ -2258,4 +2277,3 @@ namespace libtorrent
|
|||
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -278,6 +278,12 @@ namespace libtorrent
|
|||
}
|
||||
}
|
||||
|
||||
void entry::swap(entry& e)
|
||||
{
|
||||
// not implemented
|
||||
assert(false);
|
||||
}
|
||||
|
||||
void entry::print(std::ostream& os, int indent) const
|
||||
{
|
||||
assert(indent >= 0);
|
||||
|
|
|
@ -82,8 +82,6 @@ BOOST_STATIC_ASSERT(sizeof(lseek(0, 0, 0)) >= 8);
|
|||
#endif
|
||||
|
||||
|
||||
namespace fs = boost::filesystem;
|
||||
|
||||
namespace
|
||||
{
|
||||
enum { mode_in = 1, mode_out = 2 };
|
||||
|
@ -130,6 +128,8 @@ namespace
|
|||
namespace libtorrent
|
||||
{
|
||||
|
||||
namespace fs = boost::filesystem;
|
||||
|
||||
const file::open_mode file::in(mode_in);
|
||||
const file::open_mode file::out(mode_out);
|
||||
|
||||
|
@ -248,15 +248,15 @@ namespace libtorrent
|
|||
void set_size(size_type s)
|
||||
{
|
||||
size_type pos = tell();
|
||||
seek(1, 0);
|
||||
seek(s - 1);
|
||||
char dummy = 0;
|
||||
read(&dummy, 1);
|
||||
seek(1, 0);
|
||||
seek(s - 1);
|
||||
write(&dummy, 1);
|
||||
seek(pos, 1);
|
||||
seek(pos);
|
||||
}
|
||||
|
||||
size_type seek(size_type offset, int m)
|
||||
size_type seek(size_type offset, int m = 1)
|
||||
{
|
||||
assert(m_open_mode);
|
||||
assert(m_fd != -1);
|
||||
|
@ -303,13 +303,13 @@ namespace libtorrent
|
|||
|
||||
file::file() : m_impl(new impl()) {}
|
||||
|
||||
file::file(boost::filesystem::path const& p, file::open_mode m)
|
||||
file::file(fs::path const& p, file::open_mode m)
|
||||
: m_impl(new impl(p, m.m_mask))
|
||||
{}
|
||||
|
||||
file::~file() {}
|
||||
|
||||
void file::open(boost::filesystem::path const& p, file::open_mode m)
|
||||
void file::open(fs::path const& p, file::open_mode m)
|
||||
{
|
||||
m_impl->open(p, m.m_mask);
|
||||
}
|
||||
|
|
|
@ -237,7 +237,7 @@ void http_connection::on_read(asio::error_code const& e
|
|||
data = m_parser.get_body().begin;
|
||||
size = m_parser.get_body().left();
|
||||
}
|
||||
m_handler(asio::error_code(), m_parser, data, size);
|
||||
m_handler(e, m_parser, data, size);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -411,6 +411,12 @@ namespace libtorrent
|
|||
std::min(req.num_want, 999));
|
||||
m_send_buffer += '&';
|
||||
}
|
||||
if (m_settings.announce_ip != address() && !url_has_argument(request, "ip"))
|
||||
{
|
||||
m_send_buffer += "ip=";
|
||||
m_send_buffer += m_settings.announce_ip.to_string();
|
||||
m_send_buffer += '&';
|
||||
}
|
||||
|
||||
#ifndef TORRENT_DISABLE_ENCRYPTION
|
||||
m_send_buffer += "supportcrypto=1&";
|
||||
|
|
|
@ -197,7 +197,7 @@ namespace
|
|||
, {"XT", "XanTorrent"}
|
||||
, {"XX", "Xtorrent"}
|
||||
, {"ZT", "ZipTorrent"}
|
||||
, {"lt", "libTorrent (libtorrent.rakshasa.no/}"}
|
||||
, {"lt", "rTorrent"}
|
||||
, {"pX", "pHoeniX"}
|
||||
, {"qB", "qBittorrent"}
|
||||
};
|
||||
|
|
|
@ -67,6 +67,12 @@ namespace libtorrent
|
|||
if (ps.type == proxy_settings::socks5_pw)
|
||||
s->get<socks5_stream>().set_username(ps.username, ps.password);
|
||||
}
|
||||
else if (ps.type == proxy_settings::socks4)
|
||||
{
|
||||
s->instantiate<socks4_stream>();
|
||||
s->get<socks4_stream>().set_proxy(ps.hostname, ps.port);
|
||||
s->get<socks4_stream>().set_username(ps.username);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw std::runtime_error("unsupported proxy type");
|
||||
|
|
|
@ -69,6 +69,15 @@ namespace libtorrent
|
|||
, m_filter6.export_filter());
|
||||
}
|
||||
|
||||
void port_filter::add_rule(boost::uint16_t first, boost::uint16_t last, int flags)
|
||||
{
|
||||
m_filter.add_rule(first, last, flags);
|
||||
}
|
||||
|
||||
int port_filter::access(boost::uint16_t port) const
|
||||
{
|
||||
return m_filter.access(port);
|
||||
}
|
||||
/*
|
||||
void ip_filter::print() const
|
||||
{
|
||||
|
|
|
@ -53,17 +53,20 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
#include "libtorrent/peer_request.hpp"
|
||||
#include "libtorrent/peer_connection.hpp"
|
||||
|
||||
namespace libtorrent { namespace
|
||||
namespace libtorrent {
|
||||
|
||||
namespace fs = boost::filesystem;
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
struct logger_peer_plugin : peer_plugin
|
||||
{
|
||||
logger_peer_plugin(std::string const& filename)
|
||||
{
|
||||
using namespace boost::filesystem;
|
||||
path dir(complete("libtorrent_ext_logs"));
|
||||
if (!exists(dir)) create_directories(dir);
|
||||
m_file.open(dir / filename, std::ios_base::out | std::ios_base::out);
|
||||
fs::path dir(fs::complete("libtorrent_ext_logs"));
|
||||
if (!fs::exists(dir)) fs::create_directories(dir);
|
||||
m_file.open((dir / filename).string().c_str(), std::ios_base::out | std::ios_base::out);
|
||||
m_file << "\n\n\n";
|
||||
log_timestamp();
|
||||
m_file << "*** starting log ***\n";
|
||||
|
@ -201,7 +204,7 @@ namespace libtorrent { namespace
|
|||
}
|
||||
|
||||
private:
|
||||
boost::filesystem::ofstream m_file;
|
||||
std::ofstream m_file;
|
||||
};
|
||||
|
||||
struct logger_plugin : torrent_plugin
|
||||
|
|
|
@ -38,8 +38,6 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <boost/filesystem/fstream.hpp>
|
||||
#include <boost/filesystem/convenience.hpp>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
|
@ -56,6 +54,7 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
#include "libtorrent/torrent.hpp"
|
||||
#include "libtorrent/extensions.hpp"
|
||||
#include "libtorrent/extensions/metadata_transfer.hpp"
|
||||
#include "libtorrent/alert_types.hpp"
|
||||
|
||||
namespace libtorrent { namespace
|
||||
{
|
||||
|
@ -176,14 +175,13 @@ namespace libtorrent { namespace
|
|||
, false);
|
||||
m_metadata_progress = 0;
|
||||
m_metadata_size = 0;
|
||||
// TODO: allow plugins to post alerts
|
||||
/*
|
||||
if (m_ses.m_alerts.should_post(alert::info))
|
||||
|
||||
if (m_torrent.alerts().should_post(alert::info))
|
||||
{
|
||||
m_ses.m_alerts.post_alert(metadata_failed_alert(
|
||||
get_handle(), "invalid metadata received from swarm"));
|
||||
m_torrent.alerts().post_alert(metadata_failed_alert(
|
||||
m_torrent.get_handle(), "invalid metadata received from swarm"));
|
||||
}
|
||||
*/
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -59,21 +59,6 @@ using libtorrent::aux::session_impl;
|
|||
namespace libtorrent
|
||||
{
|
||||
|
||||
void intrusive_ptr_add_ref(peer_connection const* c)
|
||||
{
|
||||
assert(c->m_refs >= 0);
|
||||
assert(c != 0);
|
||||
++c->m_refs;
|
||||
}
|
||||
|
||||
void intrusive_ptr_release(peer_connection const* c)
|
||||
{
|
||||
assert(c->m_refs > 0);
|
||||
assert(c != 0);
|
||||
if (--c->m_refs == 0)
|
||||
delete c;
|
||||
}
|
||||
|
||||
// outbound connection
|
||||
peer_connection::peer_connection(
|
||||
session_impl& ses
|
||||
|
@ -94,6 +79,7 @@ namespace libtorrent
|
|||
, m_packet_size(0)
|
||||
, m_recv_pos(0)
|
||||
, m_current_send_buffer(0)
|
||||
, m_reading_bytes(0)
|
||||
, m_write_pos(0)
|
||||
, m_last_receive(time_now())
|
||||
, m_last_sent(time_now())
|
||||
|
@ -122,12 +108,14 @@ namespace libtorrent
|
|||
, m_prefer_whole_pieces(false)
|
||||
, m_request_large_blocks(false)
|
||||
, m_non_prioritized(false)
|
||||
, m_refs(0)
|
||||
, m_upload_limit(resource_request::inf)
|
||||
, m_download_limit(resource_request::inf)
|
||||
, m_peer_info(peerinfo)
|
||||
, m_speed(slow)
|
||||
, m_connection_ticket(-1)
|
||||
, m_remote_bytes_dled(0)
|
||||
, m_remote_dl_rate(0)
|
||||
, m_remote_dl_update(time_now())
|
||||
#ifndef NDEBUG
|
||||
, m_in_constructor(true)
|
||||
#endif
|
||||
|
@ -167,6 +155,7 @@ namespace libtorrent
|
|||
, m_packet_size(0)
|
||||
, m_recv_pos(0)
|
||||
, m_current_send_buffer(0)
|
||||
, m_reading_bytes(0)
|
||||
, m_write_pos(0)
|
||||
, m_last_receive(time_now())
|
||||
, m_last_sent(time_now())
|
||||
|
@ -193,11 +182,13 @@ namespace libtorrent
|
|||
, m_prefer_whole_pieces(false)
|
||||
, m_request_large_blocks(false)
|
||||
, m_non_prioritized(false)
|
||||
, m_refs(0)
|
||||
, m_upload_limit(resource_request::inf)
|
||||
, m_download_limit(resource_request::inf)
|
||||
, m_peer_info(peerinfo)
|
||||
, m_speed(slow)
|
||||
, m_remote_bytes_dled(0)
|
||||
, m_remote_dl_rate(0)
|
||||
, m_remote_dl_update(time_now())
|
||||
#ifndef NDEBUG
|
||||
, m_in_constructor(true)
|
||||
#endif
|
||||
|
@ -742,6 +733,16 @@ namespace libtorrent
|
|||
&& !is_interesting()
|
||||
&& t->picker().piece_priority(index) != 0)
|
||||
t->get_policy().peer_is_interesting(*this);
|
||||
|
||||
// this will disregard all have messages we get within
|
||||
// the first two seconds. Since some clients implements
|
||||
// lazy bitfields, these will not be reliable to use
|
||||
// for an estimated peer download rate.
|
||||
if (!peer_info_struct() || time_now() - peer_info_struct()->connected > seconds(2))
|
||||
{
|
||||
// update bytes downloaded since last timer
|
||||
m_remote_bytes_dled += t->torrent_file().piece_size(index);
|
||||
}
|
||||
}
|
||||
|
||||
if (is_seed())
|
||||
|
@ -1012,7 +1013,7 @@ namespace libtorrent
|
|||
for (std::vector<piece_picker::downloading_piece>::const_iterator i =
|
||||
dl_queue.begin(); i != dl_queue.end(); ++i)
|
||||
{
|
||||
assert(i->finished < blocks_per_piece);
|
||||
assert(i->finished <= blocks_per_piece);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1076,7 +1077,6 @@ namespace libtorrent
|
|||
|
||||
piece_picker& picker = t->picker();
|
||||
piece_manager& fs = t->filesystem();
|
||||
policy& pol = t->get_policy();
|
||||
|
||||
std::vector<piece_block> finished_blocks;
|
||||
piece_block block_finished(p.piece, p.start / t->block_size());
|
||||
|
@ -1125,13 +1125,12 @@ namespace libtorrent
|
|||
}
|
||||
else
|
||||
{
|
||||
// cancel the block from the
|
||||
/* // cancel the block from the
|
||||
// peer that has taken over it.
|
||||
boost::optional<tcp::endpoint> peer
|
||||
= t->picker().get_downloader(block_finished);
|
||||
if (peer)
|
||||
if (peer && t->picker().is_requested(block_finished))
|
||||
{
|
||||
assert(!t->picker().is_finished(block_finished));
|
||||
peer_connection* pc = t->connection_for(*peer);
|
||||
if (pc && pc != this)
|
||||
{
|
||||
|
@ -1140,7 +1139,7 @@ namespace libtorrent
|
|||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
*/ {
|
||||
if (t->alerts().should_post(alert::debug))
|
||||
{
|
||||
t->alerts().post_alert(
|
||||
|
@ -1153,15 +1152,28 @@ namespace libtorrent
|
|||
(*m_logger) << " *** The block we just got was not in the "
|
||||
"request queue ***\n";
|
||||
#endif
|
||||
t->received_redundant_data(p.length);
|
||||
if (!has_peer_choked())
|
||||
{
|
||||
request_a_block(*t, *this);
|
||||
send_block_requests();
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
assert(picker.is_requested(block_finished));
|
||||
|
||||
// if the block we got is already finished, then ignore it
|
||||
if (picker.is_finished(block_finished))
|
||||
if (picker.is_downloaded(block_finished))
|
||||
{
|
||||
t->received_redundant_data(t->block_size());
|
||||
pol.block_finished(*this, block_finished);
|
||||
t->received_redundant_data(p.length);
|
||||
|
||||
if (!has_peer_choked())
|
||||
{
|
||||
request_a_block(*t, *this);
|
||||
send_block_requests();
|
||||
}
|
||||
|
||||
if (request_peer && !request_peer->has_peer_choked() && !t->is_seed())
|
||||
{
|
||||
|
@ -1171,16 +1183,9 @@ namespace libtorrent
|
|||
return;
|
||||
}
|
||||
|
||||
fs.write(data, p.piece, p.start, p.length);
|
||||
|
||||
picker.mark_as_finished(block_finished, m_remote);
|
||||
|
||||
try
|
||||
{
|
||||
pol.block_finished(*this, block_finished);
|
||||
send_block_requests();
|
||||
}
|
||||
catch (std::exception const&) {}
|
||||
fs.async_write(p, data, bind(&peer_connection::on_disk_write_complete
|
||||
, self(), _1, _2, p, t));
|
||||
picker.mark_as_writing(block_finished, m_remote);
|
||||
|
||||
if (request_peer && !request_peer->has_peer_choked() && !t->is_seed())
|
||||
{
|
||||
|
@ -1188,90 +1193,63 @@ namespace libtorrent
|
|||
request_peer->send_block_requests();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void peer_connection::on_disk_write_complete(int ret, disk_io_job const& j
|
||||
, peer_request p, boost::shared_ptr<torrent> t)
|
||||
{
|
||||
session_impl::mutex_t::scoped_lock l(m_ses.m_mutex);
|
||||
|
||||
if (ret == -1 || !t)
|
||||
{
|
||||
if (!t)
|
||||
{
|
||||
m_ses.connection_failed(m_socket, remote(), j.str.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
if (t->alerts().should_post(alert::fatal))
|
||||
{
|
||||
std::string err = "torrent paused: disk write error, " + j.str;
|
||||
t->alerts().post_alert(file_error_alert(t->get_handle(), err));
|
||||
}
|
||||
t->pause();
|
||||
return;
|
||||
}
|
||||
|
||||
if (t->is_seed()) return;
|
||||
|
||||
piece_picker& picker = t->picker();
|
||||
|
||||
assert(p.piece == j.piece);
|
||||
assert(p.start == j.offset);
|
||||
piece_block block_finished(p.piece, p.start / t->block_size());
|
||||
picker.mark_as_finished(block_finished, m_remote);
|
||||
|
||||
if (!has_peer_choked() && !t->is_seed() && !m_torrent.expired())
|
||||
{
|
||||
// this is a free function defined in policy.cpp
|
||||
request_a_block(*t, *this);
|
||||
try
|
||||
{
|
||||
send_block_requests();
|
||||
}
|
||||
catch (std::exception const&) {}
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
try
|
||||
{
|
||||
#endif
|
||||
|
||||
bool was_seed = t->is_seed();
|
||||
bool was_finished = picker.num_filtered() + t->num_pieces()
|
||||
== t->torrent_file().num_pieces();
|
||||
|
||||
// did we just finish the piece?
|
||||
if (picker.is_piece_finished(p.piece))
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
check_postcondition post_checker2_(t, false);
|
||||
#endif
|
||||
bool verified = t->verify_piece(p.piece);
|
||||
if (verified)
|
||||
{
|
||||
// the following call may cause picker to become invalid
|
||||
// in case we just became a seed
|
||||
t->announce_piece(p.piece);
|
||||
assert(t->valid_metadata());
|
||||
// if we just became a seed, picker is now invalid, since it
|
||||
// is deallocated by the torrent once it starts seeding
|
||||
if (!was_finished
|
||||
&& (t->is_seed()
|
||||
|| picker.num_filtered() + t->num_pieces()
|
||||
== t->torrent_file().num_pieces()))
|
||||
{
|
||||
// torrent finished
|
||||
// i.e. all the pieces we're interested in have
|
||||
// been downloaded. Release the files (they will open
|
||||
// in read only mode if needed)
|
||||
try { t->finished(); }
|
||||
catch (std::exception& e)
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
std::cerr << e.what() << std::endl;
|
||||
assert(false);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
t->piece_failed(p.piece);
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
try
|
||||
{
|
||||
#endif
|
||||
|
||||
pol.piece_finished(p.piece, verified);
|
||||
|
||||
#ifndef NDEBUG
|
||||
}
|
||||
catch (std::exception const& e)
|
||||
{
|
||||
std::cerr << e.what() << std::endl;
|
||||
assert(false);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef NDEBUG
|
||||
try
|
||||
{
|
||||
#endif
|
||||
|
||||
if (!was_seed && t->is_seed())
|
||||
{
|
||||
assert(verified);
|
||||
t->completed();
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
}
|
||||
catch (std::exception const& e)
|
||||
{
|
||||
std::cerr << e.what() << std::endl;
|
||||
assert(false);
|
||||
}
|
||||
#endif
|
||||
|
||||
t->async_verify_piece(p.piece, bind(&torrent::piece_finished, t
|
||||
, p.piece, _1));
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
|
@ -1350,7 +1328,7 @@ namespace libtorrent
|
|||
assert(block.piece_index < t->torrent_file().num_pieces());
|
||||
assert(block.block_index >= 0);
|
||||
assert(block.block_index < t->torrent_file().piece_size(block.piece_index));
|
||||
assert(!t->picker().is_downloading(block));
|
||||
assert(!t->picker().is_requested(block));
|
||||
|
||||
piece_picker::piece_state_t state;
|
||||
peer_speed_t speed = peer_speed();
|
||||
|
@ -1376,7 +1354,7 @@ namespace libtorrent
|
|||
assert(block.piece_index < t->torrent_file().num_pieces());
|
||||
assert(block.block_index >= 0);
|
||||
assert(block.block_index < t->torrent_file().piece_size(block.piece_index));
|
||||
assert(t->picker().is_downloading(block));
|
||||
assert(t->picker().is_requested(block));
|
||||
|
||||
t->picker().abort_download(block);
|
||||
|
||||
|
@ -1603,6 +1581,8 @@ namespace libtorrent
|
|||
|
||||
void peer_connection::disconnect()
|
||||
{
|
||||
session_impl::mutex_t::scoped_lock l(m_ses.m_mutex);
|
||||
|
||||
boost::intrusive_ptr<peer_connection> me(this);
|
||||
|
||||
INVARIANT_CHECK;
|
||||
|
@ -1753,12 +1733,14 @@ namespace libtorrent
|
|||
p.failcount = peer_info_struct()->failcount;
|
||||
p.num_hashfails = peer_info_struct()->hashfails;
|
||||
p.flags |= peer_info_struct()->on_parole ? peer_info::on_parole : 0;
|
||||
p.remote_dl_rate = m_remote_dl_rate;
|
||||
}
|
||||
else
|
||||
{
|
||||
p.source = 0;
|
||||
p.failcount = 0;
|
||||
p.num_hashfails = 0;
|
||||
p.remote_dl_rate = 0;
|
||||
}
|
||||
|
||||
p.send_buffer_size = send_buffer_size();
|
||||
|
@ -1868,10 +1850,13 @@ namespace libtorrent
|
|||
|
||||
m_assume_fifo = true;
|
||||
|
||||
if (!has_peer_choked())
|
||||
{
|
||||
request_a_block(*t, *this);
|
||||
send_block_requests();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_statistics.second_tick(tick_interval);
|
||||
|
||||
|
@ -1915,6 +1900,21 @@ namespace libtorrent
|
|||
, m_upload_limit));
|
||||
}
|
||||
|
||||
// update once every minute
|
||||
if (now - m_remote_dl_update >= seconds(60))
|
||||
{
|
||||
float factor = 0.6666666666667f;
|
||||
|
||||
if (m_remote_dl_rate == 0) factor = 0.0f;
|
||||
|
||||
m_remote_dl_rate =
|
||||
(m_remote_dl_rate * factor) +
|
||||
((m_remote_bytes_dled * (1.0f-factor)) / 60.f);
|
||||
|
||||
m_remote_bytes_dled = 0;
|
||||
m_remote_dl_update = now;
|
||||
}
|
||||
|
||||
fill_send_buffer();
|
||||
/*
|
||||
size_type diff = share_diff();
|
||||
|
@ -1970,7 +1970,7 @@ namespace libtorrent
|
|||
else if (buffer_size_watermark > 80 * 1024) buffer_size_watermark = 80 * 1024;
|
||||
|
||||
while (!m_requests.empty()
|
||||
&& (send_buffer_size() < buffer_size_watermark)
|
||||
&& (send_buffer_size() + m_reading_bytes < buffer_size_watermark)
|
||||
&& !m_choked)
|
||||
{
|
||||
assert(t->valid_metadata());
|
||||
|
@ -1982,16 +1982,12 @@ namespace libtorrent
|
|||
assert(r.start + r.length <= t->torrent_file().piece_size(r.piece));
|
||||
assert(r.length > 0 && r.start >= 0);
|
||||
|
||||
write_piece(r);
|
||||
|
||||
#ifdef TORRENT_VERBOSE_LOGGING
|
||||
(*m_logger) << time_now_string()
|
||||
<< " ==> PIECE [ piece: " << r.piece << " | s: " << r.start
|
||||
<< " | l: " << r.length << " ]\n";
|
||||
#endif
|
||||
t->filesystem().async_read(r, bind(&peer_connection::on_disk_read_complete
|
||||
, self(), _1, _2, r));
|
||||
m_reading_bytes += r.length;
|
||||
|
||||
m_requests.erase(m_requests.begin());
|
||||
|
||||
/*
|
||||
if (m_requests.empty()
|
||||
&& m_num_invalid_requests > 0
|
||||
&& is_peer_interested()
|
||||
|
@ -2004,9 +2000,49 @@ namespace libtorrent
|
|||
send_choke();
|
||||
send_unchoke();
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
void peer_connection::on_disk_read_complete(int ret, disk_io_job const& j, peer_request r)
|
||||
{
|
||||
session_impl::mutex_t::scoped_lock l(m_ses.m_mutex);
|
||||
|
||||
m_reading_bytes -= r.length;
|
||||
|
||||
if (ret != r.length || m_torrent.expired())
|
||||
{
|
||||
boost::shared_ptr<torrent> t = m_torrent.lock();
|
||||
if (!t)
|
||||
{
|
||||
m_ses.connection_failed(m_socket, remote(), j.str.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
if (t->alerts().should_post(alert::fatal))
|
||||
{
|
||||
std::string err = "torrent paused: disk read error";
|
||||
if (!j.str.empty())
|
||||
{
|
||||
err += ", ";
|
||||
err += j.str;
|
||||
}
|
||||
t->alerts().post_alert(file_error_alert(t->get_handle(), err));
|
||||
}
|
||||
t->pause();
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef TORRENT_VERBOSE_LOGGING
|
||||
(*m_logger) << time_now_string()
|
||||
<< " ==> PIECE [ piece: " << r.piece << " | s: " << r.start
|
||||
<< " | l: " << r.length << " ]\n";
|
||||
#endif
|
||||
|
||||
write_piece(r, j.buffer);
|
||||
setup_send();
|
||||
}
|
||||
|
||||
void peer_connection::assign_bandwidth(int channel, int amount)
|
||||
{
|
||||
session_impl::mutex_t::scoped_lock l(m_ses.m_mutex);
|
||||
|
@ -2599,11 +2635,13 @@ namespace libtorrent
|
|||
time_duration d2;
|
||||
d1 = now - m_became_uninterested;
|
||||
d2 = now - m_became_uninteresting;
|
||||
// TODO: these timeouts should be user settable
|
||||
time_duration time_limit = seconds(
|
||||
m_ses.settings().inactivity_timeout);
|
||||
|
||||
if (!m_interesting
|
||||
&& !m_peer_interested
|
||||
&& d1 > minutes(10)
|
||||
&& d2 > minutes(10))
|
||||
&& d1 > time_limit
|
||||
&& d2 > time_limit)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
@ -2663,3 +2701,4 @@ namespace libtorrent
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -88,8 +88,9 @@ namespace libtorrent
|
|||
|
||||
// pieces is a bitmask with the pieces we have
|
||||
void piece_picker::files_checked(
|
||||
const std::vector<bool>& pieces
|
||||
, const std::vector<downloading_piece>& unfinished)
|
||||
std::vector<bool> const& pieces
|
||||
, std::vector<downloading_piece> const& unfinished
|
||||
, std::vector<int>& verify_pieces)
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
m_files_checked_called = true;
|
||||
|
@ -118,14 +119,12 @@ namespace libtorrent
|
|||
tcp::endpoint peer;
|
||||
for (int j = 0; j < m_blocks_per_piece; ++j)
|
||||
{
|
||||
if (i->info[j].finished)
|
||||
if (i->info[j].state == block_info::state_finished)
|
||||
mark_as_finished(piece_block(i->index, j), peer);
|
||||
}
|
||||
if (is_piece_finished(i->index))
|
||||
{
|
||||
// TODO: handle this case by verifying the
|
||||
// piece and either accept it or discard it
|
||||
assert(false);
|
||||
verify_pieces.push_back(i->index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -212,8 +211,7 @@ namespace libtorrent
|
|||
for (int i = 0; i < m_blocks_per_piece; ++i)
|
||||
{
|
||||
ret.info[i].num_downloads = 0;
|
||||
ret.info[i].requested = 0;
|
||||
ret.info[i].finished = 0;
|
||||
ret.info[i].state = block_info::state_none;
|
||||
ret.info[i].peer = tcp::endpoint();
|
||||
}
|
||||
return ret;
|
||||
|
@ -258,25 +256,28 @@ namespace libtorrent
|
|||
int num_blocks = blocks_in_piece(i->index);
|
||||
int num_requested = 0;
|
||||
int num_finished = 0;
|
||||
int num_writing = 0;
|
||||
for (int k = 0; k < num_blocks; ++k)
|
||||
{
|
||||
if (i->info[k].finished)
|
||||
if (i->info[k].state == block_info::state_finished)
|
||||
{
|
||||
++num_finished;
|
||||
assert(i->info[k].requested);
|
||||
++num_requested;
|
||||
continue;
|
||||
}
|
||||
if (i->info[k].requested)
|
||||
if (i->info[k].state == block_info::state_requested)
|
||||
{
|
||||
++num_requested;
|
||||
blocks_requested = true;
|
||||
}
|
||||
if (i->info[k].state == block_info::state_writing)
|
||||
{
|
||||
++num_writing;
|
||||
}
|
||||
}
|
||||
assert(blocks_requested == (i->state != none));
|
||||
assert(num_requested == i->requested);
|
||||
assert(num_writing == i->writing);
|
||||
assert(num_finished == i->finished);
|
||||
assert(num_finished <= num_requested);
|
||||
}
|
||||
|
||||
|
||||
|
@ -1070,8 +1071,7 @@ namespace libtorrent
|
|||
for (int j = 0; j < num_blocks_in_piece; ++j)
|
||||
{
|
||||
piece_picker::block_info const& info = p.info[j];
|
||||
if ((info.finished == 1
|
||||
|| info.requested == 1)
|
||||
if (info.state != piece_picker::block_info::state_none
|
||||
&& info.peer != peer
|
||||
&& info.peer != tcp::endpoint())
|
||||
{
|
||||
|
@ -1089,6 +1089,9 @@ namespace libtorrent
|
|||
, int num_blocks, bool prefer_whole_pieces
|
||||
, tcp::endpoint peer, piece_state_t speed) const
|
||||
{
|
||||
// if we have less than 1% of the pieces, ignore speed priorities and just try
|
||||
// to finish any downloading piece
|
||||
bool ignore_speed_categories = (m_num_have * 100 / m_piece_map.size()) < 1;
|
||||
for (std::vector<int>::const_iterator i = piece_list.begin();
|
||||
i != piece_list.end(); ++i)
|
||||
{
|
||||
|
@ -1099,10 +1102,6 @@ namespace libtorrent
|
|||
// skip it
|
||||
if (!pieces[*i]) continue;
|
||||
|
||||
// if we have less than 1% of the pieces, ignore speed priorities and just try
|
||||
// to finish any downloading piece
|
||||
bool ignore_speed_categories = (m_num_have * 100 / m_piece_map.size()) < 1;
|
||||
|
||||
int num_blocks_in_piece = blocks_in_piece(*i);
|
||||
|
||||
if (m_piece_map[*i].downloading == 1)
|
||||
|
@ -1130,8 +1129,10 @@ namespace libtorrent
|
|||
for (int j = 0; j < num_blocks_in_piece; ++j)
|
||||
{
|
||||
block_info const& info = p->info[j];
|
||||
if (info.finished) continue;
|
||||
if (info.requested
|
||||
if (info.state == block_info::state_finished
|
||||
|| info.state == block_info::state_writing)
|
||||
continue;
|
||||
if (info.state == block_info::state_requested
|
||||
&& info.peer == peer) continue;
|
||||
backup_blocks.push_back(piece_block(*i, j));
|
||||
}
|
||||
|
@ -1142,9 +1143,13 @@ namespace libtorrent
|
|||
{
|
||||
// ignore completed blocks
|
||||
block_info const& info = p->info[j];
|
||||
if (info.finished) continue;
|
||||
if (info.state == block_info::state_finished
|
||||
|| info.state == block_info::state_writing)
|
||||
continue;
|
||||
// ignore blocks requested from this peer already
|
||||
if (info.requested && info.peer == peer) continue;
|
||||
if (info.state == block_info::state_requested
|
||||
&& info.peer == peer)
|
||||
continue;
|
||||
// if the piece is fast and the peer is slow, or vice versa,
|
||||
// add the block as a backup.
|
||||
// override this behavior if all the other blocks
|
||||
|
@ -1169,7 +1174,7 @@ namespace libtorrent
|
|||
// blocks that have not been requested from any
|
||||
// other peer.
|
||||
interesting_blocks.push_back(piece_block(*i, j));
|
||||
if (p->info[j].requested == 0)
|
||||
if (p->info[j].state == block_info::state_none)
|
||||
{
|
||||
// we have found a block that's free to download
|
||||
num_blocks--;
|
||||
|
@ -1217,11 +1222,18 @@ namespace libtorrent
|
|||
int max_blocks = blocks_in_piece(index);
|
||||
if ((int)i->finished < max_blocks) return false;
|
||||
|
||||
assert((int)i->requested == max_blocks);
|
||||
#ifndef NDEBUG
|
||||
for (int k = 0; k < max_blocks; ++k)
|
||||
{
|
||||
assert(i->info[k].state == block_info::state_finished);
|
||||
}
|
||||
#endif
|
||||
|
||||
assert((int)i->finished == max_blocks);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool piece_picker::is_downloading(piece_block block) const
|
||||
bool piece_picker::is_requested(piece_block block) const
|
||||
{
|
||||
assert(block.piece_index >= 0);
|
||||
assert(block.block_index >= 0);
|
||||
|
@ -1235,7 +1247,22 @@ namespace libtorrent
|
|||
, has_index(block.piece_index));
|
||||
|
||||
assert(i != m_downloads.end());
|
||||
return i->info[block.block_index].requested;
|
||||
return i->info[block.block_index].state == block_info::state_requested;
|
||||
}
|
||||
|
||||
bool piece_picker::is_downloaded(piece_block block) const
|
||||
{
|
||||
assert(block.piece_index >= 0);
|
||||
assert(block.block_index >= 0);
|
||||
assert(block.piece_index < (int)m_piece_map.size());
|
||||
|
||||
if (m_piece_map[block.piece_index].index == piece_pos::we_have_index) return true;
|
||||
if (m_piece_map[block.piece_index].downloading == 0) return false;
|
||||
std::vector<downloading_piece>::const_iterator i
|
||||
= std::find_if(m_downloads.begin(), m_downloads.end(), has_index(block.piece_index));
|
||||
assert(i != m_downloads.end());
|
||||
return i->info[block.block_index].state == block_info::state_finished
|
||||
|| i->info[block.block_index].state == block_info::state_writing;
|
||||
}
|
||||
|
||||
bool piece_picker::is_finished(piece_block block) const
|
||||
|
@ -1249,7 +1276,7 @@ namespace libtorrent
|
|||
std::vector<downloading_piece>::const_iterator i
|
||||
= std::find_if(m_downloads.begin(), m_downloads.end(), has_index(block.piece_index));
|
||||
assert(i != m_downloads.end());
|
||||
return i->info[block.block_index].finished;
|
||||
return i->info[block.block_index].state == block_info::state_finished;
|
||||
}
|
||||
|
||||
|
||||
|
@ -1274,7 +1301,7 @@ namespace libtorrent
|
|||
dp.state = state;
|
||||
dp.index = block.piece_index;
|
||||
block_info& info = dp.info[block.block_index];
|
||||
info.requested = 1;
|
||||
info.state = block_info::state_requested;
|
||||
info.peer = peer;
|
||||
++dp.requested;
|
||||
}
|
||||
|
@ -1284,15 +1311,26 @@ namespace libtorrent
|
|||
= std::find_if(m_downloads.begin(), m_downloads.end(), has_index(block.piece_index));
|
||||
assert(i != m_downloads.end());
|
||||
block_info& info = i->info[block.block_index];
|
||||
assert(info.requested == 0);
|
||||
assert(info.state == block_info::state_none);
|
||||
info.peer = peer;
|
||||
info.requested = 1;
|
||||
info.state = block_info::state_requested;
|
||||
++i->requested;
|
||||
if (i->state == none) i->state = state;
|
||||
}
|
||||
}
|
||||
|
||||
void piece_picker::mark_as_finished(piece_block block, const tcp::endpoint& peer)
|
||||
void piece_picker::get_availability(std::vector<int>& avail) const
|
||||
{
|
||||
TORRENT_PIECE_PICKER_INVARIANT_CHECK;
|
||||
|
||||
avail.resize(m_piece_map.size());
|
||||
std::vector<int>::iterator j = avail.begin();
|
||||
for (std::vector<piece_pos>::const_iterator i = m_piece_map.begin()
|
||||
, end(m_piece_map.end()); i != end; ++i, ++j)
|
||||
*j = i->peer_count;
|
||||
}
|
||||
|
||||
void piece_picker::mark_as_writing(piece_block block, tcp::endpoint const& peer)
|
||||
{
|
||||
TORRENT_PIECE_PICKER_INVARIANT_CHECK;
|
||||
|
||||
|
@ -1302,10 +1340,11 @@ namespace libtorrent
|
|||
assert(block.block_index < blocks_in_piece(block.piece_index));
|
||||
|
||||
piece_pos& p = m_piece_map[block.piece_index];
|
||||
int prio = p.priority(m_sequenced_download_threshold);
|
||||
assert(p.downloading);
|
||||
|
||||
if (p.downloading == 0)
|
||||
/* if (p.downloading == 0)
|
||||
{
|
||||
int prio = p.priority(m_sequenced_download_threshold);
|
||||
p.downloading = 1;
|
||||
if (prio > 0) move(prio, p.index);
|
||||
else assert(p.priority(m_sequenced_download_threshold) == 0);
|
||||
|
@ -1314,25 +1353,84 @@ namespace libtorrent
|
|||
dp.state = none;
|
||||
dp.index = block.piece_index;
|
||||
block_info& info = dp.info[block.block_index];
|
||||
info.requested = 1;
|
||||
info.finished = 1;
|
||||
++dp.requested;
|
||||
++dp.finished;
|
||||
dp.info[block.block_index].peer = peer;
|
||||
info.peer = peer;
|
||||
if (info.state == block_info::state_requested) --dp.requested;
|
||||
assert(dp.requested >= 0);
|
||||
assert (info.state != block_info::state_finished);
|
||||
assert (info.state != block_info::state_writing);
|
||||
if (info.state != block_info::state_requested) ++dp.writing;
|
||||
info.state = block_info::state_writing;
|
||||
}
|
||||
else
|
||||
*/ {
|
||||
std::vector<downloading_piece>::iterator i
|
||||
= std::find_if(m_downloads.begin(), m_downloads.end(), has_index(block.piece_index));
|
||||
assert(i != m_downloads.end());
|
||||
block_info& info = i->info[block.block_index];
|
||||
info.peer == peer;
|
||||
assert(info.state == block_info::state_requested);
|
||||
if (info.state == block_info::state_requested) --i->requested;
|
||||
assert(i->requested >= 0);
|
||||
assert (info.state != block_info::state_writing);
|
||||
++i->writing;
|
||||
info.state = block_info::state_writing;
|
||||
|
||||
if (i->requested == 0)
|
||||
{
|
||||
// there are no blocks requested in this piece.
|
||||
// remove the fast/slow state from it
|
||||
i->state = none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void piece_picker::mark_as_finished(piece_block block, tcp::endpoint const& peer)
|
||||
{
|
||||
assert(block.piece_index >= 0);
|
||||
assert(block.block_index >= 0);
|
||||
assert(block.piece_index < (int)m_piece_map.size());
|
||||
assert(block.block_index < blocks_in_piece(block.piece_index));
|
||||
|
||||
piece_pos& p = m_piece_map[block.piece_index];
|
||||
|
||||
if (p.downloading == 0)
|
||||
{
|
||||
TORRENT_PIECE_PICKER_INVARIANT_CHECK;
|
||||
|
||||
assert(peer == tcp::endpoint());
|
||||
int prio = p.priority(m_sequenced_download_threshold);
|
||||
p.downloading = 1;
|
||||
if (prio > 0) move(prio, p.index);
|
||||
else assert(p.priority(m_sequenced_download_threshold) == 0);
|
||||
|
||||
downloading_piece& dp = add_download_piece();
|
||||
dp.state = none;
|
||||
dp.index = block.piece_index;
|
||||
block_info& info = dp.info[block.block_index];
|
||||
info.peer = peer;
|
||||
assert(info.state == block_info::state_none);
|
||||
// if (info.state == block_info::state_writing) --dp.writing;
|
||||
// assert(dp.writing >= 0);
|
||||
if (info.state != block_info::state_finished) ++dp.finished;
|
||||
info.state = block_info::state_finished;
|
||||
}
|
||||
else
|
||||
{
|
||||
TORRENT_PIECE_PICKER_INVARIANT_CHECK;
|
||||
|
||||
std::vector<downloading_piece>::iterator i
|
||||
= std::find_if(m_downloads.begin(), m_downloads.end(), has_index(block.piece_index));
|
||||
assert(i != m_downloads.end());
|
||||
block_info& info = i->info[block.block_index];
|
||||
info.peer = peer;
|
||||
if (!info.requested) ++i->requested;
|
||||
info.requested = 1;
|
||||
if (!info.finished) ++i->finished;
|
||||
info.finished = 1;
|
||||
assert(info.state == block_info::state_writing
|
||||
|| peer == tcp::endpoint());
|
||||
if (info.state == block_info::state_writing) --i->writing;
|
||||
assert(i->writing >= 0);
|
||||
++i->finished;
|
||||
info.state = block_info::state_finished;
|
||||
|
||||
if (i->requested == i->finished)
|
||||
if (i->requested == 0)
|
||||
{
|
||||
// there are no blocks requested in this piece.
|
||||
// remove the fast/slow state from it
|
||||
|
@ -1367,8 +1465,7 @@ namespace libtorrent
|
|||
|
||||
assert(block.block_index >= 0);
|
||||
|
||||
if (i->info[block.block_index].requested == false
|
||||
|| i->info[block.block_index].requested == true)
|
||||
if (i->info[block.block_index].state == block_info::state_none)
|
||||
return boost::optional<tcp::endpoint>();
|
||||
|
||||
return boost::optional<tcp::endpoint>(i->info[block.block_index].peer);
|
||||
|
@ -1394,17 +1491,17 @@ namespace libtorrent
|
|||
, m_downloads.end(), has_index(block.piece_index));
|
||||
assert(i != m_downloads.end());
|
||||
|
||||
if (i->info[block.block_index].finished)
|
||||
if (i->info[block.block_index].state == block_info::state_finished
|
||||
|| i->info[block.block_index].state == block_info::state_writing)
|
||||
{
|
||||
assert(i->info[block.block_index].requested);
|
||||
return;
|
||||
}
|
||||
|
||||
assert(block.block_index < blocks_in_piece(block.piece_index));
|
||||
assert(i->info[block.block_index].requested);
|
||||
assert(i->info[block.block_index].state == block_info::state_requested);
|
||||
|
||||
// clear this block as being downloaded
|
||||
i->info[block.block_index].requested = false;
|
||||
i->info[block.block_index].state = block_info::state_none;
|
||||
--i->requested;
|
||||
|
||||
// clear the downloader of this block
|
||||
|
@ -1412,7 +1509,7 @@ namespace libtorrent
|
|||
|
||||
// if there are no other blocks in this piece
|
||||
// that's being downloaded, remove it from the list
|
||||
if (i->requested == 0)
|
||||
if (i->requested + i->finished + i->writing == 0)
|
||||
{
|
||||
erase_download_piece(i);
|
||||
piece_pos& p = m_piece_map[block.piece_index];
|
||||
|
@ -1423,7 +1520,7 @@ namespace libtorrent
|
|||
assert(std::find_if(m_downloads.begin(), m_downloads.end()
|
||||
, has_index(block.piece_index)) == m_downloads.end());
|
||||
}
|
||||
else if (i->requested == i->finished)
|
||||
else if (i->requested == 0)
|
||||
{
|
||||
// there are no blocks requested in this piece.
|
||||
// remove the fast/slow state from it
|
||||
|
|
|
@ -142,7 +142,19 @@ namespace
|
|||
bool operator()(policy::peer const& p) const
|
||||
{ return p.ip.address() == m_ip.address(); }
|
||||
|
||||
tcp::endpoint m_ip;
|
||||
tcp::endpoint const& m_ip;
|
||||
};
|
||||
|
||||
struct match_peer_id
|
||||
{
|
||||
match_peer_id(peer_id const& id_)
|
||||
: m_id(id_)
|
||||
{}
|
||||
|
||||
bool operator()(policy::peer const& p) const
|
||||
{ return p.connection && p.connection->pid() == m_id; }
|
||||
|
||||
peer_id const& m_id;
|
||||
};
|
||||
|
||||
struct match_peer_connection
|
||||
|
@ -152,9 +164,13 @@ namespace
|
|||
{}
|
||||
|
||||
bool operator()(policy::peer const& p) const
|
||||
{ return p.connection == &m_conn; }
|
||||
{
|
||||
return p.connection == &m_conn
|
||||
|| (p.ip == m_conn.remote()
|
||||
&& p.type == policy::peer::connectable);
|
||||
}
|
||||
|
||||
const peer_connection& m_conn;
|
||||
peer_connection const& m_conn;
|
||||
};
|
||||
|
||||
|
||||
|
@ -230,7 +246,7 @@ namespace libtorrent
|
|||
for (std::vector<piece_block>::iterator i = interesting_pieces.begin();
|
||||
i != interesting_pieces.end(); ++i)
|
||||
{
|
||||
if (p.is_downloading(*i))
|
||||
if (p.is_requested(*i))
|
||||
{
|
||||
busy_pieces.push_back(*i);
|
||||
continue;
|
||||
|
@ -524,6 +540,8 @@ namespace libtorrent
|
|||
int max_failcount = m_torrent->settings().max_failcount;
|
||||
int min_reconnect_time = m_torrent->settings().min_reconnect_time;
|
||||
|
||||
aux::session_impl& ses = m_torrent->session();
|
||||
|
||||
for (iterator i = m_peers.begin(); i != m_peers.end(); ++i)
|
||||
{
|
||||
if (i->connection) continue;
|
||||
|
@ -533,6 +551,8 @@ namespace libtorrent
|
|||
if (i->failcount >= max_failcount) continue;
|
||||
if (now - i->connected < seconds(i->failcount * min_reconnect_time))
|
||||
continue;
|
||||
if (ses.m_port_filter.access(i->ip.port()) & port_filter::blocked)
|
||||
continue;
|
||||
|
||||
assert(i->connected <= now);
|
||||
|
||||
|
@ -846,12 +866,13 @@ namespace libtorrent
|
|||
--m_num_unchoked;
|
||||
} while (m_num_unchoked > m_torrent->m_uploads_quota.given);
|
||||
}
|
||||
else
|
||||
// this should prevent the choke/unchoke
|
||||
// problem, since it will not unchoke unless
|
||||
// there actually are any choked peers
|
||||
else if (count_choked() > 0)
|
||||
{
|
||||
// optimistic unchoke. trade the 'worst'
|
||||
// unchoked peer with one of the choked
|
||||
// TODO: This rotation should happen
|
||||
// far less frequent than this!
|
||||
assert(m_num_unchoked <= m_torrent->num_peers());
|
||||
iterator p = find_unchoke_candidate();
|
||||
if (p != m_peers.end())
|
||||
|
@ -871,6 +892,22 @@ namespace libtorrent
|
|||
}
|
||||
}
|
||||
|
||||
int policy::count_choked() const
|
||||
{
|
||||
int ret = 0;
|
||||
for (const_iterator i = m_peers.begin();
|
||||
i != m_peers.end(); ++i)
|
||||
{
|
||||
if (!i->connection
|
||||
|| i->connection->is_connecting()
|
||||
|| i->connection->is_disconnecting()
|
||||
|| !i->connection->is_peer_interested())
|
||||
continue;
|
||||
if (i->connection->is_choked()) ++ret;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void policy::new_connection(peer_connection& c)
|
||||
{
|
||||
assert(!c.is_local());
|
||||
|
@ -902,7 +939,10 @@ namespace libtorrent
|
|||
|
||||
if (m_torrent->settings().allow_multiple_connections_per_ip)
|
||||
{
|
||||
i = m_peers.end();
|
||||
i = std::find_if(
|
||||
m_peers.begin()
|
||||
, m_peers.end()
|
||||
, match_peer_connection(c));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -968,13 +1008,29 @@ namespace libtorrent
|
|||
if(remote.address() == address() || remote.port() == 0)
|
||||
return;
|
||||
|
||||
aux::session_impl& ses = m_torrent->session();
|
||||
|
||||
port_filter const& pf = ses.m_port_filter;
|
||||
if (pf.access(remote.port()) & port_filter::blocked)
|
||||
{
|
||||
if (ses.m_alerts.should_post(alert::info))
|
||||
{
|
||||
ses.m_alerts.post_alert(peer_blocked_alert(remote.address()
|
||||
, "outgoing port blocked, peer not added to peer list"));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
iterator i;
|
||||
|
||||
if (m_torrent->settings().allow_multiple_connections_per_ip)
|
||||
{
|
||||
i = m_peers.end();
|
||||
i = std::find_if(
|
||||
m_peers.begin()
|
||||
, m_peers.end()
|
||||
, match_peer_id(pid));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -986,8 +1042,6 @@ namespace libtorrent
|
|||
|
||||
if (i == m_peers.end())
|
||||
{
|
||||
aux::session_impl& ses = m_torrent->session();
|
||||
|
||||
// if the IP is blocked, don't add it
|
||||
if (ses.m_ip_filter.access(remote.address()) & ip_filter::blocked)
|
||||
{
|
||||
|
@ -1095,18 +1149,6 @@ namespace libtorrent
|
|||
}
|
||||
}
|
||||
|
||||
// TODO: we must be able to get interested
|
||||
// in a peer again, if a piece fails that
|
||||
// this peer has.
|
||||
void policy::block_finished(peer_connection& c, piece_block)
|
||||
{
|
||||
INVARIANT_CHECK;
|
||||
|
||||
// if the peer hasn't choked us, ask for another piece
|
||||
if (!c.has_peer_choked() && !m_torrent->is_seed())
|
||||
request_a_block(*m_torrent, c);
|
||||
}
|
||||
|
||||
// this is called when we are unchoked by a peer
|
||||
// i.e. a peer lets us know that we will receive
|
||||
// data from now on
|
||||
|
@ -1167,6 +1209,7 @@ namespace libtorrent
|
|||
c.add_free_upload(-diff);
|
||||
}
|
||||
}
|
||||
/*
|
||||
if (!c.is_choked())
|
||||
{
|
||||
c.send_choke();
|
||||
|
@ -1175,6 +1218,7 @@ namespace libtorrent
|
|||
if (m_torrent->is_seed()) seed_unchoke_one_peer();
|
||||
else unchoke_one_peer();
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
bool policy::unchoke_one_peer()
|
||||
|
|
|
@ -143,11 +143,21 @@ namespace libtorrent
|
|||
m_impl->set_ip_filter(f);
|
||||
}
|
||||
|
||||
void session::set_port_filter(port_filter const& f)
|
||||
{
|
||||
m_impl->set_port_filter(f);
|
||||
}
|
||||
|
||||
void session::set_peer_id(peer_id const& id)
|
||||
{
|
||||
m_impl->set_peer_id(id);
|
||||
}
|
||||
|
||||
peer_id session::id() const
|
||||
{
|
||||
return m_impl->get_peer_id();
|
||||
}
|
||||
|
||||
void session::set_key(int key)
|
||||
{
|
||||
m_impl->set_key(key);
|
||||
|
@ -167,7 +177,7 @@ namespace libtorrent
|
|||
// if the torrent already exists, this will throw duplicate_torrent
|
||||
torrent_handle session::add_torrent(
|
||||
torrent_info const& ti
|
||||
, boost::filesystem::path const& save_path
|
||||
, fs::path const& save_path
|
||||
, entry const& resume_data
|
||||
, bool compact_mode
|
||||
, int block_size
|
||||
|
@ -181,7 +191,7 @@ namespace libtorrent
|
|||
char const* tracker_url
|
||||
, sha1_hash const& info_hash
|
||||
, char const* name
|
||||
, boost::filesystem::path const& save_path
|
||||
, fs::path const& save_path
|
||||
, entry const& e
|
||||
, bool compact_mode
|
||||
, int block_size
|
||||
|
@ -372,6 +382,36 @@ namespace libtorrent
|
|||
m_impl->set_severity_level(s);
|
||||
}
|
||||
|
||||
void session::start_lsd()
|
||||
{
|
||||
m_impl->start_lsd();
|
||||
}
|
||||
|
||||
void session::start_natpmp()
|
||||
{
|
||||
m_impl->start_natpmp();
|
||||
}
|
||||
|
||||
void session::start_upnp()
|
||||
{
|
||||
m_impl->start_upnp();
|
||||
}
|
||||
|
||||
void session::stop_lsd()
|
||||
{
|
||||
m_impl->stop_lsd();
|
||||
}
|
||||
|
||||
void session::stop_natpmp()
|
||||
{
|
||||
m_impl->stop_natpmp();
|
||||
}
|
||||
|
||||
void session::stop_upnp()
|
||||
{
|
||||
m_impl->stop_upnp();
|
||||
}
|
||||
|
||||
connection_queue& session::get_connection_queue()
|
||||
{
|
||||
return m_impl->m_half_open;
|
||||
|
|
|
@ -81,7 +81,11 @@ using boost::bind;
|
|||
using boost::mutex;
|
||||
using libtorrent::aux::session_impl;
|
||||
|
||||
namespace libtorrent { namespace detail
|
||||
namespace libtorrent {
|
||||
|
||||
namespace fs = boost::filesystem;
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
std::string generate_auth_string(std::string const& user
|
||||
|
@ -412,7 +416,6 @@ namespace libtorrent { namespace detail
|
|||
for (std::deque<boost::shared_ptr<piece_checker_data> >::iterator i
|
||||
= m_processing.begin(); i != m_processing.end(); ++i)
|
||||
{
|
||||
|
||||
if ((*i)->info_hash == info_hash) return i->get();
|
||||
}
|
||||
|
||||
|
@ -494,13 +497,6 @@ namespace libtorrent { namespace detail
|
|||
, m_dht_same_port(true)
|
||||
, m_external_udp_port(0)
|
||||
#endif
|
||||
, m_natpmp(m_io_service, m_listen_interface.address()
|
||||
, bind(&session_impl::on_port_mapping, this, _1, _2, _3))
|
||||
, m_upnp(m_io_service, m_half_open, m_listen_interface.address()
|
||||
, m_settings.user_agent
|
||||
, bind(&session_impl::on_port_mapping, this, _1, _2, _3))
|
||||
, m_lsd(m_io_service, m_listen_interface.address()
|
||||
, bind(&session_impl::on_lsd_peer, this, _1, _2))
|
||||
, m_timer(m_io_service)
|
||||
, m_next_connect_torrent(0)
|
||||
, m_checker_impl(*this)
|
||||
|
@ -589,6 +585,12 @@ namespace libtorrent { namespace detail
|
|||
m_checker_impl.m_abort = true;
|
||||
}
|
||||
|
||||
void session_impl::set_port_filter(port_filter const& f)
|
||||
{
|
||||
mutex_t::scoped_lock l(m_mutex);
|
||||
m_port_filter = f;
|
||||
}
|
||||
|
||||
void session_impl::set_ip_filter(ip_filter const& f)
|
||||
{
|
||||
mutex_t::scoped_lock l(m_mutex);
|
||||
|
@ -600,12 +602,8 @@ namespace libtorrent { namespace detail
|
|||
= m_connections.begin(); i != m_connections.end();)
|
||||
{
|
||||
tcp::endpoint sender;
|
||||
try {
|
||||
sender = i->first->remote_endpoint();
|
||||
} catch (asio::system_error& e) {
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
try { sender = i->first->remote_endpoint(); }
|
||||
catch (std::exception&) { sender = i->second->remote(); }
|
||||
if (m_ip_filter.access(sender.address()) & ip_filter::blocked)
|
||||
{
|
||||
#if defined(TORRENT_VERBOSE_LOGGING)
|
||||
|
@ -630,6 +628,9 @@ namespace libtorrent { namespace detail
|
|||
mutex_t::scoped_lock l(m_mutex);
|
||||
assert(s.connection_speed > 0);
|
||||
assert(s.file_pool_size > 0);
|
||||
|
||||
// less than 5 seconds unchoke interval is insane
|
||||
assert(s.unchoke_interval >= 5);
|
||||
m_settings = s;
|
||||
m_files.resize(m_settings.file_pool_size);
|
||||
// replace all occurances of '\n' with ' '.
|
||||
|
@ -1087,8 +1088,10 @@ namespace libtorrent { namespace detail
|
|||
{
|
||||
session_impl::mutex_t::scoped_lock l(m_mutex);
|
||||
open_listen_port();
|
||||
m_natpmp.set_mappings(m_listen_interface.port(), 0);
|
||||
m_upnp.set_mappings(m_listen_interface.port(), 0);
|
||||
if (m_natpmp.get())
|
||||
m_natpmp->set_mappings(m_listen_interface.port(), 0);
|
||||
if (m_upnp.get())
|
||||
m_upnp->set_mappings(m_listen_interface.port(), 0);
|
||||
}
|
||||
|
||||
ptime timer = time_now();
|
||||
|
@ -1113,8 +1116,10 @@ namespace libtorrent { namespace detail
|
|||
|
||||
deadline_timer tracker_timer(m_io_service);
|
||||
// this will remove the port mappings
|
||||
m_natpmp.close();
|
||||
m_upnp.close();
|
||||
if (m_natpmp.get())
|
||||
m_natpmp->close();
|
||||
if (m_upnp.get())
|
||||
m_upnp->close();
|
||||
|
||||
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
|
||||
(*m_logger) << time_now_string() << " locking mutex\n";
|
||||
|
@ -1276,7 +1281,7 @@ namespace libtorrent { namespace detail
|
|||
|
||||
torrent_handle session_impl::add_torrent(
|
||||
torrent_info const& ti
|
||||
, boost::filesystem::path const& save_path
|
||||
, fs::path const& save_path
|
||||
, entry const& resume_data
|
||||
, bool compact_mode
|
||||
, int block_size
|
||||
|
@ -1367,7 +1372,7 @@ namespace libtorrent { namespace detail
|
|||
char const* tracker_url
|
||||
, sha1_hash const& info_hash
|
||||
, char const* name
|
||||
, boost::filesystem::path const& save_path
|
||||
, fs::path const& save_path
|
||||
, entry const&
|
||||
, bool compact_mode
|
||||
, int block_size
|
||||
|
@ -1524,13 +1529,18 @@ namespace libtorrent { namespace detail
|
|||
|
||||
if (new_listen_address)
|
||||
{
|
||||
m_natpmp.rebind(new_interface.address());
|
||||
m_upnp.rebind(new_interface.address());
|
||||
m_lsd.rebind(new_interface.address());
|
||||
if (m_natpmp.get())
|
||||
m_natpmp->rebind(new_interface.address());
|
||||
if (m_upnp.get())
|
||||
m_upnp->rebind(new_interface.address());
|
||||
if (m_lsd.get())
|
||||
m_lsd->rebind(new_interface.address());
|
||||
}
|
||||
|
||||
m_natpmp.set_mappings(m_listen_interface.port(), 0);
|
||||
m_upnp.set_mappings(m_listen_interface.port(), 0);
|
||||
if (m_natpmp.get())
|
||||
m_natpmp->set_mappings(m_listen_interface.port(), 0);
|
||||
if (m_upnp.get())
|
||||
m_upnp->set_mappings(m_listen_interface.port(), 0);
|
||||
|
||||
#ifndef TORRENT_DISABLE_DHT
|
||||
if ((new_listen_address || m_dht_same_port) && m_dht)
|
||||
|
@ -1540,8 +1550,10 @@ namespace libtorrent { namespace detail
|
|||
// the listen interface changed, rebind the dht listen socket as well
|
||||
m_dht->rebind(new_interface.address()
|
||||
, m_dht_settings.service_port);
|
||||
m_natpmp.set_mappings(0, m_dht_settings.service_port);
|
||||
m_upnp.set_mappings(0, m_dht_settings.service_port);
|
||||
if (m_natpmp.get())
|
||||
m_natpmp->set_mappings(0, m_dht_settings.service_port);
|
||||
if (m_upnp.get())
|
||||
m_upnp->set_mappings(0, m_dht_settings.service_port);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -1563,7 +1575,8 @@ namespace libtorrent { namespace detail
|
|||
{
|
||||
mutex_t::scoped_lock l(m_mutex);
|
||||
// use internal listen port for local peers
|
||||
m_lsd.announce(ih, m_listen_interface.port());
|
||||
if (m_lsd.get())
|
||||
m_lsd->announce(ih, m_listen_interface.port());
|
||||
}
|
||||
|
||||
void session_impl::on_lsd_peer(tcp::endpoint peer, sha1_hash const& ih)
|
||||
|
@ -1683,8 +1696,10 @@ namespace libtorrent { namespace detail
|
|||
m_dht_settings.service_port = m_listen_interface.port();
|
||||
}
|
||||
m_external_udp_port = m_dht_settings.service_port;
|
||||
m_natpmp.set_mappings(0, m_dht_settings.service_port);
|
||||
m_upnp.set_mappings(0, m_dht_settings.service_port);
|
||||
if (m_natpmp.get())
|
||||
m_natpmp->set_mappings(0, m_dht_settings.service_port);
|
||||
if (m_upnp.get())
|
||||
m_upnp->set_mappings(0, m_dht_settings.service_port);
|
||||
m_dht = new dht::dht_tracker(m_io_service
|
||||
, m_dht_settings, m_listen_interface.address()
|
||||
, startup_state);
|
||||
|
@ -1714,8 +1729,10 @@ namespace libtorrent { namespace detail
|
|||
{
|
||||
m_dht->rebind(m_listen_interface.address()
|
||||
, settings.service_port);
|
||||
m_natpmp.set_mappings(0, m_dht_settings.service_port);
|
||||
m_upnp.set_mappings(0, m_dht_settings.service_port);
|
||||
if (m_natpmp.get())
|
||||
m_natpmp->set_mappings(0, m_dht_settings.service_port);
|
||||
if (m_upnp.get())
|
||||
m_upnp->set_mappings(0, m_dht_settings.service_port);
|
||||
m_external_udp_port = settings.service_port;
|
||||
}
|
||||
m_dht_settings = settings;
|
||||
|
@ -1897,6 +1914,69 @@ namespace libtorrent { namespace detail
|
|||
return m_dl_bandwidth_manager.throttle();
|
||||
}
|
||||
|
||||
|
||||
void session_impl::start_lsd()
|
||||
{
|
||||
mutex_t::scoped_lock l(m_mutex);
|
||||
m_lsd.reset(new lsd(m_io_service
|
||||
, m_listen_interface.address()
|
||||
, bind(&session_impl::on_lsd_peer, this, _1, _2)));
|
||||
}
|
||||
|
||||
void session_impl::start_natpmp()
|
||||
{
|
||||
mutex_t::scoped_lock l(m_mutex);
|
||||
m_natpmp.reset(new natpmp(m_io_service
|
||||
, m_listen_interface.address()
|
||||
, bind(&session_impl::on_port_mapping
|
||||
, this, _1, _2, _3)));
|
||||
|
||||
m_natpmp->set_mappings(m_listen_interface.port(),
|
||||
#ifndef TORRENT_DISABLE_DHT
|
||||
m_dht ? m_dht_settings.service_port :
|
||||
#endif
|
||||
0);
|
||||
}
|
||||
|
||||
void session_impl::start_upnp()
|
||||
{
|
||||
mutex_t::scoped_lock l(m_mutex);
|
||||
m_upnp.reset(new upnp(m_io_service, m_half_open
|
||||
, m_listen_interface.address()
|
||||
, m_settings.user_agent
|
||||
, bind(&session_impl::on_port_mapping
|
||||
, this, _1, _2, _3)));
|
||||
|
||||
m_upnp->set_mappings(m_listen_interface.port(),
|
||||
#ifndef TORRENT_DISABLE_DHT
|
||||
m_dht ? m_dht_settings.service_port :
|
||||
#endif
|
||||
0);
|
||||
}
|
||||
|
||||
void session_impl::stop_lsd()
|
||||
{
|
||||
mutex_t::scoped_lock l(m_mutex);
|
||||
m_lsd.reset();
|
||||
}
|
||||
|
||||
void session_impl::stop_natpmp()
|
||||
{
|
||||
mutex_t::scoped_lock l(m_mutex);
|
||||
if (m_natpmp.get())
|
||||
m_natpmp->close();
|
||||
m_natpmp.reset();
|
||||
}
|
||||
|
||||
void session_impl::stop_upnp()
|
||||
{
|
||||
mutex_t::scoped_lock l(m_mutex);
|
||||
if (m_upnp.get())
|
||||
m_upnp->close();
|
||||
m_upnp.reset();
|
||||
}
|
||||
|
||||
|
||||
#ifndef NDEBUG
|
||||
void session_impl::check_invariant(const char *place)
|
||||
{
|
||||
|
@ -2033,12 +2113,13 @@ namespace libtorrent { namespace detail
|
|||
for (int j = 0; j < num_bitmask_bytes; ++j)
|
||||
{
|
||||
unsigned char bits = bitmask[j];
|
||||
for (int k = 0; k < 8; ++k)
|
||||
int num_bits = std::min(num_blocks_per_piece - j*8, 8);
|
||||
for (int k = 0; k < num_bits; ++k)
|
||||
{
|
||||
const int bit = j * 8 + k;
|
||||
if (bits & (1 << k))
|
||||
{
|
||||
p.info[bit].finished = true;
|
||||
p.info[bit].state = piece_picker::block_info::state_finished;
|
||||
++p.finished;
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -77,7 +77,6 @@ using namespace libtorrent;
|
|||
using boost::tuples::tuple;
|
||||
using boost::tuples::get;
|
||||
using boost::tuples::make_tuple;
|
||||
using boost::filesystem::complete;
|
||||
using boost::bind;
|
||||
using boost::mutex;
|
||||
using libtorrent::aux::session_impl;
|
||||
|
@ -147,11 +146,12 @@ namespace
|
|||
|
||||
namespace libtorrent
|
||||
{
|
||||
|
||||
torrent::torrent(
|
||||
session_impl& ses
|
||||
, aux::checker_impl& checker
|
||||
, torrent_info const& tf
|
||||
, boost::filesystem::path const& save_path
|
||||
, fs::path const& save_path
|
||||
, tcp::endpoint const& net_interface
|
||||
, bool compact_mode
|
||||
, int block_size
|
||||
|
@ -174,7 +174,9 @@ namespace libtorrent
|
|||
, m_resolve_countries(false)
|
||||
#endif
|
||||
, m_announce_timer(ses.m_io_service)
|
||||
#ifndef TORRENT_DISABLE_DHT
|
||||
, m_last_dht_announce(time_now() - minutes(15))
|
||||
#endif
|
||||
, m_policy()
|
||||
, m_ses(ses)
|
||||
, m_checker(checker)
|
||||
|
@ -202,8 +204,6 @@ namespace libtorrent
|
|||
m_initial_done = 0;
|
||||
#endif
|
||||
|
||||
INVARIANT_CHECK;
|
||||
|
||||
m_uploads_quota.min = 2;
|
||||
m_connections_quota.min = 2;
|
||||
// this will be corrected the next time the main session
|
||||
|
@ -212,7 +212,6 @@ namespace libtorrent
|
|||
m_uploads_quota.max = std::numeric_limits<int>::max();
|
||||
m_connections_quota.max = std::numeric_limits<int>::max();
|
||||
m_policy.reset(new policy(this));
|
||||
init();
|
||||
}
|
||||
|
||||
|
||||
|
@ -222,7 +221,7 @@ namespace libtorrent
|
|||
, char const* tracker_url
|
||||
, sha1_hash const& info_hash
|
||||
, char const* name
|
||||
, boost::filesystem::path const& save_path
|
||||
, fs::path const& save_path
|
||||
, tcp::endpoint const& net_interface
|
||||
, bool compact_mode
|
||||
, int block_size
|
||||
|
@ -245,7 +244,9 @@ namespace libtorrent
|
|||
, m_resolve_countries(false)
|
||||
#endif
|
||||
, m_announce_timer(ses.m_io_service)
|
||||
#ifndef TORRENT_DISABLE_DHT
|
||||
, m_last_dht_announce(time_now() - minutes(15))
|
||||
#endif
|
||||
, m_policy()
|
||||
, m_ses(ses)
|
||||
, m_checker(checker)
|
||||
|
@ -295,6 +296,7 @@ namespace libtorrent
|
|||
void torrent::start()
|
||||
{
|
||||
boost::weak_ptr<torrent> self(shared_from_this());
|
||||
if (m_torrent_file.is_valid()) init();
|
||||
m_announce_timer.expires_from_now(seconds(1));
|
||||
m_announce_timer.async_wait(m_ses.m_strand.wrap(
|
||||
bind(&torrent::on_announce_disp, self, _1)));
|
||||
|
@ -327,8 +329,7 @@ namespace libtorrent
|
|||
|
||||
INVARIANT_CHECK;
|
||||
|
||||
if (m_ses.is_aborted())
|
||||
m_abort = true;
|
||||
assert(m_abort);
|
||||
if (!m_connections.empty())
|
||||
disconnect_all();
|
||||
}
|
||||
|
@ -347,17 +348,20 @@ namespace libtorrent
|
|||
}
|
||||
#endif
|
||||
|
||||
// this may not be called from a constructor because of the call to
|
||||
// shared_from_this()
|
||||
void torrent::init()
|
||||
{
|
||||
INVARIANT_CHECK;
|
||||
|
||||
assert(m_torrent_file.is_valid());
|
||||
assert(m_torrent_file.num_files() > 0);
|
||||
assert(m_torrent_file.total_size() >= 0);
|
||||
|
||||
m_have_pieces.resize(m_torrent_file.num_pieces(), false);
|
||||
m_storage.reset(new piece_manager(m_torrent_file, m_save_path
|
||||
, m_ses.m_files, m_storage_constructor));
|
||||
// the shared_from_this() will create an intentional
|
||||
// cycle of ownership, se the hpp file for description.
|
||||
m_owning_storage = new piece_manager(shared_from_this(), m_torrent_file
|
||||
, m_save_path, m_ses.m_files, m_ses.m_disk_thread, m_storage_constructor);
|
||||
m_storage = m_owning_storage.get();
|
||||
m_block_size = calculate_block_size(m_torrent_file, m_default_block_size);
|
||||
m_picker.reset(new piece_picker(
|
||||
static_cast<int>(m_torrent_file.piece_length() / m_block_size)
|
||||
|
@ -558,7 +562,6 @@ namespace libtorrent
|
|||
bind(&torrent::on_peer_name_lookup, shared_from_this(), _1, _2, i->pid)));
|
||||
}
|
||||
}
|
||||
m_policy->pulse();
|
||||
|
||||
if (m_ses.m_alerts.should_post(alert::info))
|
||||
{
|
||||
|
@ -687,7 +690,7 @@ namespace libtorrent
|
|||
int corr = 0;
|
||||
int index = i->index;
|
||||
assert(!m_have_pieces[index]);
|
||||
assert(i->finished < m_picker->blocks_in_piece(index));
|
||||
assert(i->finished <= m_picker->blocks_in_piece(index));
|
||||
|
||||
#ifndef NDEBUG
|
||||
for (std::vector<piece_picker::downloading_piece>::const_iterator j = boost::next(i);
|
||||
|
@ -699,17 +702,17 @@ namespace libtorrent
|
|||
|
||||
for (int j = 0; j < blocks_per_piece; ++j)
|
||||
{
|
||||
assert(i->info[j].finished == 0 || i->info[j].finished == 1);
|
||||
assert(m_picker->is_finished(piece_block(index, j)) == i->info[j].finished);
|
||||
corr += i->info[j].finished * m_block_size;
|
||||
assert(m_picker->is_finished(piece_block(index, j)) == (i->info[j].state == piece_picker::block_info::state_finished));
|
||||
corr += (i->info[j].state == piece_picker::block_info::state_finished) * m_block_size;
|
||||
assert(index != last_piece || j < m_picker->blocks_in_last_piece()
|
||||
|| i->info[j].finished == 0);
|
||||
|| i->info[j].state != piece_picker::block_info::state_finished);
|
||||
}
|
||||
|
||||
// correction if this was the last piece
|
||||
// and if we have the last block
|
||||
if (i->index == last_piece
|
||||
&& i->info[m_picker->blocks_in_last_piece()-1].finished)
|
||||
&& i->info[m_picker->blocks_in_last_piece()-1].state
|
||||
== piece_picker::block_info::state_finished)
|
||||
{
|
||||
corr -= m_block_size;
|
||||
corr += m_torrent_file.piece_size(last_piece) % m_block_size;
|
||||
|
@ -719,8 +722,8 @@ namespace libtorrent
|
|||
wanted_done += corr;
|
||||
}
|
||||
|
||||
assert(total_done < m_torrent_file.total_size());
|
||||
assert(wanted_done < m_torrent_file.total_size());
|
||||
assert(total_done <= m_torrent_file.total_size());
|
||||
assert(wanted_done <= m_torrent_file.total_size());
|
||||
|
||||
std::map<piece_block, int> downloading_piece;
|
||||
for (const_peer_iterator i = begin(); i != end(); ++i)
|
||||
|
@ -784,7 +787,7 @@ namespace libtorrent
|
|||
std::cerr << " " << i->index << " ";
|
||||
for (int j = 0; j < blocks_per_piece; ++j)
|
||||
{
|
||||
std::cerr << i->info[j].finished;
|
||||
std::cerr << (i->info[j].state == piece_picker::block_info::state_finished ? "1" : "0");
|
||||
}
|
||||
std::cerr << std::endl;
|
||||
}
|
||||
|
@ -800,8 +803,8 @@ namespace libtorrent
|
|||
|
||||
}
|
||||
|
||||
assert(total_done < m_torrent_file.total_size());
|
||||
assert(wanted_done < m_torrent_file.total_size());
|
||||
assert(total_done <= m_torrent_file.total_size());
|
||||
assert(wanted_done <= m_torrent_file.total_size());
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -809,6 +812,84 @@ namespace libtorrent
|
|||
return make_tuple(total_done, wanted_done);
|
||||
}
|
||||
|
||||
void torrent::piece_finished(int index, bool passed_hash_check)
|
||||
{
|
||||
session_impl::mutex_t::scoped_lock l(m_ses.m_mutex);
|
||||
|
||||
bool was_seed = is_seed();
|
||||
bool was_finished = m_picker->num_filtered() + num_pieces()
|
||||
== torrent_file().num_pieces();
|
||||
|
||||
if (passed_hash_check)
|
||||
{
|
||||
// the following call may cause picker to become invalid
|
||||
// in case we just became a seed
|
||||
announce_piece(index);
|
||||
assert(valid_metadata());
|
||||
// if we just became a seed, picker is now invalid, since it
|
||||
// is deallocated by the torrent once it starts seeding
|
||||
if (!was_finished
|
||||
&& (is_seed()
|
||||
|| m_picker->num_filtered() + num_pieces()
|
||||
== torrent_file().num_pieces()))
|
||||
{
|
||||
// torrent finished
|
||||
// i.e. all the pieces we're interested in have
|
||||
// been downloaded. Release the files (they will open
|
||||
// in read only mode if needed)
|
||||
try { finished(); }
|
||||
catch (std::exception& e)
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
std::cerr << e.what() << std::endl;
|
||||
assert(false);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
piece_failed(index);
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
try
|
||||
{
|
||||
#endif
|
||||
|
||||
m_policy->piece_finished(index, passed_hash_check);
|
||||
|
||||
#ifndef NDEBUG
|
||||
}
|
||||
catch (std::exception const& e)
|
||||
{
|
||||
std::cerr << e.what() << std::endl;
|
||||
assert(false);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef NDEBUG
|
||||
try
|
||||
{
|
||||
#endif
|
||||
|
||||
if (!was_seed && is_seed())
|
||||
{
|
||||
assert(passed_hash_check);
|
||||
completed();
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
}
|
||||
catch (std::exception const& e)
|
||||
{
|
||||
std::cerr << e.what() << std::endl;
|
||||
assert(false);
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
void torrent::piece_failed(int index)
|
||||
{
|
||||
// if the last piece fails the peer connection will still
|
||||
|
@ -818,7 +899,8 @@ namespace libtorrent
|
|||
// (total_done == m_torrent_file.total_size()) => is_seed()
|
||||
// INVARIANT_CHECK;
|
||||
|
||||
assert(m_storage.get());
|
||||
assert(m_storage);
|
||||
assert(m_storage->refcount() > 0);
|
||||
assert(m_picker.get());
|
||||
assert(index >= 0);
|
||||
assert(index < m_torrent_file.num_pieces());
|
||||
|
@ -907,6 +989,7 @@ namespace libtorrent
|
|||
// start with redownloading the pieces that the client
|
||||
// that has sent the least number of pieces
|
||||
m_picker->restore_piece(index);
|
||||
assert(m_storage);
|
||||
m_storage->mark_failed(index);
|
||||
|
||||
assert(m_have_pieces[index] == false);
|
||||
|
@ -924,7 +1007,19 @@ namespace libtorrent
|
|||
// disconnect all peers and close all
|
||||
// files belonging to the torrents
|
||||
disconnect_all();
|
||||
if (m_storage.get()) m_storage->release_files();
|
||||
if (m_owning_storage.get()) m_storage->async_release_files(
|
||||
bind(&torrent::on_files_released, shared_from_this(), _1, _2));
|
||||
m_owning_storage = 0;
|
||||
}
|
||||
|
||||
void torrent::on_files_released(int ret, disk_io_job const& j)
|
||||
{
|
||||
session_impl::mutex_t::scoped_lock l(m_ses.m_mutex);
|
||||
|
||||
if (alerts().should_post(alert::warning))
|
||||
{
|
||||
alerts().post_alert(torrent_paused_alert(get_handle(), "torrent paused"));
|
||||
}
|
||||
}
|
||||
|
||||
void torrent::announce_piece(int index)
|
||||
|
@ -981,7 +1076,19 @@ namespace libtorrent
|
|||
return m_username + ":" + m_password;
|
||||
}
|
||||
|
||||
void torrent::piece_availability(std::vector<int>& avail) const
|
||||
{
|
||||
INVARIANT_CHECK;
|
||||
|
||||
assert(valid_metadata());
|
||||
if (is_seed())
|
||||
{
|
||||
avail.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
m_picker->get_availability(avail);
|
||||
}
|
||||
|
||||
void torrent::set_piece_priority(int index, int priority)
|
||||
{
|
||||
|
@ -1713,8 +1820,13 @@ namespace libtorrent
|
|||
|
||||
void torrent::set_metadata(entry const& metadata)
|
||||
{
|
||||
INVARIANT_CHECK;
|
||||
|
||||
assert(!m_torrent_file.is_valid());
|
||||
m_torrent_file.parse_info_section(metadata);
|
||||
|
||||
init();
|
||||
|
||||
boost::mutex::scoped_lock(m_checker.m_mutex);
|
||||
|
||||
boost::shared_ptr<aux::piece_checker_data> d(
|
||||
|
@ -1928,7 +2040,9 @@ namespace libtorrent
|
|||
std::for_each(seeds.begin(), seeds.end()
|
||||
, bind(&peer_connection::disconnect, _1));
|
||||
|
||||
m_storage->release_files();
|
||||
assert(m_storage);
|
||||
m_storage->async_release_files(
|
||||
bind(&torrent::on_files_released, shared_from_this(), _1, _2));
|
||||
}
|
||||
|
||||
// called when torrent is complete (all pieces downloaded)
|
||||
|
@ -2004,17 +2118,12 @@ namespace libtorrent
|
|||
{
|
||||
INVARIANT_CHECK;
|
||||
|
||||
if (!m_storage.get())
|
||||
{
|
||||
// this means we have received the metadata through the
|
||||
// metadata extension, and we have to initialize
|
||||
init();
|
||||
}
|
||||
|
||||
assert(m_storage.get());
|
||||
assert(valid_metadata());
|
||||
bool done = true;
|
||||
try
|
||||
{
|
||||
assert(m_storage);
|
||||
assert(m_owning_storage.get());
|
||||
done = m_storage->check_fastresume(data, m_have_pieces, m_num_pieces
|
||||
, m_compact_mode);
|
||||
}
|
||||
|
@ -2043,11 +2152,12 @@ namespace libtorrent
|
|||
{
|
||||
INVARIANT_CHECK;
|
||||
|
||||
assert(m_storage.get());
|
||||
assert(m_owning_storage.get());
|
||||
|
||||
std::pair<bool, float> progress(true, 1.f);
|
||||
try
|
||||
{
|
||||
assert(m_storage);
|
||||
progress = m_storage->check_files(m_have_pieces, m_num_pieces
|
||||
, m_ses.m_mutex);
|
||||
}
|
||||
|
@ -2082,9 +2192,19 @@ namespace libtorrent
|
|||
|
||||
if (!is_seed())
|
||||
{
|
||||
m_picker->files_checked(m_have_pieces, unfinished_pieces);
|
||||
// this is filled in with pieces that needs to be checked
|
||||
// against its hashes.
|
||||
std::vector<int> verify_pieces;
|
||||
m_picker->files_checked(m_have_pieces, unfinished_pieces, verify_pieces);
|
||||
if (m_sequenced_download_threshold > 0)
|
||||
picker().set_sequenced_download_threshold(m_sequenced_download_threshold);
|
||||
while (!verify_pieces.empty())
|
||||
{
|
||||
int piece = verify_pieces.back();
|
||||
verify_pieces.pop_back();
|
||||
async_verify_piece(piece, bind(&torrent::piece_finished
|
||||
, shared_from_this(), piece, _1));
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef TORRENT_DISABLE_EXTENSIONS
|
||||
|
@ -2137,34 +2257,45 @@ namespace libtorrent
|
|||
return m_ses.m_alerts;
|
||||
}
|
||||
|
||||
boost::filesystem::path torrent::save_path() const
|
||||
fs::path torrent::save_path() const
|
||||
{
|
||||
if (m_owning_storage.get())
|
||||
return m_owning_storage->save_path();
|
||||
else
|
||||
return m_save_path;
|
||||
}
|
||||
|
||||
bool torrent::move_storage(boost::filesystem::path const& save_path)
|
||||
void torrent::move_storage(fs::path const& save_path)
|
||||
{
|
||||
INVARIANT_CHECK;
|
||||
|
||||
bool ret = true;
|
||||
if (m_storage.get())
|
||||
if (m_owning_storage.get())
|
||||
{
|
||||
ret = m_storage->move_storage(save_path);
|
||||
m_save_path = m_storage->save_path();
|
||||
m_owning_storage->async_move_storage(save_path
|
||||
, bind(&torrent::on_storage_moved, shared_from_this(), _1, _2));
|
||||
}
|
||||
else
|
||||
{
|
||||
m_save_path = save_path;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void torrent::on_storage_moved(int ret, disk_io_job const& j)
|
||||
{
|
||||
session_impl::mutex_t::scoped_lock l(m_ses.m_mutex);
|
||||
|
||||
if (alerts().should_post(alert::warning))
|
||||
{
|
||||
alerts().post_alert(storage_moved_alert(get_handle(), j.str));
|
||||
}
|
||||
}
|
||||
|
||||
piece_manager& torrent::filesystem()
|
||||
{
|
||||
INVARIANT_CHECK;
|
||||
|
||||
assert(m_storage.get());
|
||||
return *m_storage;
|
||||
assert(m_owning_storage.get());
|
||||
return *m_owning_storage;
|
||||
}
|
||||
|
||||
|
||||
|
@ -2198,11 +2329,11 @@ namespace libtorrent
|
|||
|
||||
if (valid_metadata())
|
||||
{
|
||||
assert(int(m_have_pieces.size()) == m_torrent_file.num_pieces());
|
||||
assert(m_abort || int(m_have_pieces.size()) == m_torrent_file.num_pieces());
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(m_have_pieces.empty());
|
||||
assert(m_abort || m_have_pieces.empty());
|
||||
}
|
||||
|
||||
size_type total_done = quantized_bytes_done();
|
||||
|
@ -2321,7 +2452,14 @@ namespace libtorrent
|
|||
m_just_paused = true;
|
||||
// this will make the storage close all
|
||||
// files and flush all cached data
|
||||
if (m_storage.get()) m_storage->release_files();
|
||||
if (m_owning_storage.get())
|
||||
{
|
||||
assert(m_storage);
|
||||
// TOOD: add a callback which posts
|
||||
// an alert for the client to sync. with
|
||||
m_storage->async_release_files(
|
||||
bind(&torrent::on_files_released, shared_from_this(), _1, _2));
|
||||
}
|
||||
}
|
||||
|
||||
void torrent::resume()
|
||||
|
@ -2448,33 +2586,31 @@ namespace libtorrent
|
|||
m_time_scaler--;
|
||||
if (m_time_scaler <= 0)
|
||||
{
|
||||
m_time_scaler = 10;
|
||||
m_time_scaler = settings().unchoke_interval;
|
||||
m_policy->pulse();
|
||||
}
|
||||
}
|
||||
|
||||
bool torrent::verify_piece(int piece_index)
|
||||
void torrent::async_verify_piece(int piece_index, boost::function<void(bool)> const& f)
|
||||
{
|
||||
// INVARIANT_CHECK;
|
||||
INVARIANT_CHECK;
|
||||
|
||||
assert(m_storage.get());
|
||||
assert(m_storage);
|
||||
assert(m_storage->refcount() > 0);
|
||||
assert(piece_index >= 0);
|
||||
assert(piece_index < m_torrent_file.num_pieces());
|
||||
assert(piece_index < (int)m_have_pieces.size());
|
||||
|
||||
int size = static_cast<int>(m_torrent_file.piece_size(piece_index));
|
||||
std::vector<char> buffer(size);
|
||||
assert(size > 0);
|
||||
m_storage->read(&buffer[0], piece_index, 0, size);
|
||||
m_storage->async_hash(piece_index, bind(&torrent::on_piece_verified
|
||||
, shared_from_this(), _1, _2, f));
|
||||
}
|
||||
|
||||
hasher h;
|
||||
h.update(&buffer[0], size);
|
||||
sha1_hash digest = h.final();
|
||||
|
||||
if (m_torrent_file.hash_for_piece(piece_index) != digest)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
void torrent::on_piece_verified(int ret, disk_io_job const& j
|
||||
, boost::function<void(bool)> f)
|
||||
{
|
||||
sha1_hash h(j.str);
|
||||
session_impl::mutex_t::scoped_lock l(m_ses.m_mutex);
|
||||
f(m_torrent_file.hash_for_piece(j.piece) == h);
|
||||
}
|
||||
|
||||
const tcp::endpoint& torrent::current_tracker() const
|
||||
|
@ -2483,7 +2619,7 @@ namespace libtorrent
|
|||
}
|
||||
|
||||
bool torrent::is_allocating() const
|
||||
{ return m_storage.get() && m_storage->is_allocating(); }
|
||||
{ return m_owning_storage.get() && m_owning_storage->is_allocating(); }
|
||||
|
||||
void torrent::file_progress(std::vector<float>& fp) const
|
||||
{
|
||||
|
@ -2533,8 +2669,8 @@ namespace libtorrent
|
|||
torrent_status st;
|
||||
|
||||
st.num_peers = (int)std::count_if(m_connections.begin(), m_connections.end(),
|
||||
boost::bind<bool>(std::logical_not<bool>(), boost::bind(&peer_connection::is_connecting,
|
||||
boost::bind(&std::map<tcp::endpoint,peer_connection*>::value_type::second, _1))));
|
||||
!boost::bind(&peer_connection::is_connecting
|
||||
, boost::bind(&std::map<tcp::endpoint,peer_connection*>::value_type::second, _1)));
|
||||
|
||||
st.num_complete = m_complete;
|
||||
st.num_incomplete = m_incomplete;
|
||||
|
@ -2635,7 +2771,6 @@ namespace libtorrent
|
|||
}
|
||||
else if (st.total_wanted_done == st.total_wanted)
|
||||
{
|
||||
assert(st.total_done != m_torrent_file.total_size());
|
||||
st.state = torrent_status::finished;
|
||||
}
|
||||
else
|
||||
|
@ -2655,10 +2790,10 @@ namespace libtorrent
|
|||
{
|
||||
INVARIANT_CHECK;
|
||||
|
||||
return (int)std::count_if(m_connections.begin(), m_connections.end(),
|
||||
boost::bind(&peer_connection::is_seed,
|
||||
boost::bind(&std::map<tcp::endpoint
|
||||
,peer_connection*>::value_type::second, _1)));
|
||||
return (int)std::count_if(m_connections.begin(), m_connections.end()
|
||||
, boost::bind(&peer_connection::is_seed
|
||||
, boost::bind(&std::map<tcp::endpoint
|
||||
, peer_connection*>::value_type::second, _1)));
|
||||
}
|
||||
|
||||
void torrent::tracker_request_timed_out(
|
||||
|
|
|
@ -80,6 +80,8 @@ using libtorrent::aux::session_impl;
|
|||
|
||||
namespace libtorrent
|
||||
{
|
||||
namespace fs = boost::filesystem;
|
||||
|
||||
namespace
|
||||
{
|
||||
void throw_invalid_handle()
|
||||
|
@ -205,12 +207,12 @@ namespace libtorrent
|
|||
, bind(&torrent::download_limit, _1));
|
||||
}
|
||||
|
||||
bool torrent_handle::move_storage(
|
||||
boost::filesystem::path const& save_path) const
|
||||
void torrent_handle::move_storage(
|
||||
fs::path const& save_path) const
|
||||
{
|
||||
INVARIANT_CHECK;
|
||||
|
||||
return call_member<bool>(m_ses, m_chk, m_info_hash
|
||||
call_member<void>(m_ses, m_chk, m_info_hash
|
||||
, bind(&torrent::move_storage, _1, save_path));
|
||||
}
|
||||
|
||||
|
@ -352,6 +354,14 @@ namespace libtorrent
|
|||
}
|
||||
|
||||
|
||||
void torrent_handle::piece_availability(std::vector<int>& avail) const
|
||||
{
|
||||
INVARIANT_CHECK;
|
||||
|
||||
call_member<void>(m_ses, m_chk, m_info_hash
|
||||
, bind(&torrent::piece_availability, _1, boost::ref(avail)));
|
||||
}
|
||||
|
||||
void torrent_handle::piece_priority(int index, int priority) const
|
||||
{
|
||||
INVARIANT_CHECK;
|
||||
|
@ -556,9 +566,12 @@ namespace libtorrent
|
|||
for (int j = 0; j < num_bitmask_bytes; ++j)
|
||||
{
|
||||
unsigned char v = 0;
|
||||
for (int k = 0; k < 8; ++k)
|
||||
v |= i->info[j*8+k].finished?(1 << k):0;
|
||||
int bits = std::min(num_blocks_per_piece - j*8, 8);
|
||||
for (int k = 0; k < bits; ++k)
|
||||
v |= (i->info[j*8+k].state == piece_picker::block_info::state_finished)
|
||||
? (1 << k) : 0;
|
||||
bitmask.insert(bitmask.end(), v);
|
||||
assert(bits == 8 || j == num_bitmask_bytes - 1);
|
||||
}
|
||||
piece_struct["bitmask"] = bitmask;
|
||||
|
||||
|
@ -608,11 +621,11 @@ namespace libtorrent
|
|||
}
|
||||
|
||||
|
||||
boost::filesystem::path torrent_handle::save_path() const
|
||||
fs::path torrent_handle::save_path() const
|
||||
{
|
||||
INVARIANT_CHECK;
|
||||
|
||||
return call_member<boost::filesystem::path>(m_ses, m_chk, m_info_hash
|
||||
return call_member<fs::path>(m_ses, m_chk, m_info_hash
|
||||
, bind(&torrent::save_path, _1));
|
||||
}
|
||||
|
||||
|
@ -717,7 +730,7 @@ namespace libtorrent
|
|||
{
|
||||
peer_connection* peer = i->second;
|
||||
|
||||
// peers that haven't finished the handshake should
|
||||
// incoming peers that haven't finished the handshake should
|
||||
// not be included in this list
|
||||
if (peer->associated_torrent().expired()) continue;
|
||||
|
||||
|
@ -760,10 +773,9 @@ namespace libtorrent
|
|||
pi.blocks_in_piece = p.blocks_in_piece(i->index);
|
||||
for (int j = 0; j < pi.blocks_in_piece; ++j)
|
||||
{
|
||||
pi.peer[j] = i->info[j].peer;
|
||||
pi.num_downloads[j] = i->info[j].num_downloads;
|
||||
pi.finished_blocks[j] = i->info[j].finished;
|
||||
pi.requested_blocks[j] = i->info[j].requested;
|
||||
pi.blocks[j].peer = i->info[j].peer;
|
||||
pi.blocks[j].num_downloads = i->info[j].num_downloads;
|
||||
pi.blocks[j].state = i->info[j].state;
|
||||
}
|
||||
pi.piece_index = i->index;
|
||||
queue.push_back(pi);
|
||||
|
|
|
@ -62,10 +62,12 @@ namespace pt = boost::posix_time;
|
|||
namespace gr = boost::gregorian;
|
||||
|
||||
using namespace libtorrent;
|
||||
using namespace boost::filesystem;
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
namespace fs = boost::filesystem;
|
||||
|
||||
void convert_to_utf8(std::string& str, unsigned char chr)
|
||||
{
|
||||
str += 0xc0 | ((chr & 0xff) >> 6);
|
||||
|
@ -153,7 +155,7 @@ namespace
|
|||
// encoded string
|
||||
if (!valid_encoding)
|
||||
{
|
||||
target.orig_path.reset(new path(target.path));
|
||||
target.orig_path.reset(new fs::path(target.path));
|
||||
target.path = tmp_path;
|
||||
}
|
||||
}
|
||||
|
@ -203,8 +205,8 @@ namespace
|
|||
offset += target.back().size;
|
||||
}
|
||||
}
|
||||
|
||||
void remove_dir(path& p)
|
||||
/*
|
||||
void remove_dir(fs::path& p)
|
||||
{
|
||||
assert(p.begin() != p.end());
|
||||
path tmp;
|
||||
|
@ -212,6 +214,7 @@ namespace
|
|||
tmp /= *i;
|
||||
p = tmp;
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
namespace libtorrent
|
||||
|
@ -277,6 +280,29 @@ namespace libtorrent
|
|||
torrent_info::~torrent_info()
|
||||
{}
|
||||
|
||||
void torrent_info::swap(torrent_info& ti)
|
||||
{
|
||||
using std::swap;
|
||||
m_urls.swap(ti.m_urls);
|
||||
m_url_seeds.swap(ti.m_url_seeds);
|
||||
swap(m_piece_length, ti.m_piece_length);
|
||||
m_piece_hash.swap(ti.m_piece_hash);
|
||||
m_files.swap(ti.m_files);
|
||||
m_nodes.swap(ti.m_nodes);
|
||||
swap(m_num_pieces, ti.m_num_pieces);
|
||||
swap(m_info_hash, ti.m_info_hash);
|
||||
m_name.swap(ti.m_name);
|
||||
swap(m_creation_date, ti.m_creation_date);
|
||||
m_comment.swap(ti.m_comment);
|
||||
m_created_by.swap(ti.m_created_by);
|
||||
swap(m_multifile, ti.m_multifile);
|
||||
swap(m_private, ti.m_private);
|
||||
m_extra_info.swap(ti.m_extra_info);
|
||||
#ifndef NDEBUG
|
||||
swap(m_half_metadata, ti.m_half_metadata);
|
||||
#endif
|
||||
}
|
||||
|
||||
void torrent_info::set_piece_size(int size)
|
||||
{
|
||||
// make sure the size is an even power of 2
|
||||
|
@ -323,7 +349,7 @@ namespace libtorrent
|
|||
else
|
||||
{ m_name = info["name"].string(); }
|
||||
|
||||
path tmp = m_name;
|
||||
fs::path tmp = m_name;
|
||||
if (tmp.is_complete()) throw std::runtime_error("torrent contains "
|
||||
"a file with an absolute path: '" + m_name + "'");
|
||||
if (tmp.has_branch_path()) throw std::runtime_error(
|
||||
|
@ -526,9 +552,9 @@ namespace libtorrent
|
|||
, bind(&announce_entry::tier, _1), bind(&announce_entry::tier, _2)));
|
||||
}
|
||||
|
||||
void torrent_info::add_file(boost::filesystem::path file, size_type size)
|
||||
void torrent_info::add_file(fs::path file, size_type size)
|
||||
{
|
||||
assert(file.begin() != file.end());
|
||||
// assert(file.begin() != file.end());
|
||||
|
||||
if (!file.has_branch_path())
|
||||
{
|
||||
|
@ -589,8 +615,6 @@ namespace libtorrent
|
|||
|
||||
entry torrent_info::create_info_metadata() const
|
||||
{
|
||||
namespace fs = boost::filesystem;
|
||||
|
||||
// you have to add files to the torrent first
|
||||
assert(!m_files.empty());
|
||||
|
||||
|
@ -650,8 +674,6 @@ namespace libtorrent
|
|||
{
|
||||
assert(m_piece_length > 0);
|
||||
|
||||
namespace fs = boost::filesystem;
|
||||
|
||||
if ((m_urls.empty() && m_nodes.empty()) || m_files.empty())
|
||||
{
|
||||
// TODO: throw something here
|
||||
|
|
|
@ -289,28 +289,6 @@ namespace libtorrent
|
|||
return ret;
|
||||
}
|
||||
|
||||
void intrusive_ptr_add_ref(timeout_handler const* c)
|
||||
{
|
||||
assert(c != 0);
|
||||
assert(c->m_refs >= 0);
|
||||
timeout_handler::mutex_t::scoped_lock l(c->m_mutex);
|
||||
++c->m_refs;
|
||||
}
|
||||
|
||||
void intrusive_ptr_release(timeout_handler const* c)
|
||||
{
|
||||
assert(c != 0);
|
||||
assert(c->m_refs > 0);
|
||||
timeout_handler::mutex_t::scoped_lock l(c->m_mutex);
|
||||
--c->m_refs;
|
||||
if (c->m_refs == 0)
|
||||
{
|
||||
l.unlock();
|
||||
delete c;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
timeout_handler::timeout_handler(asio::strand& str)
|
||||
: m_strand(str)
|
||||
, m_start_time(time_now())
|
||||
|
@ -318,7 +296,6 @@ namespace libtorrent
|
|||
, m_timeout(str.io_service())
|
||||
, m_completion_timeout(0)
|
||||
, m_read_timeout(0)
|
||||
, m_refs(0)
|
||||
{}
|
||||
|
||||
void timeout_handler::set_timeout(int completion_timeout, int read_timeout)
|
||||
|
@ -467,8 +444,19 @@ namespace libtorrent
|
|||
++start;
|
||||
}
|
||||
|
||||
std::string::iterator port_pos
|
||||
= std::find(start, url.end(), ':');
|
||||
std::string::iterator port_pos;
|
||||
|
||||
// this is for IPv6 addresses
|
||||
if (start != url.end() && *start == '[')
|
||||
{
|
||||
port_pos = std::find(start, url.end(), ']');
|
||||
if (port_pos == url.end()) throw std::runtime_error("invalid hostname syntax");
|
||||
port_pos = std::find(port_pos, url.end(), ':');
|
||||
}
|
||||
else
|
||||
{
|
||||
port_pos = std::find(start, url.end(), ':');
|
||||
}
|
||||
|
||||
if (port_pos < end)
|
||||
{
|
||||
|
|
|
@ -307,6 +307,9 @@ namespace libtorrent
|
|||
// event
|
||||
detail::write_int32(req.event, out);
|
||||
// ip address
|
||||
if (m_settings.announce_ip != address() && m_settings.announce_ip.is_v4())
|
||||
detail::write_uint32(m_settings.announce_ip.to_v4().to_ulong(), out);
|
||||
else
|
||||
detail::write_int32(0, out);
|
||||
// key
|
||||
detail::write_int32(req.key, out);
|
||||
|
|
2
setup.py
2
setup.py
|
@ -160,6 +160,7 @@ deluge_core = Extension('deluge_core',
|
|||
'libtorrent/src/bandwidth_manager.cpp',
|
||||
'libtorrent/src/bt_peer_connection.cpp',
|
||||
'libtorrent/src/connection_queue.cpp',
|
||||
'libtorrent/src/disk_io_thread.cpp',
|
||||
'libtorrent/src/entry.cpp',
|
||||
'libtorrent/src/escape_string.cpp',
|
||||
'libtorrent/src/file.cpp',
|
||||
|
@ -181,6 +182,7 @@ deluge_core = Extension('deluge_core',
|
|||
'libtorrent/src/session.cpp',
|
||||
'libtorrent/src/session_impl.cpp',
|
||||
'libtorrent/src/sha1.cpp',
|
||||
'libtorrent/src/socks4_stream.cpp',
|
||||
'libtorrent/src/socks5_stream.cpp',
|
||||
'libtorrent/src/stat.cpp',
|
||||
'libtorrent/src/storage.cpp',
|
||||
|
|
|
@ -71,7 +71,10 @@ PREF_FUNCTIONS = {
|
|||
"max_active_torrents" : None, # no need for a function, applied constantly
|
||||
"auto_seed_ratio" : None, # no need for a function, applied constantly
|
||||
"max_download_rate_bps" : deluge_core.set_download_rate_limit,
|
||||
"max_upload_rate_bps" : deluge_core.set_upload_rate_limit
|
||||
"max_upload_rate_bps" : deluge_core.set_upload_rate_limit,
|
||||
"use_upnp" : deluge_core.use_upnp,
|
||||
"use_natpmp" : deluge_core.use_natpmp,
|
||||
"use_utpex" : deluge_core.use_utpex
|
||||
}
|
||||
|
||||
STATE_MESSAGES = ( "Queued",
|
||||
|
@ -443,6 +446,7 @@ class Manager:
|
|||
# altering max_active_torrents), or just from time to time
|
||||
# ___ALL queuing code should be in this function, and ONLY here___
|
||||
def apply_queue(self, efficient = True):
|
||||
print "applying queue";
|
||||
# Handle autoseeding - downqueue as needed
|
||||
if self.get_pref('auto_seed_ratio') > 0:
|
||||
for unique_ID in self.unique_IDs:
|
||||
|
@ -781,3 +785,6 @@ class Manager:
|
|||
# Adds an IP range (as two dotted quad strings) to the filter
|
||||
def add_range_to_ip_filter(self, start, end):
|
||||
return deluge_core.add_range_to_IP_filter(start, end)
|
||||
|
||||
def netextras(self, proto, action):
|
||||
return deluge_core.netextras(proto, action)
|
||||
|
|
|
@ -338,7 +338,6 @@ static PyObject *torrent_init(PyObject *self, PyObject *args)
|
|||
M_ses->set_severity_level(alert::debug);
|
||||
|
||||
M_ses->add_extension(&libtorrent::create_metadata_plugin);
|
||||
M_ses->add_extension(&libtorrent::create_ut_pex_plugin);
|
||||
|
||||
M_constants = Py_BuildValue("{s:i,s:i,s:i,s:i,s:i,s:i,s:i,s:i,s:i,s:i,s:i,s:i,s:i,s:i,s:i,s:i,s:i,s:i}",
|
||||
"EVENT_NULL", EVENT_NULL,
|
||||
|
@ -1228,6 +1227,56 @@ static PyObject *torrent_add_range_to_IP_filter(PyObject *self, PyObject *args)
|
|||
Py_INCREF(Py_None); return Py_None;
|
||||
}
|
||||
|
||||
static PyObject *torrent_use_upnp(PyObject *self, PyObject *args)
|
||||
{
|
||||
python_long action;
|
||||
PyArg_ParseTuple(args, "i", &action);
|
||||
|
||||
if (action){
|
||||
printf("Starting UPnP\r\n");
|
||||
M_ses->start_upnp();
|
||||
}
|
||||
else{
|
||||
printf("Stopping natpmp\r\n");
|
||||
M_ses->stop_upnp();
|
||||
}
|
||||
|
||||
Py_INCREF(Py_None); return Py_None;
|
||||
|
||||
}
|
||||
|
||||
static PyObject *torrent_use_natpmp(PyObject *self, PyObject *args)
|
||||
{
|
||||
python_long action;
|
||||
|
||||
PyArg_ParseTuple(args, "i", &action);
|
||||
|
||||
if (action){
|
||||
printf("Starting NAT-PMP\r\n");
|
||||
M_ses->start_natpmp();
|
||||
}
|
||||
else{
|
||||
printf("Stopping NAT-PMP\r\n");
|
||||
M_ses->stop_natpmp();
|
||||
}
|
||||
|
||||
Py_INCREF(Py_None); return Py_None;
|
||||
}
|
||||
|
||||
static PyObject *torrent_use_utpex(PyObject *self, PyObject *args)
|
||||
{
|
||||
python_long action;
|
||||
|
||||
PyArg_ParseTuple(args, "i", &action);
|
||||
|
||||
if (action){
|
||||
printf("Starting UTPEX\r\n");
|
||||
M_ses->add_extension(&libtorrent::create_ut_pex_plugin);
|
||||
}
|
||||
|
||||
Py_INCREF(Py_None); return Py_None;
|
||||
}
|
||||
|
||||
static PyObject *torrent_pe_settings(PyObject *self, PyObject *args)
|
||||
{
|
||||
M_pe_settings = new pe_settings();
|
||||
|
@ -1284,6 +1333,9 @@ static PyMethodDef deluge_core_methods[] =
|
|||
{"create_torrent", torrent_create_torrent, METH_VARARGS, "."},
|
||||
{"reset_IP_filter", torrent_reset_IP_filter, METH_VARARGS, "."},
|
||||
{"add_range_to_IP_filter", torrent_add_range_to_IP_filter, METH_VARARGS, "."},
|
||||
{"use_upnp", torrent_use_upnp, METH_VARARGS, "."},
|
||||
{"use_natpmp", torrent_use_natpmp, METH_VARARGS, "."},
|
||||
{"use_utpex", torrent_use_utpex, METH_VARARGS, "."},
|
||||
{NULL}
|
||||
};
|
||||
|
||||
|
|
|
@ -53,6 +53,9 @@ class PreferencesDlg:
|
|||
self.glade.get_widget("combo_encout").set_active(self.preferences.get("encout_state", int, default=common.EncState.enabled))
|
||||
self.glade.get_widget("combo_enclevel").set_active(self.preferences.get("enclevel_type", int, default=common.EncLevel.both))
|
||||
self.glade.get_widget("chk_pref_rc4").set_active(self.preferences.get("pref_rc4", bool, default=True))
|
||||
self.glade.get_widget("chk_upnp").set_active(self.preferences.get("use_upnp", bool, default=True))
|
||||
self.glade.get_widget("chk_natpmp").set_active(self.preferences.get("use_natpmp", bool, default=True))
|
||||
self.glade.get_widget("chk_utpex").set_active(self.preferences.get("use_utpex", bool, default=True))
|
||||
self.glade.get_widget("chk_use_tray").set_active(self.preferences.get("enable_system_tray", bool, default=True))
|
||||
self.glade.get_widget("chk_min_on_close").set_active(self.preferences.get("close_to_tray", bool, default=False))
|
||||
self.glade.get_widget("chk_lock_tray").set_active(self.preferences.get("lock_tray", bool, default=False))
|
||||
|
@ -90,6 +93,9 @@ class PreferencesDlg:
|
|||
self.preferences.set("encout_state", self.glade.get_widget("combo_encout").get_active())
|
||||
self.preferences.set("enclevel_type", self.glade.get_widget("combo_enclevel").get_active())
|
||||
self.preferences.set("pref_rc4", self.glade.get_widget("chk_pref_rc4").get_active())
|
||||
self.preferences.set("use_upnp", self.glade.get_widget("chk_upnp").get_active())
|
||||
self.preferences.set("use_natpmp", self.glade.get_widget("chk_natpmp").get_active())
|
||||
self.preferences.set("use_utpex", self.glade.get_widget("chk_utpex").get_active())
|
||||
self.preferences.set("system_tray", self.glade.get_widget("chk_use_tray").get_active())
|
||||
self.preferences.set("close_to_tray", self.glade.get_widget("chk_min_on_close").get_active())
|
||||
self.preferences.set("lock_tray", self.glade.get_widget("chk_lock_tray").get_active())
|
||||
|
|
|
@ -76,6 +76,9 @@ DEFAULT_PREFS = {
|
|||
"tray_passwd" : "",
|
||||
"use_compact_storage" : False,
|
||||
"use_default_dir" : False,
|
||||
"use_natpmp" : False,
|
||||
"use_upnp" : False,
|
||||
"use_utpex" : False,
|
||||
"window_height" : 480,
|
||||
"window_maximized" : False,
|
||||
"window_pane_position" : -1,
|
||||
|
|
Loading…
Reference in New Issue