summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorZach Tibbitts <zach@collegegeek.org>2006-12-01 18:07:03 +0000
committerZach Tibbitts <zach@collegegeek.org>2006-12-01 18:07:03 +0000
commit5ef57145ba5743888cc5230766440078bb6b2d06 (patch)
treed25495d1dbf3e33d179d833079bdfec58e16c608
parentd86bef1a777073652750d8cc43b171d51041295c (diff)
downloaddeluge-5ef57145ba5743888cc5230766440078bb6b2d06.tar.gz
deluge-5ef57145ba5743888cc5230766440078bb6b2d06.tar.bz2
deluge-5ef57145ba5743888cc5230766440078bb6b2d06.zip
directory restructuring
-rw-r--r--library/INSTALL32
-rw-r--r--library/LICENSE13
-rw-r--r--library/Makefile.am71
-rw-r--r--library/Makefile.in641
-rw-r--r--library/Makefile.libtorrent-only.am71
-rw-r--r--library/Makefile.libtorrent-only.in641
-rw-r--r--library/README30
-rwxr-xr-xlibrary/alert.cpp124
-rw-r--r--library/allocate_resources.cpp201
-rwxr-xr-xlibrary/bt_peer_connection.cpp1571
-rw-r--r--library/control9
-rwxr-xr-xlibrary/debianit6
-rwxr-xr-xlibrary/entry.cpp344
-rw-r--r--library/errorlog.txt6
-rwxr-xr-xlibrary/escape_string.cpp148
-rwxr-xr-xlibrary/file.cpp313
-rwxr-xr-xlibrary/http_tracker_connection.cpp845
-rwxr-xr-xlibrary/identify_client.cpp326
-rwxr-xr-xlibrary/include/libtorrent/alert.hpp174
-rwxr-xr-xlibrary/include/libtorrent/alert_types.hpp300
-rw-r--r--library/include/libtorrent/allocate_resources.hpp72
-rw-r--r--library/include/libtorrent/asio.hpp71
-rw-r--r--library/include/libtorrent/asio/basic_datagram_socket.hpp802
-rw-r--r--library/include/libtorrent/asio/basic_deadline_timer.hpp309
-rw-r--r--library/include/libtorrent/asio/basic_io_object.hpp75
-rw-r--r--library/include/libtorrent/asio/basic_resolver.hpp252
-rw-r--r--library/include/libtorrent/asio/basic_socket.hpp919
-rw-r--r--library/include/libtorrent/asio/basic_socket_acceptor.hpp854
-rw-r--r--library/include/libtorrent/asio/basic_socket_iostream.hpp176
-rw-r--r--library/include/libtorrent/asio/basic_socket_streambuf.hpp280
-rw-r--r--library/include/libtorrent/asio/basic_stream_socket.hpp816
-rw-r--r--library/include/libtorrent/asio/basic_streambuf.hpp200
-rw-r--r--library/include/libtorrent/asio/buffer.hpp639
-rw-r--r--library/include/libtorrent/asio/buffered_read_stream.hpp407
-rw-r--r--library/include/libtorrent/asio/buffered_read_stream_fwd.hpp29
-rw-r--r--library/include/libtorrent/asio/buffered_stream.hpp249
-rw-r--r--library/include/libtorrent/asio/buffered_stream_fwd.hpp29
-rw-r--r--library/include/libtorrent/asio/buffered_write_stream.hpp362
-rw-r--r--library/include/libtorrent/asio/buffered_write_stream_fwd.hpp29
-rw-r--r--library/include/libtorrent/asio/completion_condition.hpp101
-rw-r--r--library/include/libtorrent/asio/datagram_socket_service.hpp295
-rw-r--r--library/include/libtorrent/asio/deadline_timer.hpp37
-rw-r--r--library/include/libtorrent/asio/deadline_timer_service.hpp152
-rw-r--r--library/include/libtorrent/asio/detail/bind_handler.hpp349
-rw-r--r--library/include/libtorrent/asio/detail/buffer_resize_guard.hpp70
-rw-r--r--library/include/libtorrent/asio/detail/buffered_stream_storage.hpp127
-rw-r--r--library/include/libtorrent/asio/detail/call_stack.hpp90
-rw-r--r--library/include/libtorrent/asio/detail/const_buffers_iterator.hpp150
-rw-r--r--library/include/libtorrent/asio/detail/consuming_buffers.hpp196
-rw-r--r--library/include/libtorrent/asio/detail/deadline_timer_service.hpp182
-rw-r--r--library/include/libtorrent/asio/detail/epoll_reactor.hpp593
-rw-r--r--library/include/libtorrent/asio/detail/epoll_reactor_fwd.hpp47
-rw-r--r--library/include/libtorrent/asio/detail/event.hpp50
-rw-r--r--library/include/libtorrent/asio/detail/fd_set_adapter.hpp41
-rw-r--r--library/include/libtorrent/asio/detail/handler_alloc_helpers.hpp256
-rw-r--r--library/include/libtorrent/asio/detail/handler_invoke_helpers.hpp47
-rw-r--r--library/include/libtorrent/asio/detail/hash_map.hpp197
-rw-r--r--library/include/libtorrent/asio/detail/io_control.hpp137
-rw-r--r--library/include/libtorrent/asio/detail/kqueue_reactor.hpp596
-rw-r--r--library/include/libtorrent/asio/detail/kqueue_reactor_fwd.hpp41
-rw-r--r--library/include/libtorrent/asio/detail/mutex.hpp50
-rw-r--r--library/include/libtorrent/asio/detail/noncopyable.hpp55
-rw-r--r--library/include/libtorrent/asio/detail/null_event.hpp68
-rw-r--r--library/include/libtorrent/asio/detail/null_mutex.hpp66
-rw-r--r--library/include/libtorrent/asio/detail/null_signal_blocker.hpp63
-rw-r--r--library/include/libtorrent/asio/detail/null_thread.hpp67
-rw-r--r--library/include/libtorrent/asio/detail/null_tss_ptr.hpp70
-rw-r--r--library/include/libtorrent/asio/detail/old_win_sdk_compat.hpp312
-rw-r--r--library/include/libtorrent/asio/detail/pipe_select_interrupter.hpp104
-rw-r--r--library/include/libtorrent/asio/detail/pop_options.hpp88
-rw-r--r--library/include/libtorrent/asio/detail/posix_event.hpp107
-rw-r--r--library/include/libtorrent/asio/detail/posix_fd_set_adapter.hpp71
-rw-r--r--library/include/libtorrent/asio/detail/posix_mutex.hpp94
-rw-r--r--library/include/libtorrent/asio/detail/posix_signal_blocker.hpp89
-rw-r--r--library/include/libtorrent/asio/detail/posix_thread.hpp125
-rw-r--r--library/include/libtorrent/asio/detail/posix_tss_ptr.hpp84
-rw-r--r--library/include/libtorrent/asio/detail/push_options.hpp106
-rw-r--r--library/include/libtorrent/asio/detail/reactive_socket_service.hpp1640
-rw-r--r--library/include/libtorrent/asio/detail/reactor_op_queue.hpp379
-rw-r--r--library/include/libtorrent/asio/detail/resolver_service.hpp361
-rw-r--r--library/include/libtorrent/asio/detail/scoped_lock.hpp79
-rw-r--r--library/include/libtorrent/asio/detail/select_interrupter.hpp41
-rw-r--r--library/include/libtorrent/asio/detail/select_reactor.hpp435
-rw-r--r--library/include/libtorrent/asio/detail/select_reactor_fwd.hpp31
-rw-r--r--library/include/libtorrent/asio/detail/service_registry.hpp163
-rw-r--r--library/include/libtorrent/asio/detail/signal_blocker.hpp50
-rw-r--r--library/include/libtorrent/asio/detail/signal_init.hpp51
-rw-r--r--library/include/libtorrent/asio/detail/socket_holder.hpp91
-rw-r--r--library/include/libtorrent/asio/detail/socket_ops.hpp1534
-rw-r--r--library/include/libtorrent/asio/detail/socket_option.hpp323
-rw-r--r--library/include/libtorrent/asio/detail/socket_select_interrupter.hpp177
-rw-r--r--library/include/libtorrent/asio/detail/socket_types.hpp184
-rw-r--r--library/include/libtorrent/asio/detail/strand_service.hpp528
-rw-r--r--library/include/libtorrent/asio/detail/task_io_service.hpp526
-rw-r--r--library/include/libtorrent/asio/detail/task_io_service_fwd.hpp31
-rw-r--r--library/include/libtorrent/asio/detail/thread.hpp50
-rw-r--r--library/include/libtorrent/asio/detail/timer_queue.hpp345
-rw-r--r--library/include/libtorrent/asio/detail/timer_queue_base.hpp56
-rw-r--r--library/include/libtorrent/asio/detail/tss_ptr.hpp65
-rw-r--r--library/include/libtorrent/asio/detail/win_event.hpp88
-rw-r--r--library/include/libtorrent/asio/detail/win_fd_set_adapter.hpp83
-rw-r--r--library/include/libtorrent/asio/detail/win_iocp_io_service.hpp389
-rw-r--r--library/include/libtorrent/asio/detail/win_iocp_io_service_fwd.hpp46
-rw-r--r--library/include/libtorrent/asio/detail/win_iocp_operation.hpp81
-rw-r--r--library/include/libtorrent/asio/detail/win_iocp_socket_service.hpp2077
-rw-r--r--library/include/libtorrent/asio/detail/win_local_free_on_block_exit.hpp59
-rw-r--r--library/include/libtorrent/asio/detail/win_mutex.hpp142
-rw-r--r--library/include/libtorrent/asio/detail/win_signal_blocker.hpp67
-rw-r--r--library/include/libtorrent/asio/detail/win_thread.hpp121
-rw-r--r--library/include/libtorrent/asio/detail/win_tss_ptr.hpp85
-rw-r--r--library/include/libtorrent/asio/detail/winsock_init.hpp116
-rw-r--r--library/include/libtorrent/asio/detail/wrapped_handler.hpp187
-rw-r--r--library/include/libtorrent/asio/error.hpp387
-rw-r--r--library/include/libtorrent/asio/error_handler.hpp120
-rw-r--r--library/include/libtorrent/asio/handler_alloc_hook.hpp88
-rw-r--r--library/include/libtorrent/asio/handler_invoke_hook.hpp69
-rw-r--r--library/include/libtorrent/asio/impl/io_service.ipp150
-rw-r--r--library/include/libtorrent/asio/impl/read.ipp294
-rw-r--r--library/include/libtorrent/asio/impl/read_until.ipp664
-rw-r--r--library/include/libtorrent/asio/impl/write.ipp266
-rw-r--r--library/include/libtorrent/asio/io_service.hpp424
-rw-r--r--library/include/libtorrent/asio/ip/address.hpp284
-rw-r--r--library/include/libtorrent/asio/ip/address_v4.hpp292
-rw-r--r--library/include/libtorrent/asio/ip/address_v6.hpp381
-rw-r--r--library/include/libtorrent/asio/ip/basic_endpoint.hpp358
-rw-r--r--library/include/libtorrent/asio/ip/basic_resolver_entry.hpp98
-rw-r--r--library/include/libtorrent/asio/ip/basic_resolver_iterator.hpp151
-rw-r--r--library/include/libtorrent/asio/ip/basic_resolver_query.hpp152
-rw-r--r--library/include/libtorrent/asio/ip/detail/socket_option.hpp406
-rw-r--r--library/include/libtorrent/asio/ip/host_name.hpp60
-rw-r--r--library/include/libtorrent/asio/ip/multicast.hpp181
-rw-r--r--library/include/libtorrent/asio/ip/resolver_query_base.hpp107
-rw-r--r--library/include/libtorrent/asio/ip/tcp.hpp146
-rw-r--r--library/include/libtorrent/asio/ip/udp.hpp104
-rw-r--r--library/include/libtorrent/asio/is_read_buffered.hpp62
-rw-r--r--library/include/libtorrent/asio/is_write_buffered.hpp62
-rw-r--r--library/include/libtorrent/asio/placeholders.hpp80
-rw-r--r--library/include/libtorrent/asio/read.hpp547
-rw-r--r--library/include/libtorrent/asio/read_until.hpp487
-rw-r--r--library/include/libtorrent/asio/resolver_service.hpp126
-rw-r--r--library/include/libtorrent/asio/socket_acceptor_service.hpp219
-rw-r--r--library/include/libtorrent/asio/socket_base.hpp482
-rw-r--r--library/include/libtorrent/asio/ssl.hpp26
-rwxr-xr-xlibrary/include/libtorrent/asio/ssl/basic_context.hpp467
-rw-r--r--library/include/libtorrent/asio/ssl/context.hpp35
-rwxr-xr-xlibrary/include/libtorrent/asio/ssl/context_base.hpp164
-rwxr-xr-xlibrary/include/libtorrent/asio/ssl/context_service.hpp171
-rwxr-xr-xlibrary/include/libtorrent/asio/ssl/detail/openssl_context_service.hpp396
-rwxr-xr-xlibrary/include/libtorrent/asio/ssl/detail/openssl_init.hpp128
-rwxr-xr-xlibrary/include/libtorrent/asio/ssl/detail/openssl_operation.hpp474
-rw-r--r--library/include/libtorrent/asio/ssl/detail/openssl_stream_service.hpp505
-rwxr-xr-xlibrary/include/libtorrent/asio/ssl/detail/openssl_types.hpp29
-rw-r--r--library/include/libtorrent/asio/ssl/stream.hpp509
-rwxr-xr-xlibrary/include/libtorrent/asio/ssl/stream_base.hpp60
-rw-r--r--library/include/libtorrent/asio/ssl/stream_service.hpp173
-rw-r--r--library/include/libtorrent/asio/strand.hpp166
-rw-r--r--library/include/libtorrent/asio/stream_socket_service.hpp256
-rw-r--r--library/include/libtorrent/asio/streambuf.hpp31
-rw-r--r--library/include/libtorrent/asio/system_exception.hpp198
-rw-r--r--library/include/libtorrent/asio/thread.hpp91
-rw-r--r--library/include/libtorrent/asio/time_traits.hpp78
-rw-r--r--library/include/libtorrent/asio/write.hpp543
-rw-r--r--library/include/libtorrent/aux_/allocate_resources_impl.hpp239
-rw-r--r--library/include/libtorrent/aux_/session_impl.hpp382
-rwxr-xr-xlibrary/include/libtorrent/bencode.hpp299
-rwxr-xr-xlibrary/include/libtorrent/bt_peer_connection.hpp295
-rw-r--r--library/include/libtorrent/buffer.hpp447
-rwxr-xr-xlibrary/include/libtorrent/config.hpp66
-rwxr-xr-xlibrary/include/libtorrent/debug.hpp92
-rwxr-xr-xlibrary/include/libtorrent/entry.hpp272
-rwxr-xr-xlibrary/include/libtorrent/escape_string.hpp46
-rwxr-xr-xlibrary/include/libtorrent/file.hpp130
-rwxr-xr-xlibrary/include/libtorrent/fingerprint.hpp93
-rwxr-xr-xlibrary/include/libtorrent/hasher.hpp118
-rwxr-xr-xlibrary/include/libtorrent/http_tracker_connection.hpp176
-rwxr-xr-xlibrary/include/libtorrent/identify_client.hpp58
-rwxr-xr-xlibrary/include/libtorrent/invariant_check.hpp78
-rwxr-xr-xlibrary/include/libtorrent/io.hpp140
-rw-r--r--library/include/libtorrent/ip_filter.hpp276
-rw-r--r--library/include/libtorrent/kademlia/closest_nodes.hpp86
-rw-r--r--library/include/libtorrent/kademlia/dht_tracker.hpp144
-rw-r--r--library/include/libtorrent/kademlia/find_data.hpp95
-rw-r--r--library/include/libtorrent/kademlia/logging.hpp146
-rw-r--r--library/include/libtorrent/kademlia/node.hpp185
-rw-r--r--library/include/libtorrent/kademlia/node_entry.hpp63
-rw-r--r--library/include/libtorrent/kademlia/node_id.hpp60
-rw-r--r--library/include/libtorrent/kademlia/packet_iterator.hpp95
-rw-r--r--library/include/libtorrent/kademlia/refresh.hpp159
-rw-r--r--library/include/libtorrent/kademlia/routing_table.hpp246
-rw-r--r--library/include/libtorrent/kademlia/rpc_manager.hpp194
-rw-r--r--library/include/libtorrent/kademlia/traversal_algorithm.hpp161
-rwxr-xr-xlibrary/include/libtorrent/peer.hpp63
-rwxr-xr-xlibrary/include/libtorrent/peer_connection.hpp557
-rwxr-xr-xlibrary/include/libtorrent/peer_id.hpp177
-rwxr-xr-xlibrary/include/libtorrent/peer_info.hpp106
-rw-r--r--library/include/libtorrent/peer_request.hpp49
-rw-r--r--library/include/libtorrent/piece_block_progress.hpp57
-rwxr-xr-xlibrary/include/libtorrent/piece_picker.hpp355
-rwxr-xr-xlibrary/include/libtorrent/policy.hpp245
-rw-r--r--library/include/libtorrent/random_sample.hpp72
-rwxr-xr-xlibrary/include/libtorrent/resource_request.hpp91
-rwxr-xr-xlibrary/include/libtorrent/session.hpp226
-rw-r--r--library/include/libtorrent/session_settings.hpp164
-rw-r--r--library/include/libtorrent/session_status.hpp68
-rwxr-xr-xlibrary/include/libtorrent/size_type.hpp52
-rwxr-xr-xlibrary/include/libtorrent/socket.hpp156
-rwxr-xr-xlibrary/include/libtorrent/stat.hpp193
-rwxr-xr-xlibrary/include/libtorrent/storage.hpp178
-rwxr-xr-xlibrary/include/libtorrent/torrent.hpp654
-rwxr-xr-xlibrary/include/libtorrent/torrent_handle.hpp350
-rwxr-xr-xlibrary/include/libtorrent/torrent_info.hpp242
-rwxr-xr-xlibrary/include/libtorrent/tracker_manager.hpp248
-rwxr-xr-xlibrary/include/libtorrent/udp_tracker_connection.hpp122
-rw-r--r--library/include/libtorrent/utf8.hpp160
-rwxr-xr-xlibrary/include/libtorrent/version.hpp41
-rwxr-xr-xlibrary/include/libtorrent/web_peer_connection.hpp169
-rwxr-xr-xlibrary/installit1
-rw-r--r--library/ip_filter.cpp80
-rw-r--r--library/kademlia/closest_nodes.cpp145
-rw-r--r--library/kademlia/dht_tracker.cpp905
-rw-r--r--library/kademlia/find_data.cpp156
-rw-r--r--library/kademlia/node.cpp539
-rw-r--r--library/kademlia/node_id.cpp97
-rw-r--r--library/kademlia/refresh.cpp190
-rw-r--r--library/kademlia/routing_table.cpp434
-rw-r--r--library/kademlia/rpc_manager.cpp356
-rw-r--r--library/kademlia/traversal_algorithm.cpp162
-rwxr-xr-xlibrary/makeit1
-rwxr-xr-xlibrary/peer_connection.cpp2050
-rwxr-xr-xlibrary/piece_picker.cpp1198
-rwxr-xr-xlibrary/policy.cpp1419
-rw-r--r--library/preset.txt0
-rwxr-xr-xlibrary/python-libtorrent.cpp1103
-rwxr-xr-xlibrary/session.cpp287
-rwxr-xr-xlibrary/session_impl.cpp1855
-rw-r--r--library/setup.py64
-rwxr-xr-xlibrary/sha1.cpp314
-rwxr-xr-xlibrary/stat.cpp91
-rw-r--r--library/state.txt0
-rwxr-xr-xlibrary/storage.cpp2172
-rw-r--r--library/test.py24
-rwxr-xr-xlibrary/testit5
-rwxr-xr-xlibrary/torrent.cpp2186
-rwxr-xr-xlibrary/torrent_handle.cpp729
-rwxr-xr-xlibrary/torrent_info.cpp833
-rwxr-xr-xlibrary/tracker_manager.cpp569
-rwxr-xr-xlibrary/udp_tracker_connection.cpp522
-rwxr-xr-xlibrary/web_peer_connection.cpp455
248 files changed, 68815 insertions, 0 deletions
diff --git a/library/INSTALL b/library/INSTALL
new file mode 100644
index 000000000..b2248bcf0
--- /dev/null
+++ b/library/INSTALL
@@ -0,0 +1,32 @@
+=================================
+Installation of python-libtorrent
+=================================
+
+Compile: python setup.py build
+Compile&install: sudo python setup.py install
+
+Dependencies to compile (written as Ubuntu packages): python, libboost-filesystem1.33.1, libboost-date-time1.33.1, libboost-program-options1.33.1, libboost-regex1.33.1, libboost-thread1.33.1, libc6-dev, zlib1g-dev
+
+May also depend on (not sure): python-dev, and -dev packages for all boost libs
+
+Note: Makefile.libtorrent-only.* are the makefiles from libtorrent, copies as it. They don't refer to python-libtorrent, and probably won't work in the current directory structure. However, they may help people know what dependencies etc. are needed
+
+Note: If you find mistakes here, please notify me on the project page,
+http://code.google.com/p/python-libtorrent
+
+Note: skolnick reports that the following are needed on Debian Etch:
+apt-get install python
+apt-get install libboost-filesystem1.33.1
+apt-get install libboost-date-time1.33.1
+apt-get install libboost-program-options1.33.1
+apt-get install libboost-regex1.33.1
+apt-get install libboost-thread1.33.1
+apt-get install libc6-dev
+apt-get install zlib1g-dev
+apt-get install libboost-thread-dev
+apt-get install libboost-date-time-dev
+apt-get install libboost-filesystem-dev
+apt-get install libboost-program-options-dev
+apt-get install libboost-serialization-dev
+apt-get install python-dev
+apt-get install libboost-regex-dev
diff --git a/library/LICENSE b/library/LICENSE
new file mode 100644
index 000000000..c6d326b72
--- /dev/null
+++ b/library/LICENSE
@@ -0,0 +1,13 @@
+/*
+Copyright: A. Zakai ('Kripken') <kripkensteiner@gmail.com> http://6thsenseless.blogspot.com
+
+2006-15-9
+
+This code is licensed under the terms of the GNU General Public License (GPL),
+version 2 or above; See /usr/share/common-licenses/GPL , or see
+http://www.fsf.org/licensing/licenses/gpl.html
+
+The libtorrent code is copyrighted by Arvid Norberg; see the notice in
+the libtorrent files. However, to ensure no misunderstanding: the entire project
+as a whole is licenced as mentioned above, under the GPL.
+*/
diff --git a/library/Makefile.am b/library/Makefile.am
new file mode 100644
index 000000000..8f650d061
--- /dev/null
+++ b/library/Makefile.am
@@ -0,0 +1,71 @@
+lib_LTLIBRARIES = libtorrent.la
+
+libtorrent_la_SOURCES = allocate_resources.cpp \
+entry.cpp escape_string.cpp \
+peer_connection.cpp bt_peer_connection.cpp web_peer_connection.cpp \
+piece_picker.cpp policy.cpp session.cpp session_impl.cpp sha1.cpp stat.cpp \
+storage.cpp torrent.cpp torrent_handle.cpp \
+torrent_info.cpp tracker_manager.cpp \
+http_tracker_connection.cpp udp_tracker_connection.cpp \
+alert.cpp identify_client.cpp ip_filter.cpp file.cpp \
+\
+kademlia/closest_nodes.cpp \
+kademlia/dht_tracker.cpp \
+kademlia/find_data.cpp \
+kademlia/node.cpp \
+kademlia/node_id.cpp \
+kademlia/refresh.cpp \
+kademlia/routing_table.cpp \
+kademlia/rpc_manager.cpp \
+kademlia/traversal_algorithm.cpp
+
+noinst_HEADERS = \
+$(top_srcdir)/include/libtorrent/alert.hpp \
+$(top_srcdir)/include/libtorrent/alert_types.hpp \
+$(top_srcdir)/include/libtorrent/allocate_resources.hpp \
+$(top_srcdir)/include/libtorrent/aux_/allocate_resources_impl.hpp \
+$(top_srcdir)/include/libtorrent/bencode.hpp \
+$(top_srcdir)/include/libtorrent/buffer.hpp \
+$(top_srcdir)/include/libtorrent/debug.hpp \
+$(top_srcdir)/include/libtorrent/entry.hpp \
+$(top_srcdir)/include/libtorrent/escape_string.hpp \
+$(top_srcdir)/include/libtorrent/file.hpp \
+$(top_srcdir)/include/libtorrent/fingerprint.hpp \
+$(top_srcdir)/include/libtorrent/hasher.hpp \
+$(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/invariant_check.hpp \
+$(top_srcdir)/include/libtorrent/io.hpp \
+$(top_srcdir)/include/libtorrent/ip_filter.hpp \
+$(top_srcdir)/include/libtorrent/peer.hpp \
+$(top_srcdir)/include/libtorrent/peer_connection.hpp \
+$(top_srcdir)/include/libtorrent/bt_peer_connection.hpp \
+$(top_srcdir)/include/libtorrent/web_peer_connection.hpp \
+$(top_srcdir)/include/libtorrent/peer_id.hpp \
+$(top_srcdir)/include/libtorrent/peer_info.hpp \
+$(top_srcdir)/include/libtorrent/peer_request.hpp \
+$(top_srcdir)/include/libtorrent/piece_block_progress.hpp \
+$(top_srcdir)/include/libtorrent/piece_picker.hpp \
+$(top_srcdir)/include/libtorrent/policy.hpp \
+$(top_srcdir)/include/libtorrent/resource_request.hpp \
+$(top_srcdir)/include/libtorrent/session.hpp \
+$(top_srcdir)/include/libtorrent/aux_/session_impl.hpp \
+$(top_srcdir)/include/libtorrent/size_type.hpp \
+$(top_srcdir)/include/libtorrent/socket.hpp \
+$(top_srcdir)/include/libtorrent/stat.hpp \
+$(top_srcdir)/include/libtorrent/storage.hpp \
+$(top_srcdir)/include/libtorrent/torrent.hpp \
+$(top_srcdir)/include/libtorrent/torrent_handle.hpp \
+$(top_srcdir)/include/libtorrent/torrent_info.hpp \
+$(top_srcdir)/include/libtorrent/tracker_manager.hpp \
+$(top_srcdir)/include/libtorrent/udp_tracker_connection.hpp \
+$(top_srcdir)/include/libtorrent/utf8.hpp \
+$(top_srcdir)/include/libtorrent/version.hpp
+
+
+libtorrent_la_LDFLAGS = $(LDFLAGS) -version-info 1:0:1
+libtorrent_la_LIBADD = @ZLIB@ -l@BOOST_DATE_TIME_LIB@ -l@BOOST_FILESYSTEM_LIB@ -l@BOOST_THREAD_LIB@ @PTHREAD_LIBS@
+
+AM_CXXFLAGS= -ftemplate-depth-50 -I$(top_srcdir)/include -I$(top_srcdir)/include/libtorrent @ZLIBINCL@ @DEBUGFLAGS@ @PTHREAD_CFLAGS@
+AM_LDFLAGS= $(LDFLAGS) -l@BOOST_DATE_TIME_LIB@ -l@BOOST_FILESYSTEM_LIB@ -l@BOOST_THREAD_LIB@ @PTHREAD_LIBS@
diff --git a/library/Makefile.in b/library/Makefile.in
new file mode 100644
index 000000000..dfdb17432
--- /dev/null
+++ b/library/Makefile.in
@@ -0,0 +1,641 @@
+# Makefile.in generated by automake 1.9.6 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005 Free Software Foundation, Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+top_builddir = ..
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+INSTALL = @INSTALL@
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = src
+DIST_COMMON = $(noinst_HEADERS) $(srcdir)/Makefile.am \
+ $(srcdir)/Makefile.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/ac_cxx_namespaces.m4 \
+ $(top_srcdir)/m4/acx_pthread.m4 \
+ $(top_srcdir)/m4/ax_boost_date-time.m4 \
+ $(top_srcdir)/m4/ax_boost_filesystem.m4 \
+ $(top_srcdir)/m4/ax_boost_program_options.m4 \
+ $(top_srcdir)/m4/ax_boost_regex.m4 \
+ $(top_srcdir)/m4/ax_boost_thread.m4 $(top_srcdir)/configure.in
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = `echo $$p | sed -e 's|^.*/||'`;
+am__installdirs = "$(DESTDIR)$(libdir)"
+libLTLIBRARIES_INSTALL = $(INSTALL)
+LTLIBRARIES = $(lib_LTLIBRARIES)
+libtorrent_la_DEPENDENCIES =
+am_libtorrent_la_OBJECTS = allocate_resources.lo entry.lo \
+ escape_string.lo peer_connection.lo bt_peer_connection.lo \
+ web_peer_connection.lo piece_picker.lo policy.lo session.lo \
+ session_impl.lo sha1.lo stat.lo storage.lo torrent.lo \
+ torrent_handle.lo torrent_info.lo tracker_manager.lo \
+ http_tracker_connection.lo udp_tracker_connection.lo alert.lo \
+ identify_client.lo ip_filter.lo file.lo closest_nodes.lo \
+ dht_tracker.lo find_data.lo node.lo node_id.lo refresh.lo \
+ routing_table.lo rpc_manager.lo traversal_algorithm.lo
+libtorrent_la_OBJECTS = $(am_libtorrent_la_OBJECTS)
+DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+ $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS)
+LTCXXCOMPILE = $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CXXFLAGS) $(CXXFLAGS)
+CXXLD = $(CXX)
+CXXLINK = $(LIBTOOL) --tag=CXX --mode=link $(CXXLD) $(AM_CXXFLAGS) \
+ $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
+SOURCES = $(libtorrent_la_SOURCES)
+DIST_SOURCES = $(libtorrent_la_SOURCES)
+HEADERS = $(noinst_HEADERS)
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMDEP_FALSE = @AMDEP_FALSE@
+AMDEP_TRUE = @AMDEP_TRUE@
+AMTAR = @AMTAR@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BOOST_DATE_TIME_LIB = @BOOST_DATE_TIME_LIB@
+BOOST_FILESYSTEM_LIB = @BOOST_FILESYSTEM_LIB@
+BOOST_PROGRAM_OPTIONS_LIB = @BOOST_PROGRAM_OPTIONS_LIB@
+BOOST_REGEX_LIB = @BOOST_REGEX_LIB@
+BOOST_THREAD_LIB = @BOOST_THREAD_LIB@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CLIENT_TEST_BIN = @CLIENT_TEST_BIN@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEBUGFLAGS = @DEBUGFLAGS@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+ECHO = @ECHO@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXAMPLESDIR = @EXAMPLESDIR@
+EXEEXT = @EXEEXT@
+F77 = @F77@
+FFLAGS = @FFLAGS@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LDFLAGS = @LDFLAGS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+MAKEINFO = @MAKEINFO@
+OBJEXT = @OBJEXT@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PTHREAD_CC = @PTHREAD_CC@
+PTHREAD_CFLAGS = @PTHREAD_CFLAGS@
+PTHREAD_LIBS = @PTHREAD_LIBS@
+RANLIB = @RANLIB@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+VERSION = @VERSION@
+ZLIB = @ZLIB@
+ZLIBDIR = @ZLIBDIR@
+ZLIBINCL = @ZLIBINCL@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_F77 = @ac_ct_F77@
+ac_ct_RANLIB = @ac_ct_RANLIB@
+ac_ct_STRIP = @ac_ct_STRIP@
+acx_pthread_config = @acx_pthread_config@
+am__fastdepCC_FALSE = @am__fastdepCC_FALSE@
+am__fastdepCC_TRUE = @am__fastdepCC_TRUE@
+am__fastdepCXX_FALSE = @am__fastdepCXX_FALSE@
+am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+datadir = @datadir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+lib_LTLIBRARIES = libtorrent.la
+libtorrent_la_SOURCES = allocate_resources.cpp \
+entry.cpp escape_string.cpp \
+peer_connection.cpp bt_peer_connection.cpp web_peer_connection.cpp \
+piece_picker.cpp policy.cpp session.cpp session_impl.cpp sha1.cpp stat.cpp \
+storage.cpp torrent.cpp torrent_handle.cpp \
+torrent_info.cpp tracker_manager.cpp \
+http_tracker_connection.cpp udp_tracker_connection.cpp \
+alert.cpp identify_client.cpp ip_filter.cpp file.cpp \
+\
+kademlia/closest_nodes.cpp \
+kademlia/dht_tracker.cpp \
+kademlia/find_data.cpp \
+kademlia/node.cpp \
+kademlia/node_id.cpp \
+kademlia/refresh.cpp \
+kademlia/routing_table.cpp \
+kademlia/rpc_manager.cpp \
+kademlia/traversal_algorithm.cpp
+
+noinst_HEADERS = \
+$(top_srcdir)/include/libtorrent/alert.hpp \
+$(top_srcdir)/include/libtorrent/alert_types.hpp \
+$(top_srcdir)/include/libtorrent/allocate_resources.hpp \
+$(top_srcdir)/include/libtorrent/aux_/allocate_resources_impl.hpp \
+$(top_srcdir)/include/libtorrent/bencode.hpp \
+$(top_srcdir)/include/libtorrent/buffer.hpp \
+$(top_srcdir)/include/libtorrent/debug.hpp \
+$(top_srcdir)/include/libtorrent/entry.hpp \
+$(top_srcdir)/include/libtorrent/escape_string.hpp \
+$(top_srcdir)/include/libtorrent/file.hpp \
+$(top_srcdir)/include/libtorrent/fingerprint.hpp \
+$(top_srcdir)/include/libtorrent/hasher.hpp \
+$(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/invariant_check.hpp \
+$(top_srcdir)/include/libtorrent/io.hpp \
+$(top_srcdir)/include/libtorrent/ip_filter.hpp \
+$(top_srcdir)/include/libtorrent/peer.hpp \
+$(top_srcdir)/include/libtorrent/peer_connection.hpp \
+$(top_srcdir)/include/libtorrent/bt_peer_connection.hpp \
+$(top_srcdir)/include/libtorrent/web_peer_connection.hpp \
+$(top_srcdir)/include/libtorrent/peer_id.hpp \
+$(top_srcdir)/include/libtorrent/peer_info.hpp \
+$(top_srcdir)/include/libtorrent/peer_request.hpp \
+$(top_srcdir)/include/libtorrent/piece_block_progress.hpp \
+$(top_srcdir)/include/libtorrent/piece_picker.hpp \
+$(top_srcdir)/include/libtorrent/policy.hpp \
+$(top_srcdir)/include/libtorrent/resource_request.hpp \
+$(top_srcdir)/include/libtorrent/session.hpp \
+$(top_srcdir)/include/libtorrent/aux_/session_impl.hpp \
+$(top_srcdir)/include/libtorrent/size_type.hpp \
+$(top_srcdir)/include/libtorrent/socket.hpp \
+$(top_srcdir)/include/libtorrent/stat.hpp \
+$(top_srcdir)/include/libtorrent/storage.hpp \
+$(top_srcdir)/include/libtorrent/torrent.hpp \
+$(top_srcdir)/include/libtorrent/torrent_handle.hpp \
+$(top_srcdir)/include/libtorrent/torrent_info.hpp \
+$(top_srcdir)/include/libtorrent/tracker_manager.hpp \
+$(top_srcdir)/include/libtorrent/udp_tracker_connection.hpp \
+$(top_srcdir)/include/libtorrent/utf8.hpp \
+$(top_srcdir)/include/libtorrent/version.hpp
+
+libtorrent_la_LDFLAGS = $(LDFLAGS) -version-info 1:0:1
+libtorrent_la_LIBADD = @ZLIB@ -l@BOOST_DATE_TIME_LIB@ -l@BOOST_FILESYSTEM_LIB@ -l@BOOST_THREAD_LIB@ @PTHREAD_LIBS@
+AM_CXXFLAGS = -ftemplate-depth-50 -I$(top_srcdir)/include -I$(top_srcdir)/include/libtorrent @ZLIBINCL@ @DEBUGFLAGS@ @PTHREAD_CFLAGS@
+AM_LDFLAGS = $(LDFLAGS) -l@BOOST_DATE_TIME_LIB@ -l@BOOST_FILESYSTEM_LIB@ -l@BOOST_THREAD_LIB@ @PTHREAD_LIBS@
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .cpp .lo .o .obj
+$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \
+ && exit 0; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/Makefile'; \
+ cd $(top_srcdir) && \
+ $(AUTOMAKE) --gnu src/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+install-libLTLIBRARIES: $(lib_LTLIBRARIES)
+ @$(NORMAL_INSTALL)
+ test -z "$(libdir)" || $(mkdir_p) "$(DESTDIR)$(libdir)"
+ @list='$(lib_LTLIBRARIES)'; for p in $$list; do \
+ if test -f $$p; then \
+ f=$(am__strip_dir) \
+ echo " $(LIBTOOL) --mode=install $(libLTLIBRARIES_INSTALL) $(INSTALL_STRIP_FLAG) '$$p' '$(DESTDIR)$(libdir)/$$f'"; \
+ $(LIBTOOL) --mode=install $(libLTLIBRARIES_INSTALL) $(INSTALL_STRIP_FLAG) "$$p" "$(DESTDIR)$(libdir)/$$f"; \
+ else :; fi; \
+ done
+
+uninstall-libLTLIBRARIES:
+ @$(NORMAL_UNINSTALL)
+ @set -x; list='$(lib_LTLIBRARIES)'; for p in $$list; do \
+ p=$(am__strip_dir) \
+ echo " $(LIBTOOL) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$p'"; \
+ $(LIBTOOL) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$p"; \
+ done
+
+clean-libLTLIBRARIES:
+ -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES)
+ @list='$(lib_LTLIBRARIES)'; for p in $$list; do \
+ dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
+ test "$$dir" != "$$p" || dir=.; \
+ echo "rm -f \"$${dir}/so_locations\""; \
+ rm -f "$${dir}/so_locations"; \
+ done
+libtorrent.la: $(libtorrent_la_OBJECTS) $(libtorrent_la_DEPENDENCIES)
+ $(CXXLINK) -rpath $(libdir) $(libtorrent_la_LDFLAGS) $(libtorrent_la_OBJECTS) $(libtorrent_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/alert.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/allocate_resources.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bt_peer_connection.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/closest_nodes.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dht_tracker.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/entry.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/escape_string.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/file.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/find_data.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/http_tracker_connection.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/identify_client.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ip_filter.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/node.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/node_id.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/peer_connection.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/piece_picker.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/policy.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refresh.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/routing_table.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rpc_manager.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/session.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/session_impl.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sha1.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stat.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/storage.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/torrent.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/torrent_handle.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/torrent_info.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tracker_manager.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/traversal_algorithm.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/udp_tracker_connection.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/web_peer_connection.Plo@am__quote@
+
+.cpp.o:
+@am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \
+@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ $<
+
+.cpp.obj:
+@am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \
+@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.cpp.lo:
+@am__fastdepCXX_TRUE@ if $(LTCXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \
+@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Plo"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(LTCXXCOMPILE) -c -o $@ $<
+
+closest_nodes.lo: kademlia/closest_nodes.cpp
+@am__fastdepCXX_TRUE@ if $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT closest_nodes.lo -MD -MP -MF "$(DEPDIR)/closest_nodes.Tpo" -c -o closest_nodes.lo `test -f 'kademlia/closest_nodes.cpp' || echo '$(srcdir)/'`kademlia/closest_nodes.cpp; \
+@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/closest_nodes.Tpo" "$(DEPDIR)/closest_nodes.Plo"; else rm -f "$(DEPDIR)/closest_nodes.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='kademlia/closest_nodes.cpp' object='closest_nodes.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o closest_nodes.lo `test -f 'kademlia/closest_nodes.cpp' || echo '$(srcdir)/'`kademlia/closest_nodes.cpp
+
+dht_tracker.lo: kademlia/dht_tracker.cpp
+@am__fastdepCXX_TRUE@ if $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT dht_tracker.lo -MD -MP -MF "$(DEPDIR)/dht_tracker.Tpo" -c -o dht_tracker.lo `test -f 'kademlia/dht_tracker.cpp' || echo '$(srcdir)/'`kademlia/dht_tracker.cpp; \
+@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/dht_tracker.Tpo" "$(DEPDIR)/dht_tracker.Plo"; else rm -f "$(DEPDIR)/dht_tracker.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='kademlia/dht_tracker.cpp' object='dht_tracker.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o dht_tracker.lo `test -f 'kademlia/dht_tracker.cpp' || echo '$(srcdir)/'`kademlia/dht_tracker.cpp
+
+find_data.lo: kademlia/find_data.cpp
+@am__fastdepCXX_TRUE@ if $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT find_data.lo -MD -MP -MF "$(DEPDIR)/find_data.Tpo" -c -o find_data.lo `test -f 'kademlia/find_data.cpp' || echo '$(srcdir)/'`kademlia/find_data.cpp; \
+@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/find_data.Tpo" "$(DEPDIR)/find_data.Plo"; else rm -f "$(DEPDIR)/find_data.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='kademlia/find_data.cpp' object='find_data.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o find_data.lo `test -f 'kademlia/find_data.cpp' || echo '$(srcdir)/'`kademlia/find_data.cpp
+
+node.lo: kademlia/node.cpp
+@am__fastdepCXX_TRUE@ if $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT node.lo -MD -MP -MF "$(DEPDIR)/node.Tpo" -c -o node.lo `test -f 'kademlia/node.cpp' || echo '$(srcdir)/'`kademlia/node.cpp; \
+@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/node.Tpo" "$(DEPDIR)/node.Plo"; else rm -f "$(DEPDIR)/node.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='kademlia/node.cpp' object='node.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o node.lo `test -f 'kademlia/node.cpp' || echo '$(srcdir)/'`kademlia/node.cpp
+
+node_id.lo: kademlia/node_id.cpp
+@am__fastdepCXX_TRUE@ if $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT node_id.lo -MD -MP -MF "$(DEPDIR)/node_id.Tpo" -c -o node_id.lo `test -f 'kademlia/node_id.cpp' || echo '$(srcdir)/'`kademlia/node_id.cpp; \
+@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/node_id.Tpo" "$(DEPDIR)/node_id.Plo"; else rm -f "$(DEPDIR)/node_id.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='kademlia/node_id.cpp' object='node_id.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o node_id.lo `test -f 'kademlia/node_id.cpp' || echo '$(srcdir)/'`kademlia/node_id.cpp
+
+refresh.lo: kademlia/refresh.cpp
+@am__fastdepCXX_TRUE@ if $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT refresh.lo -MD -MP -MF "$(DEPDIR)/refresh.Tpo" -c -o refresh.lo `test -f 'kademlia/refresh.cpp' || echo '$(srcdir)/'`kademlia/refresh.cpp; \
+@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/refresh.Tpo" "$(DEPDIR)/refresh.Plo"; else rm -f "$(DEPDIR)/refresh.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='kademlia/refresh.cpp' object='refresh.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o refresh.lo `test -f 'kademlia/refresh.cpp' || echo '$(srcdir)/'`kademlia/refresh.cpp
+
+routing_table.lo: kademlia/routing_table.cpp
+@am__fastdepCXX_TRUE@ if $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT routing_table.lo -MD -MP -MF "$(DEPDIR)/routing_table.Tpo" -c -o routing_table.lo `test -f 'kademlia/routing_table.cpp' || echo '$(srcdir)/'`kademlia/routing_table.cpp; \
+@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/routing_table.Tpo" "$(DEPDIR)/routing_table.Plo"; else rm -f "$(DEPDIR)/routing_table.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='kademlia/routing_table.cpp' object='routing_table.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o routing_table.lo `test -f 'kademlia/routing_table.cpp' || echo '$(srcdir)/'`kademlia/routing_table.cpp
+
+rpc_manager.lo: kademlia/rpc_manager.cpp
+@am__fastdepCXX_TRUE@ if $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT rpc_manager.lo -MD -MP -MF "$(DEPDIR)/rpc_manager.Tpo" -c -o rpc_manager.lo `test -f 'kademlia/rpc_manager.cpp' || echo '$(srcdir)/'`kademlia/rpc_manager.cpp; \
+@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/rpc_manager.Tpo" "$(DEPDIR)/rpc_manager.Plo"; else rm -f "$(DEPDIR)/rpc_manager.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='kademlia/rpc_manager.cpp' object='rpc_manager.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o rpc_manager.lo `test -f 'kademlia/rpc_manager.cpp' || echo '$(srcdir)/'`kademlia/rpc_manager.cpp
+
+traversal_algorithm.lo: kademlia/traversal_algorithm.cpp
+@am__fastdepCXX_TRUE@ if $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT traversal_algorithm.lo -MD -MP -MF "$(DEPDIR)/traversal_algorithm.Tpo" -c -o traversal_algorithm.lo `test -f 'kademlia/traversal_algorithm.cpp' || echo '$(srcdir)/'`kademlia/traversal_algorithm.cpp; \
+@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/traversal_algorithm.Tpo" "$(DEPDIR)/traversal_algorithm.Plo"; else rm -f "$(DEPDIR)/traversal_algorithm.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='kademlia/traversal_algorithm.cpp' object='traversal_algorithm.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o traversal_algorithm.lo `test -f 'kademlia/traversal_algorithm.cpp' || echo '$(srcdir)/'`kademlia/traversal_algorithm.cpp
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+distclean-libtool:
+ -rm -f libtool
+uninstall-info-am:
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ mkid -fID $$unique
+tags: TAGS
+
+TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ tags=; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$tags $$unique; \
+ fi
+ctags: CTAGS
+CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ tags=; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ test -z "$(CTAGS_ARGS)$$tags$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$tags $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && cd $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) $$here
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+ $(mkdir_p) $(distdir)/../include/libtorrent $(distdir)/../include/libtorrent/aux_
+ @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \
+ list='$(DISTFILES)'; for file in $$list; do \
+ case $$file in \
+ $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \
+ $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \
+ esac; \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test "$$dir" != "$$file" && test "$$dir" != "."; then \
+ dir="/$$dir"; \
+ $(mkdir_p) "$(distdir)$$dir"; \
+ else \
+ dir=''; \
+ fi; \
+ if test -d $$d/$$file; then \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \
+ fi; \
+ cp -pR $$d/$$file $(distdir)$$dir || exit 1; \
+ else \
+ test -f $(distdir)/$$file \
+ || cp -p $$d/$$file $(distdir)/$$file \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(LTLIBRARIES) $(HEADERS)
+installdirs:
+ for dir in "$(DESTDIR)$(libdir)"; do \
+ test -z "$$dir" || $(mkdir_p) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ `test -z '$(STRIP)' || \
+ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-libtool distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-exec-am: install-libLTLIBRARIES
+
+install-info: install-info-am
+
+install-man:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-info-am uninstall-libLTLIBRARIES
+
+.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \
+ clean-libLTLIBRARIES clean-libtool ctags distclean \
+ distclean-compile distclean-generic distclean-libtool \
+ distclean-tags distdir dvi dvi-am html html-am info info-am \
+ install install-am install-data install-data-am install-exec \
+ install-exec-am install-info install-info-am \
+ install-libLTLIBRARIES install-man install-strip installcheck \
+ installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags uninstall uninstall-am uninstall-info-am \
+ uninstall-libLTLIBRARIES
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/library/Makefile.libtorrent-only.am b/library/Makefile.libtorrent-only.am
new file mode 100644
index 000000000..8f650d061
--- /dev/null
+++ b/library/Makefile.libtorrent-only.am
@@ -0,0 +1,71 @@
+lib_LTLIBRARIES = libtorrent.la
+
+libtorrent_la_SOURCES = allocate_resources.cpp \
+entry.cpp escape_string.cpp \
+peer_connection.cpp bt_peer_connection.cpp web_peer_connection.cpp \
+piece_picker.cpp policy.cpp session.cpp session_impl.cpp sha1.cpp stat.cpp \
+storage.cpp torrent.cpp torrent_handle.cpp \
+torrent_info.cpp tracker_manager.cpp \
+http_tracker_connection.cpp udp_tracker_connection.cpp \
+alert.cpp identify_client.cpp ip_filter.cpp file.cpp \
+\
+kademlia/closest_nodes.cpp \
+kademlia/dht_tracker.cpp \
+kademlia/find_data.cpp \
+kademlia/node.cpp \
+kademlia/node_id.cpp \
+kademlia/refresh.cpp \
+kademlia/routing_table.cpp \
+kademlia/rpc_manager.cpp \
+kademlia/traversal_algorithm.cpp
+
+noinst_HEADERS = \
+$(top_srcdir)/include/libtorrent/alert.hpp \
+$(top_srcdir)/include/libtorrent/alert_types.hpp \
+$(top_srcdir)/include/libtorrent/allocate_resources.hpp \
+$(top_srcdir)/include/libtorrent/aux_/allocate_resources_impl.hpp \
+$(top_srcdir)/include/libtorrent/bencode.hpp \
+$(top_srcdir)/include/libtorrent/buffer.hpp \
+$(top_srcdir)/include/libtorrent/debug.hpp \
+$(top_srcdir)/include/libtorrent/entry.hpp \
+$(top_srcdir)/include/libtorrent/escape_string.hpp \
+$(top_srcdir)/include/libtorrent/file.hpp \
+$(top_srcdir)/include/libtorrent/fingerprint.hpp \
+$(top_srcdir)/include/libtorrent/hasher.hpp \
+$(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/invariant_check.hpp \
+$(top_srcdir)/include/libtorrent/io.hpp \
+$(top_srcdir)/include/libtorrent/ip_filter.hpp \
+$(top_srcdir)/include/libtorrent/peer.hpp \
+$(top_srcdir)/include/libtorrent/peer_connection.hpp \
+$(top_srcdir)/include/libtorrent/bt_peer_connection.hpp \
+$(top_srcdir)/include/libtorrent/web_peer_connection.hpp \
+$(top_srcdir)/include/libtorrent/peer_id.hpp \
+$(top_srcdir)/include/libtorrent/peer_info.hpp \
+$(top_srcdir)/include/libtorrent/peer_request.hpp \
+$(top_srcdir)/include/libtorrent/piece_block_progress.hpp \
+$(top_srcdir)/include/libtorrent/piece_picker.hpp \
+$(top_srcdir)/include/libtorrent/policy.hpp \
+$(top_srcdir)/include/libtorrent/resource_request.hpp \
+$(top_srcdir)/include/libtorrent/session.hpp \
+$(top_srcdir)/include/libtorrent/aux_/session_impl.hpp \
+$(top_srcdir)/include/libtorrent/size_type.hpp \
+$(top_srcdir)/include/libtorrent/socket.hpp \
+$(top_srcdir)/include/libtorrent/stat.hpp \
+$(top_srcdir)/include/libtorrent/storage.hpp \
+$(top_srcdir)/include/libtorrent/torrent.hpp \
+$(top_srcdir)/include/libtorrent/torrent_handle.hpp \
+$(top_srcdir)/include/libtorrent/torrent_info.hpp \
+$(top_srcdir)/include/libtorrent/tracker_manager.hpp \
+$(top_srcdir)/include/libtorrent/udp_tracker_connection.hpp \
+$(top_srcdir)/include/libtorrent/utf8.hpp \
+$(top_srcdir)/include/libtorrent/version.hpp
+
+
+libtorrent_la_LDFLAGS = $(LDFLAGS) -version-info 1:0:1
+libtorrent_la_LIBADD = @ZLIB@ -l@BOOST_DATE_TIME_LIB@ -l@BOOST_FILESYSTEM_LIB@ -l@BOOST_THREAD_LIB@ @PTHREAD_LIBS@
+
+AM_CXXFLAGS= -ftemplate-depth-50 -I$(top_srcdir)/include -I$(top_srcdir)/include/libtorrent @ZLIBINCL@ @DEBUGFLAGS@ @PTHREAD_CFLAGS@
+AM_LDFLAGS= $(LDFLAGS) -l@BOOST_DATE_TIME_LIB@ -l@BOOST_FILESYSTEM_LIB@ -l@BOOST_THREAD_LIB@ @PTHREAD_LIBS@
diff --git a/library/Makefile.libtorrent-only.in b/library/Makefile.libtorrent-only.in
new file mode 100644
index 000000000..dfdb17432
--- /dev/null
+++ b/library/Makefile.libtorrent-only.in
@@ -0,0 +1,641 @@
+# Makefile.in generated by automake 1.9.6 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005 Free Software Foundation, Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+top_builddir = ..
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+INSTALL = @INSTALL@
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = src
+DIST_COMMON = $(noinst_HEADERS) $(srcdir)/Makefile.am \
+ $(srcdir)/Makefile.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/ac_cxx_namespaces.m4 \
+ $(top_srcdir)/m4/acx_pthread.m4 \
+ $(top_srcdir)/m4/ax_boost_date-time.m4 \
+ $(top_srcdir)/m4/ax_boost_filesystem.m4 \
+ $(top_srcdir)/m4/ax_boost_program_options.m4 \
+ $(top_srcdir)/m4/ax_boost_regex.m4 \
+ $(top_srcdir)/m4/ax_boost_thread.m4 $(top_srcdir)/configure.in
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = `echo $$p | sed -e 's|^.*/||'`;
+am__installdirs = "$(DESTDIR)$(libdir)"
+libLTLIBRARIES_INSTALL = $(INSTALL)
+LTLIBRARIES = $(lib_LTLIBRARIES)
+libtorrent_la_DEPENDENCIES =
+am_libtorrent_la_OBJECTS = allocate_resources.lo entry.lo \
+ escape_string.lo peer_connection.lo bt_peer_connection.lo \
+ web_peer_connection.lo piece_picker.lo policy.lo session.lo \
+ session_impl.lo sha1.lo stat.lo storage.lo torrent.lo \
+ torrent_handle.lo torrent_info.lo tracker_manager.lo \
+ http_tracker_connection.lo udp_tracker_connection.lo alert.lo \
+ identify_client.lo ip_filter.lo file.lo closest_nodes.lo \
+ dht_tracker.lo find_data.lo node.lo node_id.lo refresh.lo \
+ routing_table.lo rpc_manager.lo traversal_algorithm.lo
+libtorrent_la_OBJECTS = $(am_libtorrent_la_OBJECTS)
+DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+ $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS)
+LTCXXCOMPILE = $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CXXFLAGS) $(CXXFLAGS)
+CXXLD = $(CXX)
+CXXLINK = $(LIBTOOL) --tag=CXX --mode=link $(CXXLD) $(AM_CXXFLAGS) \
+ $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
+SOURCES = $(libtorrent_la_SOURCES)
+DIST_SOURCES = $(libtorrent_la_SOURCES)
+HEADERS = $(noinst_HEADERS)
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMDEP_FALSE = @AMDEP_FALSE@
+AMDEP_TRUE = @AMDEP_TRUE@
+AMTAR = @AMTAR@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BOOST_DATE_TIME_LIB = @BOOST_DATE_TIME_LIB@
+BOOST_FILESYSTEM_LIB = @BOOST_FILESYSTEM_LIB@
+BOOST_PROGRAM_OPTIONS_LIB = @BOOST_PROGRAM_OPTIONS_LIB@
+BOOST_REGEX_LIB = @BOOST_REGEX_LIB@
+BOOST_THREAD_LIB = @BOOST_THREAD_LIB@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CLIENT_TEST_BIN = @CLIENT_TEST_BIN@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEBUGFLAGS = @DEBUGFLAGS@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+ECHO = @ECHO@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXAMPLESDIR = @EXAMPLESDIR@
+EXEEXT = @EXEEXT@
+F77 = @F77@
+FFLAGS = @FFLAGS@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LDFLAGS = @LDFLAGS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+MAKEINFO = @MAKEINFO@
+OBJEXT = @OBJEXT@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PTHREAD_CC = @PTHREAD_CC@
+PTHREAD_CFLAGS = @PTHREAD_CFLAGS@
+PTHREAD_LIBS = @PTHREAD_LIBS@
+RANLIB = @RANLIB@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+VERSION = @VERSION@
+ZLIB = @ZLIB@
+ZLIBDIR = @ZLIBDIR@
+ZLIBINCL = @ZLIBINCL@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_F77 = @ac_ct_F77@
+ac_ct_RANLIB = @ac_ct_RANLIB@
+ac_ct_STRIP = @ac_ct_STRIP@
+acx_pthread_config = @acx_pthread_config@
+am__fastdepCC_FALSE = @am__fastdepCC_FALSE@
+am__fastdepCC_TRUE = @am__fastdepCC_TRUE@
+am__fastdepCXX_FALSE = @am__fastdepCXX_FALSE@
+am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+datadir = @datadir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+lib_LTLIBRARIES = libtorrent.la
+libtorrent_la_SOURCES = allocate_resources.cpp \
+entry.cpp escape_string.cpp \
+peer_connection.cpp bt_peer_connection.cpp web_peer_connection.cpp \
+piece_picker.cpp policy.cpp session.cpp session_impl.cpp sha1.cpp stat.cpp \
+storage.cpp torrent.cpp torrent_handle.cpp \
+torrent_info.cpp tracker_manager.cpp \
+http_tracker_connection.cpp udp_tracker_connection.cpp \
+alert.cpp identify_client.cpp ip_filter.cpp file.cpp \
+\
+kademlia/closest_nodes.cpp \
+kademlia/dht_tracker.cpp \
+kademlia/find_data.cpp \
+kademlia/node.cpp \
+kademlia/node_id.cpp \
+kademlia/refresh.cpp \
+kademlia/routing_table.cpp \
+kademlia/rpc_manager.cpp \
+kademlia/traversal_algorithm.cpp
+
+noinst_HEADERS = \
+$(top_srcdir)/include/libtorrent/alert.hpp \
+$(top_srcdir)/include/libtorrent/alert_types.hpp \
+$(top_srcdir)/include/libtorrent/allocate_resources.hpp \
+$(top_srcdir)/include/libtorrent/aux_/allocate_resources_impl.hpp \
+$(top_srcdir)/include/libtorrent/bencode.hpp \
+$(top_srcdir)/include/libtorrent/buffer.hpp \
+$(top_srcdir)/include/libtorrent/debug.hpp \
+$(top_srcdir)/include/libtorrent/entry.hpp \
+$(top_srcdir)/include/libtorrent/escape_string.hpp \
+$(top_srcdir)/include/libtorrent/file.hpp \
+$(top_srcdir)/include/libtorrent/fingerprint.hpp \
+$(top_srcdir)/include/libtorrent/hasher.hpp \
+$(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/invariant_check.hpp \
+$(top_srcdir)/include/libtorrent/io.hpp \
+$(top_srcdir)/include/libtorrent/ip_filter.hpp \
+$(top_srcdir)/include/libtorrent/peer.hpp \
+$(top_srcdir)/include/libtorrent/peer_connection.hpp \
+$(top_srcdir)/include/libtorrent/bt_peer_connection.hpp \
+$(top_srcdir)/include/libtorrent/web_peer_connection.hpp \
+$(top_srcdir)/include/libtorrent/peer_id.hpp \
+$(top_srcdir)/include/libtorrent/peer_info.hpp \
+$(top_srcdir)/include/libtorrent/peer_request.hpp \
+$(top_srcdir)/include/libtorrent/piece_block_progress.hpp \
+$(top_srcdir)/include/libtorrent/piece_picker.hpp \
+$(top_srcdir)/include/libtorrent/policy.hpp \
+$(top_srcdir)/include/libtorrent/resource_request.hpp \
+$(top_srcdir)/include/libtorrent/session.hpp \
+$(top_srcdir)/include/libtorrent/aux_/session_impl.hpp \
+$(top_srcdir)/include/libtorrent/size_type.hpp \
+$(top_srcdir)/include/libtorrent/socket.hpp \
+$(top_srcdir)/include/libtorrent/stat.hpp \
+$(top_srcdir)/include/libtorrent/storage.hpp \
+$(top_srcdir)/include/libtorrent/torrent.hpp \
+$(top_srcdir)/include/libtorrent/torrent_handle.hpp \
+$(top_srcdir)/include/libtorrent/torrent_info.hpp \
+$(top_srcdir)/include/libtorrent/tracker_manager.hpp \
+$(top_srcdir)/include/libtorrent/udp_tracker_connection.hpp \
+$(top_srcdir)/include/libtorrent/utf8.hpp \
+$(top_srcdir)/include/libtorrent/version.hpp
+
+libtorrent_la_LDFLAGS = $(LDFLAGS) -version-info 1:0:1
+libtorrent_la_LIBADD = @ZLIB@ -l@BOOST_DATE_TIME_LIB@ -l@BOOST_FILESYSTEM_LIB@ -l@BOOST_THREAD_LIB@ @PTHREAD_LIBS@
+AM_CXXFLAGS = -ftemplate-depth-50 -I$(top_srcdir)/include -I$(top_srcdir)/include/libtorrent @ZLIBINCL@ @DEBUGFLAGS@ @PTHREAD_CFLAGS@
+AM_LDFLAGS = $(LDFLAGS) -l@BOOST_DATE_TIME_LIB@ -l@BOOST_FILESYSTEM_LIB@ -l@BOOST_THREAD_LIB@ @PTHREAD_LIBS@
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .cpp .lo .o .obj
+$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \
+ && exit 0; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/Makefile'; \
+ cd $(top_srcdir) && \
+ $(AUTOMAKE) --gnu src/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+install-libLTLIBRARIES: $(lib_LTLIBRARIES)
+ @$(NORMAL_INSTALL)
+ test -z "$(libdir)" || $(mkdir_p) "$(DESTDIR)$(libdir)"
+ @list='$(lib_LTLIBRARIES)'; for p in $$list; do \
+ if test -f $$p; then \
+ f=$(am__strip_dir) \
+ echo " $(LIBTOOL) --mode=install $(libLTLIBRARIES_INSTALL) $(INSTALL_STRIP_FLAG) '$$p' '$(DESTDIR)$(libdir)/$$f'"; \
+ $(LIBTOOL) --mode=install $(libLTLIBRARIES_INSTALL) $(INSTALL_STRIP_FLAG) "$$p" "$(DESTDIR)$(libdir)/$$f"; \
+ else :; fi; \
+ done
+
+uninstall-libLTLIBRARIES:
+ @$(NORMAL_UNINSTALL)
+ @set -x; list='$(lib_LTLIBRARIES)'; for p in $$list; do \
+ p=$(am__strip_dir) \
+ echo " $(LIBTOOL) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$p'"; \
+ $(LIBTOOL) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$p"; \
+ done
+
+clean-libLTLIBRARIES:
+ -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES)
+ @list='$(lib_LTLIBRARIES)'; for p in $$list; do \
+ dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
+ test "$$dir" != "$$p" || dir=.; \
+ echo "rm -f \"$${dir}/so_locations\""; \
+ rm -f "$${dir}/so_locations"; \
+ done
+libtorrent.la: $(libtorrent_la_OBJECTS) $(libtorrent_la_DEPENDENCIES)
+ $(CXXLINK) -rpath $(libdir) $(libtorrent_la_LDFLAGS) $(libtorrent_la_OBJECTS) $(libtorrent_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/alert.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/allocate_resources.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bt_peer_connection.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/closest_nodes.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dht_tracker.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/entry.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/escape_string.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/file.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/find_data.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/http_tracker_connection.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/identify_client.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ip_filter.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/node.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/node_id.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/peer_connection.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/piece_picker.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/policy.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refresh.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/routing_table.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rpc_manager.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/session.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/session_impl.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sha1.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stat.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/storage.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/torrent.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/torrent_handle.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/torrent_info.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tracker_manager.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/traversal_algorithm.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/udp_tracker_connection.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/web_peer_connection.Plo@am__quote@
+
+.cpp.o:
+@am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \
+@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ $<
+
+.cpp.obj:
+@am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \
+@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.cpp.lo:
+@am__fastdepCXX_TRUE@ if $(LTCXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \
+@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Plo"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(LTCXXCOMPILE) -c -o $@ $<
+
+closest_nodes.lo: kademlia/closest_nodes.cpp
+@am__fastdepCXX_TRUE@ if $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT closest_nodes.lo -MD -MP -MF "$(DEPDIR)/closest_nodes.Tpo" -c -o closest_nodes.lo `test -f 'kademlia/closest_nodes.cpp' || echo '$(srcdir)/'`kademlia/closest_nodes.cpp; \
+@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/closest_nodes.Tpo" "$(DEPDIR)/closest_nodes.Plo"; else rm -f "$(DEPDIR)/closest_nodes.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='kademlia/closest_nodes.cpp' object='closest_nodes.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o closest_nodes.lo `test -f 'kademlia/closest_nodes.cpp' || echo '$(srcdir)/'`kademlia/closest_nodes.cpp
+
+dht_tracker.lo: kademlia/dht_tracker.cpp
+@am__fastdepCXX_TRUE@ if $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT dht_tracker.lo -MD -MP -MF "$(DEPDIR)/dht_tracker.Tpo" -c -o dht_tracker.lo `test -f 'kademlia/dht_tracker.cpp' || echo '$(srcdir)/'`kademlia/dht_tracker.cpp; \
+@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/dht_tracker.Tpo" "$(DEPDIR)/dht_tracker.Plo"; else rm -f "$(DEPDIR)/dht_tracker.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='kademlia/dht_tracker.cpp' object='dht_tracker.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o dht_tracker.lo `test -f 'kademlia/dht_tracker.cpp' || echo '$(srcdir)/'`kademlia/dht_tracker.cpp
+
+find_data.lo: kademlia/find_data.cpp
+@am__fastdepCXX_TRUE@ if $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT find_data.lo -MD -MP -MF "$(DEPDIR)/find_data.Tpo" -c -o find_data.lo `test -f 'kademlia/find_data.cpp' || echo '$(srcdir)/'`kademlia/find_data.cpp; \
+@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/find_data.Tpo" "$(DEPDIR)/find_data.Plo"; else rm -f "$(DEPDIR)/find_data.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='kademlia/find_data.cpp' object='find_data.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o find_data.lo `test -f 'kademlia/find_data.cpp' || echo '$(srcdir)/'`kademlia/find_data.cpp
+
+node.lo: kademlia/node.cpp
+@am__fastdepCXX_TRUE@ if $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT node.lo -MD -MP -MF "$(DEPDIR)/node.Tpo" -c -o node.lo `test -f 'kademlia/node.cpp' || echo '$(srcdir)/'`kademlia/node.cpp; \
+@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/node.Tpo" "$(DEPDIR)/node.Plo"; else rm -f "$(DEPDIR)/node.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='kademlia/node.cpp' object='node.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o node.lo `test -f 'kademlia/node.cpp' || echo '$(srcdir)/'`kademlia/node.cpp
+
+node_id.lo: kademlia/node_id.cpp
+@am__fastdepCXX_TRUE@ if $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT node_id.lo -MD -MP -MF "$(DEPDIR)/node_id.Tpo" -c -o node_id.lo `test -f 'kademlia/node_id.cpp' || echo '$(srcdir)/'`kademlia/node_id.cpp; \
+@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/node_id.Tpo" "$(DEPDIR)/node_id.Plo"; else rm -f "$(DEPDIR)/node_id.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='kademlia/node_id.cpp' object='node_id.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o node_id.lo `test -f 'kademlia/node_id.cpp' || echo '$(srcdir)/'`kademlia/node_id.cpp
+
+refresh.lo: kademlia/refresh.cpp
+@am__fastdepCXX_TRUE@ if $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT refresh.lo -MD -MP -MF "$(DEPDIR)/refresh.Tpo" -c -o refresh.lo `test -f 'kademlia/refresh.cpp' || echo '$(srcdir)/'`kademlia/refresh.cpp; \
+@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/refresh.Tpo" "$(DEPDIR)/refresh.Plo"; else rm -f "$(DEPDIR)/refresh.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='kademlia/refresh.cpp' object='refresh.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o refresh.lo `test -f 'kademlia/refresh.cpp' || echo '$(srcdir)/'`kademlia/refresh.cpp
+
+routing_table.lo: kademlia/routing_table.cpp
+@am__fastdepCXX_TRUE@ if $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT routing_table.lo -MD -MP -MF "$(DEPDIR)/routing_table.Tpo" -c -o routing_table.lo `test -f 'kademlia/routing_table.cpp' || echo '$(srcdir)/'`kademlia/routing_table.cpp; \
+@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/routing_table.Tpo" "$(DEPDIR)/routing_table.Plo"; else rm -f "$(DEPDIR)/routing_table.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='kademlia/routing_table.cpp' object='routing_table.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o routing_table.lo `test -f 'kademlia/routing_table.cpp' || echo '$(srcdir)/'`kademlia/routing_table.cpp
+
+rpc_manager.lo: kademlia/rpc_manager.cpp
+@am__fastdepCXX_TRUE@ if $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT rpc_manager.lo -MD -MP -MF "$(DEPDIR)/rpc_manager.Tpo" -c -o rpc_manager.lo `test -f 'kademlia/rpc_manager.cpp' || echo '$(srcdir)/'`kademlia/rpc_manager.cpp; \
+@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/rpc_manager.Tpo" "$(DEPDIR)/rpc_manager.Plo"; else rm -f "$(DEPDIR)/rpc_manager.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='kademlia/rpc_manager.cpp' object='rpc_manager.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o rpc_manager.lo `test -f 'kademlia/rpc_manager.cpp' || echo '$(srcdir)/'`kademlia/rpc_manager.cpp
+
+traversal_algorithm.lo: kademlia/traversal_algorithm.cpp
+@am__fastdepCXX_TRUE@ if $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT traversal_algorithm.lo -MD -MP -MF "$(DEPDIR)/traversal_algorithm.Tpo" -c -o traversal_algorithm.lo `test -f 'kademlia/traversal_algorithm.cpp' || echo '$(srcdir)/'`kademlia/traversal_algorithm.cpp; \
+@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/traversal_algorithm.Tpo" "$(DEPDIR)/traversal_algorithm.Plo"; else rm -f "$(DEPDIR)/traversal_algorithm.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='kademlia/traversal_algorithm.cpp' object='traversal_algorithm.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o traversal_algorithm.lo `test -f 'kademlia/traversal_algorithm.cpp' || echo '$(srcdir)/'`kademlia/traversal_algorithm.cpp
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+distclean-libtool:
+ -rm -f libtool
+uninstall-info-am:
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ mkid -fID $$unique
+tags: TAGS
+
+TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ tags=; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$tags $$unique; \
+ fi
+ctags: CTAGS
+CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ tags=; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ test -z "$(CTAGS_ARGS)$$tags$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$tags $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && cd $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) $$here
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+ $(mkdir_p) $(distdir)/../include/libtorrent $(distdir)/../include/libtorrent/aux_
+ @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \
+ list='$(DISTFILES)'; for file in $$list; do \
+ case $$file in \
+ $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \
+ $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \
+ esac; \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test "$$dir" != "$$file" && test "$$dir" != "."; then \
+ dir="/$$dir"; \
+ $(mkdir_p) "$(distdir)$$dir"; \
+ else \
+ dir=''; \
+ fi; \
+ if test -d $$d/$$file; then \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \
+ fi; \
+ cp -pR $$d/$$file $(distdir)$$dir || exit 1; \
+ else \
+ test -f $(distdir)/$$file \
+ || cp -p $$d/$$file $(distdir)/$$file \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(LTLIBRARIES) $(HEADERS)
+installdirs:
+ for dir in "$(DESTDIR)$(libdir)"; do \
+ test -z "$$dir" || $(mkdir_p) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ `test -z '$(STRIP)' || \
+ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-libtool distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-exec-am: install-libLTLIBRARIES
+
+install-info: install-info-am
+
+install-man:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-info-am uninstall-libLTLIBRARIES
+
+.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \
+ clean-libLTLIBRARIES clean-libtool ctags distclean \
+ distclean-compile distclean-generic distclean-libtool \
+ distclean-tags distdir dvi dvi-am html html-am info info-am \
+ install install-am install-data install-data-am install-exec \
+ install-exec-am install-info install-info-am \
+ install-libLTLIBRARIES install-man install-strip installcheck \
+ installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags uninstall uninstall-am uninstall-info-am \
+ uninstall-libLTLIBRARIES
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/library/README b/library/README
new file mode 100644
index 000000000..1646e47e0
--- /dev/null
+++ b/library/README
@@ -0,0 +1,30 @@
+=================
+python-libtorrent
+=================
+
+The simplest way to use this project is to install the .deb file,
+which appears in the "debs" subfolder. This was tested under
+Ubuntu 6.06 (Dapper), but may work in other Debian-based systems.
+
+Otherwise, you may compile the code with the script 'makeit'. The
+script 'installit' will install the package, so you can import it as
+a module under python.
+
+After installation of python-libtorrent, you can install and run
+Deluge, http://code.google.com/p/deluge-torrent/
+
+As a simpler test, you can check whether python-libtorrent works by
+by running
+
+python test.py
+
+This does a simple torrent download. Note that the torrent is for
+the Ubuntu 6.06 release, and was available as of 15.9.2006. You
+may need to replace the torrent with another.
+
+If successful, you will see test.py print the torrent status
+to the screen each second. Notice in particular the download speed
+and amount downloaded so far, to see if things are working.
+
+Note: You may need to change the port used, depending on your system.
+
diff --git a/library/alert.cpp b/library/alert.cpp
new file mode 100755
index 000000000..781265b92
--- /dev/null
+++ b/library/alert.cpp
@@ -0,0 +1,124 @@
+/*
+
+Copyright (c) 2003, Arvid Norberg, Daniel Wallin
+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.
+
+*/
+
+#include "libtorrent/alert.hpp"
+
+namespace libtorrent {
+
+ alert::alert(severity_t severity, const std::string& msg)
+ : m_msg(msg)
+ , m_severity(severity)
+ , m_timestamp(boost::posix_time::second_clock::universal_time())
+ {
+ }
+
+ alert::~alert()
+ {
+ }
+
+ boost::posix_time::ptime alert::timestamp() const
+ {
+ return m_timestamp;
+ }
+
+ const std::string& alert::msg() const
+ {
+ return m_msg;
+ }
+
+ alert::severity_t alert::severity() const
+ {
+ return m_severity;
+ }
+
+
+
+ alert_manager::alert_manager()
+ : m_severity(alert::none)
+ {}
+
+ alert_manager::~alert_manager()
+ {
+ while (!m_alerts.empty())
+ {
+ delete m_alerts.front();
+ m_alerts.pop();
+ }
+ }
+
+ void alert_manager::post_alert(const alert& alert_)
+ {
+ boost::mutex::scoped_lock lock(m_mutex);
+ if (m_severity > alert_.severity()) return;
+
+ // the internal limit is 100 alerts
+ if (m_alerts.size() == 100)
+ {
+ alert* result = m_alerts.front();
+ m_alerts.pop();
+ delete result;
+ }
+ m_alerts.push(alert_.clone().release());
+ }
+
+ std::auto_ptr<alert> alert_manager::get()
+ {
+ boost::mutex::scoped_lock lock(m_mutex);
+
+ assert(!m_alerts.empty());
+
+ alert* result = m_alerts.front();
+ m_alerts.pop();
+ return std::auto_ptr<alert>(result);
+ }
+
+ bool alert_manager::pending() const
+ {
+ boost::mutex::scoped_lock lock(m_mutex);
+
+ return !m_alerts.empty();
+ }
+
+ void alert_manager::set_severity(alert::severity_t severity)
+ {
+ boost::mutex::scoped_lock lock(m_mutex);
+
+ m_severity = severity;
+ }
+
+ bool alert_manager::should_post(alert::severity_t severity) const
+ {
+ return severity >= m_severity;
+ }
+
+} // namespace libtorrent
+
diff --git a/library/allocate_resources.cpp b/library/allocate_resources.cpp
new file mode 100644
index 000000000..7d08b036d
--- /dev/null
+++ b/library/allocate_resources.cpp
@@ -0,0 +1,201 @@
+/*
+
+Copyright (c) 2003, Magnus Jonsson, 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.
+
+*/
+
+//The Standard Library defines the two template functions std::min()
+//and std::max() in the <algorithm> header. In general, you should
+//use these template functions for calculating the min and max values
+//of a pair. Unfortunately, Visual C++ does not define these function
+// templates. This is because the names min and max clash with
+//the traditional min and max macros defined in <windows.h>.
+//As a workaround, Visual C++ defines two alternative templates with
+//identical functionality called _cpp_min() and _cpp_max(). You can
+//use them instead of std::min() and std::max().To disable the
+//generation of the min and max macros in Visual C++, #define
+//NOMINMAX before #including <windows.h>.
+
+#ifdef _WIN32
+ //support boost1.32.0(2004-11-19 18:47)
+ //now all libs can be compiled and linked with static module
+ #define NOMINMAX
+#endif
+
+#include "libtorrent/allocate_resources.hpp"
+#include "libtorrent/size_type.hpp"
+#include "libtorrent/peer_connection.hpp"
+#include "libtorrent/torrent.hpp"
+#include "libtorrent/aux_/allocate_resources_impl.hpp"
+
+#include <cassert>
+#include <algorithm>
+#include <boost/limits.hpp>
+
+#if defined(_MSC_VER) && _MSC_VER < 1310
+#define for if (false) {} else for
+#else
+#include <boost/iterator/transform_iterator.hpp>
+#endif
+
+namespace libtorrent
+{
+ int saturated_add(int a, int b)
+ {
+ assert(a >= 0);
+ assert(b >= 0);
+ assert(a <= resource_request::inf);
+ assert(b <= resource_request::inf);
+ assert(resource_request::inf + resource_request::inf < 0);
+
+ unsigned int sum = unsigned(a) + unsigned(b);
+ if (sum > unsigned(resource_request::inf))
+ sum = resource_request::inf;
+
+ assert(sum >= unsigned(a) && sum >= unsigned(b));
+ return int(sum);
+ }
+
+#if defined(_MSC_VER) && _MSC_VER < 1310
+
+ namespace detail
+ {
+ struct iterator_wrapper
+ {
+ typedef std::map<sha1_hash, boost::shared_ptr<torrent> >::iterator orig_iter;
+
+ orig_iter iter;
+
+ iterator_wrapper(orig_iter i): iter(i) {}
+ void operator++() { ++iter; }
+ torrent& operator*() { return *(iter->second); }
+ bool operator==(const iterator_wrapper& i) const
+ { return iter == i.iter; }
+ bool operator!=(const iterator_wrapper& i) const
+ { return iter != i.iter; }
+ };
+
+ struct iterator_wrapper2
+ {
+ typedef std::map<tcp::endpoint, peer_connection*>::iterator orig_iter;
+
+ orig_iter iter;
+
+ iterator_wrapper2(orig_iter i): iter(i) {}
+ void operator++() { ++iter; }
+ peer_connection& operator*() { return *(iter->second); }
+ bool operator==(const iterator_wrapper2& i) const
+ { return iter == i.iter; }
+ bool operator!=(const iterator_wrapper2& i) const
+ { return iter != i.iter; }
+ };
+
+ }
+
+ void allocate_resources(
+ int resources
+ , std::map<sha1_hash, boost::shared_ptr<torrent> >& c
+ , resource_request torrent::* res)
+ {
+ aux::allocate_resources_impl(
+ resources
+ , detail::iterator_wrapper(c.begin())
+ , detail::iterator_wrapper(c.end())
+ , res);
+ }
+
+ void allocate_resources(
+ int resources
+ , std::map<tcp::endpoint, peer_connection*>& c
+ , resource_request peer_connection::* res)
+ {
+ aux::allocate_resources_impl(
+ resources
+ , detail::iterator_wrapper2(c.begin())
+ , detail::iterator_wrapper2(c.end())
+ , res);
+ }
+
+#else
+
+ namespace aux
+ {
+ peer_connection& pick_peer(
+ std::pair<boost::shared_ptr<stream_socket>
+ , boost::intrusive_ptr<peer_connection> > const& p)
+ {
+ return *p.second;
+ }
+
+ peer_connection& pick_peer2(
+ std::pair<tcp::endpoint, peer_connection*> const& p)
+ {
+ return *p.second;
+ }
+
+ torrent& deref(std::pair<sha1_hash, boost::shared_ptr<torrent> > const& p)
+ {
+ return *p.second;
+ }
+ }
+
+ void allocate_resources(
+ int resources
+ , std::map<sha1_hash, boost::shared_ptr<torrent> >& c
+ , resource_request torrent::* res)
+ {
+ typedef std::map<sha1_hash, boost::shared_ptr<torrent> >::iterator orig_iter;
+ typedef std::pair<sha1_hash, boost::shared_ptr<torrent> > in_param;
+ typedef boost::transform_iterator<torrent& (*)(in_param const&), orig_iter> new_iter;
+
+ aux::allocate_resources_impl(
+ resources
+ , new_iter(c.begin(), &aux::deref)
+ , new_iter(c.end(), &aux::deref)
+ , res);
+ }
+
+ void allocate_resources(
+ int resources
+ , std::map<tcp::endpoint, peer_connection*>& c
+ , resource_request peer_connection::* res)
+ {
+ typedef std::map<tcp::endpoint, peer_connection*>::iterator orig_iter;
+ typedef std::pair<tcp::endpoint, peer_connection*> in_param;
+ typedef boost::transform_iterator<peer_connection& (*)(in_param const&), orig_iter> new_iter;
+
+ aux::allocate_resources_impl(
+ resources
+ , new_iter(c.begin(), &aux::pick_peer2)
+ , new_iter(c.end(), &aux::pick_peer2)
+ , res);
+ }
+#endif
+
+} // namespace libtorrent
diff --git a/library/bt_peer_connection.cpp b/library/bt_peer_connection.cpp
new file mode 100755
index 000000000..0cff4b253
--- /dev/null
+++ b/library/bt_peer_connection.cpp
@@ -0,0 +1,1571 @@
+/*
+
+Copyright (c) 2003, 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.
+
+*/
+
+#include <vector>
+#include <iostream>
+#include <iomanip>
+#include <limits>
+#include <boost/bind.hpp>
+
+#include "libtorrent/bt_peer_connection.hpp"
+#include "libtorrent/session.hpp"
+#include "libtorrent/identify_client.hpp"
+#include "libtorrent/entry.hpp"
+#include "libtorrent/bencode.hpp"
+#include "libtorrent/alert_types.hpp"
+#include "libtorrent/invariant_check.hpp"
+#include "libtorrent/io.hpp"
+#include "libtorrent/version.hpp"
+#include "libtorrent/aux_/session_impl.hpp"
+
+using namespace boost::posix_time;
+using boost::bind;
+using boost::shared_ptr;
+using libtorrent::aux::session_impl;
+
+namespace libtorrent
+{
+
+ // the names of the extensions to look for in
+ // the extensions-message
+ const char* bt_peer_connection::extension_names[] =
+ { "", "LT_chat", "LT_metadata", "LT_peer_exchange" };
+
+ const bt_peer_connection::message_handler
+ bt_peer_connection::m_message_handler[] =
+ {
+ &bt_peer_connection::on_choke,
+ &bt_peer_connection::on_unchoke,
+ &bt_peer_connection::on_interested,
+ &bt_peer_connection::on_not_interested,
+ &bt_peer_connection::on_have,
+ &bt_peer_connection::on_bitfield,
+ &bt_peer_connection::on_request,
+ &bt_peer_connection::on_piece,
+ &bt_peer_connection::on_cancel,
+ &bt_peer_connection::on_dht_port,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ &bt_peer_connection::on_extended
+ };
+
+
+ bt_peer_connection::bt_peer_connection(
+ session_impl& ses
+ , boost::weak_ptr<torrent> tor
+ , shared_ptr<stream_socket> s
+ , tcp::endpoint const& remote)
+ : peer_connection(ses, tor, s, remote)
+ , m_state(read_protocol_length)
+ , m_supports_extensions(false)
+ , m_supports_dht_port(false)
+ , m_no_metadata(
+ boost::gregorian::date(1970, boost::date_time::Jan, 1)
+ , boost::posix_time::seconds(0))
+ , m_metadata_request(
+ boost::gregorian::date(1970, boost::date_time::Jan, 1)
+ , boost::posix_time::seconds(0))
+ , m_waiting_metadata_request(false)
+ , m_metadata_progress(0)
+#ifndef NDEBUG
+ , m_in_constructor(true)
+#endif
+ {
+#ifdef TORRENT_VERBOSE_LOGGING
+ (*m_logger) << "*** bt_peer_connection\n";
+#endif
+
+ // initialize the extension list to zero, since
+ // we don't know which extensions the other
+ // end supports yet
+ std::fill(m_extension_messages, m_extension_messages + num_supported_extensions, 0);
+
+ write_handshake();
+
+ // start in the state where we are trying to read the
+ // handshake from the other side
+ reset_recv_buffer(1);
+
+ // assume the other end has no pieces
+ boost::shared_ptr<torrent> t = associated_torrent().lock();
+ assert(t);
+
+ if (t->ready_for_connections())
+ write_bitfield(t->pieces());
+
+ setup_send();
+ setup_receive();
+#ifndef NDEBUG
+ m_in_constructor = false;
+#endif
+ }
+
+ bt_peer_connection::bt_peer_connection(
+ session_impl& ses
+ , boost::shared_ptr<stream_socket> s)
+ : peer_connection(ses, s)
+ , m_state(read_protocol_length)
+ , m_supports_extensions(false)
+ , m_supports_dht_port(false)
+ , m_no_metadata(
+ boost::gregorian::date(1970, boost::date_time::Jan, 1)
+ , boost::posix_time::seconds(0))
+ , m_metadata_request(
+ boost::gregorian::date(1970, boost::date_time::Jan, 1)
+ , boost::posix_time::seconds(0))
+ , m_waiting_metadata_request(false)
+ , m_metadata_progress(0)
+#ifndef NDEBUG
+ , m_in_constructor(true)
+#endif
+ {
+ // initialize the extension list to zero, since
+ // we don't know which extensions the other
+ // end supports yet
+ std::fill(m_extension_messages, m_extension_messages + num_supported_extensions, 0);
+
+ // we are not attached to any torrent yet.
+ // we have to wait for the handshake to see
+ // which torrent the connector want's to connect to
+
+ // start in the state where we are trying to read the
+ // handshake from the other side
+ reset_recv_buffer(1);
+ setup_receive();
+#ifndef NDEBUG
+ m_in_constructor = false;
+#endif
+ }
+
+ bt_peer_connection::~bt_peer_connection()
+ {
+ }
+
+ void bt_peer_connection::write_dht_port(int listen_port)
+ {
+ INVARIANT_CHECK;
+
+ buffer::interval packet = allocate_send_buffer(7);
+ detail::write_uint32(3, packet.begin);
+ detail::write_uint8(msg_dht_port, packet.begin);
+ detail::write_uint16(listen_port, packet.begin);
+ assert(packet.begin == packet.end);
+ setup_send();
+ }
+
+ void bt_peer_connection::get_peer_info(peer_info& p) const
+ {
+ assert(!associated_torrent().expired());
+
+ p.down_speed = statistics().download_rate();
+ p.up_speed = statistics().upload_rate();
+ p.payload_down_speed = statistics().download_payload_rate();
+ p.payload_up_speed = statistics().upload_payload_rate();
+ p.pid = pid();
+ p.ip = remote();
+
+ p.total_download = statistics().total_payload_download();
+ p.total_upload = statistics().total_payload_upload();
+
+ if (m_ul_bandwidth_quota.given == std::numeric_limits<int>::max())
+ p.upload_limit = -1;
+ else
+ p.upload_limit = m_ul_bandwidth_quota.given;
+
+ if (m_dl_bandwidth_quota.given == std::numeric_limits<int>::max())
+ p.download_limit = -1;
+ else
+ p.download_limit = m_dl_bandwidth_quota.given;
+
+ p.load_balancing = total_free_upload();
+
+ p.download_queue_length = (int)download_queue().size();
+ p.upload_queue_length = (int)upload_queue().size();
+
+ if (boost::optional<piece_block_progress> ret = downloading_piece_progress())
+ {
+ p.downloading_piece_index = ret->piece_index;
+ p.downloading_block_index = ret->block_index;
+ p.downloading_progress = ret->bytes_downloaded;
+ p.downloading_total = ret->full_block_bytes;
+ }
+ else
+ {
+ p.downloading_piece_index = -1;
+ p.downloading_block_index = -1;
+ p.downloading_progress = 0;
+ p.downloading_total = 0;
+ }
+
+ p.flags = 0;
+ if (is_interesting()) p.flags |= peer_info::interesting;
+ if (is_choked()) p.flags |= peer_info::choked;
+ if (is_peer_interested()) p.flags |= peer_info::remote_interested;
+ if (has_peer_choked()) p.flags |= peer_info::remote_choked;
+ if (support_extensions()) p.flags |= peer_info::supports_extensions;
+ if (is_local()) p.flags |= peer_info::local_connection;
+ if (!is_connecting() && m_state < read_packet_size)
+ p.flags |= peer_info::handshake;
+ if (is_connecting() && !is_queued()) p.flags |= peer_info::connecting;
+ if (is_queued()) p.flags |= peer_info::queued;
+
+ p.pieces = get_bitfield();
+ p.seed = is_seed();
+
+ p.client = m_client_version;
+ p.connection_type = peer_info::standard_bittorrent;
+ }
+
+ void bt_peer_connection::write_handshake()
+ {
+ INVARIANT_CHECK;
+
+ boost::shared_ptr<torrent> t = associated_torrent().lock();
+ assert(t);
+
+ // add handshake to the send buffer
+ const char version_string[] = "BitTorrent protocol";
+ const int string_len = sizeof(version_string)-1;
+
+ buffer::interval i = allocate_send_buffer(1 + string_len + 8 + 20 + 20);
+ // length of version string
+ *i.begin = string_len;
+ ++i.begin;
+
+ // version string itself
+ std::copy(
+ version_string
+ , version_string + string_len
+ , i.begin);
+ i.begin += string_len;
+
+ // 8 zeroes
+ std::fill(
+ i.begin
+ , i.begin + 8
+ , 0);
+
+#ifndef TORRENT_DISABLE_DHT
+ // indicate that we support the DHT messages
+ *(i.begin + 7) = 0x01;
+#endif
+
+ // we support extensions
+ *(i.begin + 5) = 0x10;
+
+ i.begin += 8;
+
+ // info hash
+ sha1_hash const& ih = t->torrent_file().info_hash();
+ std::copy(ih.begin(), ih.end(), i.begin);
+ i.begin += 20;
+
+ // peer id
+ std::copy(
+ m_ses.get_peer_id().begin()
+ , m_ses.get_peer_id().end()
+ , i.begin);
+ i.begin += 20;
+ assert(i.begin == i.end);
+
+#ifdef TORRENT_VERBOSE_LOGGING
+ using namespace boost::posix_time;
+ (*m_logger) << to_simple_string(second_clock::universal_time())
+ << " ==> HANDSHAKE\n";
+#endif
+ setup_send();
+ }
+
+ boost::optional<piece_block_progress> bt_peer_connection::downloading_piece_progress() const
+ {
+ boost::shared_ptr<torrent> t = associated_torrent().lock();
+ assert(t);
+
+ buffer::const_interval recv_buffer = receive_buffer();
+ // are we currently receiving a 'piece' message?
+ if (m_state != read_packet
+ || (recv_buffer.end - recv_buffer.begin) < 9
+ || recv_buffer[0] != msg_piece)
+ return boost::optional<piece_block_progress>();
+
+ const char* ptr = recv_buffer.begin + 1;
+ peer_request r;
+ r.piece = detail::read_int32(ptr);
+ r.start = detail::read_int32(ptr);
+ r.length = packet_size() - 9;
+
+ // is any of the piece message header data invalid?
+ if (!verify_piece(r))
+ return boost::optional<piece_block_progress>();
+
+ piece_block_progress p;
+
+ p.piece_index = r.piece;
+ p.block_index = r.start / t->block_size();
+ p.bytes_downloaded = recv_buffer.end - recv_buffer.begin - 9;
+ p.full_block_bytes = r.length;
+
+ return boost::optional<piece_block_progress>(p);
+ }
+
+
+ // message handlers
+
+ // -----------------------------
+ // --------- KEEPALIVE ---------
+ // -----------------------------
+
+ void bt_peer_connection::on_keepalive()
+ {
+ INVARIANT_CHECK;
+
+#ifdef TORRENT_VERBOSE_LOGGING
+ using namespace boost::posix_time;
+ (*m_logger) << to_simple_string(second_clock::universal_time())
+ << " <== KEEPALIVE\n";
+#endif
+ incoming_keepalive();
+ }
+
+ // -----------------------------
+ // ----------- CHOKE -----------
+ // -----------------------------
+
+ void bt_peer_connection::on_choke(int received)
+ {
+ INVARIANT_CHECK;
+
+ assert(received > 0);
+ if (packet_size() != 1)
+ throw protocol_error("'choke' message size != 1");
+ m_statistics.received_bytes(0, received);
+ if (!packet_finished()) return;
+
+ incoming_choke();
+ }
+
+ // -----------------------------
+ // ---------- UNCHOKE ----------
+ // -----------------------------
+
+ void bt_peer_connection::on_unchoke(int received)
+ {
+ INVARIANT_CHECK;
+
+ assert(received > 0);
+ if (packet_size() != 1)
+ throw protocol_error("'unchoke' message size != 1");
+ m_statistics.received_bytes(0, received);
+ if (!packet_finished()) return;
+
+ incoming_unchoke();
+ }
+
+ // -----------------------------
+ // -------- INTERESTED ---------
+ // -----------------------------
+
+ void bt_peer_connection::on_interested(int received)
+ {
+ INVARIANT_CHECK;
+
+ assert(received > 0);
+ if (packet_size() != 1)
+ throw protocol_error("'interested' message size != 1");
+ m_statistics.received_bytes(0, received);
+ if (!packet_finished()) return;
+
+ incoming_interested();
+ }
+
+ // -----------------------------
+ // ------ NOT INTERESTED -------
+ // -----------------------------
+
+ void bt_peer_connection::on_not_interested(int received)
+ {
+ INVARIANT_CHECK;
+
+ assert(received > 0);
+ if (packet_size() != 1)
+ throw protocol_error("'not interested' message size != 1");
+ m_statistics.received_bytes(0, received);
+ if (!packet_finished()) return;
+
+ incoming_not_interested();
+ }
+
+ // -----------------------------
+ // ----------- HAVE ------------
+ // -----------------------------
+
+ void bt_peer_connection::on_have(int received)
+ {
+ INVARIANT_CHECK;
+
+ assert(received > 0);
+ if (packet_size() != 5)
+ throw protocol_error("'have' message size != 5");
+ m_statistics.received_bytes(0, received);
+ if (!packet_finished()) return;
+
+ buffer::const_interval recv_buffer = receive_buffer();
+
+ const char* ptr = recv_buffer.begin + 1;
+ int index = detail::read_int32(ptr);
+
+ incoming_have(index);
+ }
+
+ // -----------------------------
+ // --------- BITFIELD ----------
+ // -----------------------------
+
+ void bt_peer_connection::on_bitfield(int received)
+ {
+ INVARIANT_CHECK;
+
+ assert(received > 0);
+
+ boost::shared_ptr<torrent> t = associated_torrent().lock();
+ assert(t);
+
+ // if we don't have the metedata, we cannot
+ // verify the bitfield size
+ if (t->valid_metadata()
+ && packet_size() - 1 != ((int)get_bitfield().size() + 7) / 8)
+ throw protocol_error("bitfield with invalid size");
+
+ m_statistics.received_bytes(0, received);
+ if (!packet_finished()) return;
+
+ buffer::const_interval recv_buffer = receive_buffer();
+
+ std::vector<bool> bitfield;
+
+ if (!t->valid_metadata())
+ bitfield.resize((packet_size() - 1) * 8);
+ else
+ bitfield.resize(get_bitfield().size());
+
+ // if we don't have metadata yet
+ // just remember the bitmask
+ // don't update the piecepicker
+ // (since it doesn't exist yet)
+ for (int i = 0; i < (int)bitfield.size(); ++i)
+ bitfield[i] = (recv_buffer[1 + (i>>3)] & (1 << (7 - (i&7)))) != 0;
+ incoming_bitfield(bitfield);
+ }
+
+ // -----------------------------
+ // ---------- REQUEST ----------
+ // -----------------------------
+
+ void bt_peer_connection::on_request(int received)
+ {
+ INVARIANT_CHECK;
+
+ assert(received > 0);
+ if (packet_size() != 13)
+ throw protocol_error("'request' message size != 13");
+ m_statistics.received_bytes(0, received);
+ if (!packet_finished()) return;
+
+ buffer::const_interval recv_buffer = receive_buffer();
+
+ peer_request r;
+ const char* ptr = recv_buffer.begin + 1;
+ r.piece = detail::read_int32(ptr);
+ r.start = detail::read_int32(ptr);
+ r.length = detail::read_int32(ptr);
+
+ incoming_request(r);
+ }
+
+ // -----------------------------
+ // ----------- PIECE -----------
+ // -----------------------------
+
+ void bt_peer_connection::on_piece(int received)
+ {
+ INVARIANT_CHECK;
+
+ assert(received > 0);
+
+ buffer::const_interval recv_buffer = receive_buffer();
+ int recv_pos = recv_buffer.end - recv_buffer.begin;
+
+ // classify the received data as protocol chatter
+ // or data payload for the statistics
+ if (recv_pos <= 9)
+ // only received protocol data
+ m_statistics.received_bytes(0, received);
+ else if (recv_pos - received >= 9)
+ // only received payload data
+ m_statistics.received_bytes(received, 0);
+ else
+ {
+ // received a bit of both
+ assert(recv_pos - received < 9);
+ assert(recv_pos > 9);
+ assert(9 - (recv_pos - received) <= 9);
+ m_statistics.received_bytes(
+ recv_pos - 9
+ , 9 - (recv_pos - received));
+ }
+
+ incoming_piece_fragment();
+ if (!packet_finished()) return;
+
+ const char* ptr = recv_buffer.begin + 1;
+ peer_request p;
+ p.piece = detail::read_int32(ptr);
+ p.start = detail::read_int32(ptr);
+ p.length = packet_size() - 9;
+
+ incoming_piece(p, recv_buffer.begin + 9);
+ }
+
+ // -----------------------------
+ // ---------- CANCEL -----------
+ // -----------------------------
+
+ void bt_peer_connection::on_cancel(int received)
+ {
+ INVARIANT_CHECK;
+
+ assert(received > 0);
+ if (packet_size() != 13)
+ throw protocol_error("'cancel' message size != 13");
+ m_statistics.received_bytes(0, received);
+ if (!packet_finished()) return;
+
+ buffer::const_interval recv_buffer = receive_buffer();
+
+ peer_request r;
+ const char* ptr = recv_buffer.begin + 1;
+ r.piece = detail::read_int32(ptr);
+ r.start = detail::read_int32(ptr);
+ r.length = detail::read_int32(ptr);
+
+ incoming_cancel(r);
+ }
+
+ // -----------------------------
+ // --------- DHT PORT ----------
+ // -----------------------------
+
+ void bt_peer_connection::on_dht_port(int received)
+ {
+ INVARIANT_CHECK;
+
+ assert(received > 0);
+ if (packet_size() != 3)
+ throw protocol_error("'dht_port' message size != 3");
+ m_statistics.received_bytes(0, received);
+ if (!packet_finished()) return;
+
+ buffer::const_interval recv_buffer = receive_buffer();
+
+ const char* ptr = recv_buffer.begin + 1;
+ int listen_port = detail::read_uint16(ptr);
+
+ incoming_dht_port(listen_port);
+ }
+
+ // -----------------------------
+ // --------- EXTENDED ----------
+ // -----------------------------
+
+ void bt_peer_connection::on_extended(int received)
+ {
+ INVARIANT_CHECK;
+
+ assert(received > 0);
+ m_statistics.received_bytes(0, received);
+ if (packet_size() < 2)
+ throw protocol_error("'extended' message smaller than 2 bytes");
+
+ if (associated_torrent().expired())
+ throw protocol_error("'extended' message sent before proper handshake");
+
+ buffer::const_interval recv_buffer = receive_buffer();
+ if (recv_buffer.end - recv_buffer.begin < 2) return;
+
+ assert(*recv_buffer.begin == msg_extended);
+ ++recv_buffer.begin;
+
+ int extended_id = detail::read_uint8(recv_buffer.begin);
+
+ if (extended_id > 0 && extended_id < num_supported_extensions
+ && !m_ses.extension_enabled(extended_id))
+ throw protocol_error("'extended' message using disabled extension");
+
+ switch (extended_id)
+ {
+ case extended_handshake:
+ on_extended_handshake(); break;
+ case extended_chat_message:
+ on_chat(); break;
+ case extended_metadata_message:
+ on_metadata(); break;
+ case extended_peer_exchange_message:
+ on_peer_exchange(); break;
+ default:
+ throw protocol_error("unknown extended message id: "
+ + boost::lexical_cast<std::string>(extended_id));
+ };
+ }
+
+ void bt_peer_connection::write_chat_message(const std::string& msg)
+ {
+ INVARIANT_CHECK;
+
+ assert(msg.length() <= 1 * 1024);
+ if (!supports_extension(extended_chat_message)) return;
+
+ entry e(entry::dictionary_t);
+ e["msg"] = msg;
+
+ std::vector<char> message;
+ bencode(std::back_inserter(message), e);
+
+ buffer::interval i = allocate_send_buffer(message.size() + 6);
+
+ detail::write_uint32(1 + 1 + (int)message.size(), i.begin);
+ detail::write_uint8(msg_extended, i.begin);
+ detail::write_uint8(m_extension_messages[extended_chat_message], i.begin);
+
+ std::copy(message.begin(), message.end(), i.begin);
+ i.begin += message.size();
+ assert(i.begin == i.end);
+ setup_send();
+ }
+
+
+ void bt_peer_connection::on_extended_handshake() try
+ {
+ if (!packet_finished()) return;
+
+ boost::shared_ptr<torrent> t = associated_torrent().lock();
+ assert(t);
+
+ buffer::const_interval recv_buffer = receive_buffer();
+
+ entry root = bdecode(recv_buffer.begin + 2, recv_buffer.end);
+
+#ifdef TORRENT_VERBOSE_LOGGING
+ std::stringstream ext;
+ root.print(ext);
+ (*m_logger) << "<== EXTENDED HANDSHAKE: \n" << ext.str();
+#endif
+
+ if (entry* msgs = root.find_key("m"))
+ {
+ if (msgs->type() == entry::dictionary_t)
+ {
+ // this must be the initial handshake message
+ // lets see if any of our extensions are supported
+ // if not, we will signal no extensions support to the upper layer
+ for (int i = 1; i < num_supported_extensions; ++i)
+ {
+ if (entry* f = msgs->find_key(extension_names[i]))
+ {
+ m_extension_messages[i] = (int)f->integer();
+ }
+ else
+ {
+ m_extension_messages[i] = 0;
+ }
+ }
+ }
+ }
+
+ // there is supposed to be a remote listen port
+ if (entry* listen_port = root.find_key("p"))
+ {
+ if (listen_port->type() == entry::int_t)
+ {
+ tcp::endpoint adr(remote().address()
+ , (unsigned short)listen_port->integer());
+ t->get_policy().peer_from_tracker(adr, pid());
+ }
+ }
+ // there should be a version too
+ // but where do we put that info?
+
+ if (entry* client_info = root.find_key("v"))
+ {
+ if (client_info->type() == entry::string_t)
+ m_client_version = client_info->string();
+ }
+
+ if (entry* reqq = root.find_key("reqq"))
+ {
+ if (reqq->type() == entry::int_t)
+ m_max_out_request_queue = reqq->integer();
+ if (m_max_out_request_queue < 1)
+ m_max_out_request_queue = 1;
+ }
+ }
+ catch (std::exception& exc)
+ {
+#ifdef TORRENT_VERBOSE_LOGGIGN
+ (*m_logger) << "invalid extended handshake: " << exc.what() << "\n";
+#endif
+ }
+
+ // -----------------------------
+ // ----------- CHAT ------------
+ // -----------------------------
+
+ void bt_peer_connection::on_chat()
+ {
+ if (packet_size() > 2 * 1024)
+ throw protocol_error("CHAT message larger than 2 kB");
+
+ if (!packet_finished()) return;
+
+ try
+ {
+ boost::shared_ptr<torrent> t = associated_torrent().lock();
+ assert(t);
+
+ buffer::const_interval recv_buffer = receive_buffer();
+ entry d = bdecode(recv_buffer.begin + 2, recv_buffer.end);
+ const std::string& str = d["msg"].string();
+
+ if (t->alerts().should_post(alert::critical))
+ {
+ t->alerts().post_alert(
+ chat_message_alert(
+ t->get_handle()
+ , remote(), str));
+ }
+
+ }
+ catch (invalid_encoding&)
+ {
+ // TODO: post an alert about the invalid chat message
+ return;
+// throw protocol_error("invalid bencoding in CHAT message");
+ }
+ catch (type_error&)
+ {
+ // TODO: post an alert about the invalid chat message
+ return;
+// throw protocol_error("invalid types in bencoded CHAT message");
+ }
+ return;
+ }
+
+ // -----------------------------
+ // --------- METADATA ----------
+ // -----------------------------
+
+ void bt_peer_connection::on_metadata()
+ {
+ boost::shared_ptr<torrent> t = associated_torrent().lock();
+ assert(t);
+
+ if (packet_size() > 500 * 1024)
+ throw protocol_error("metadata message larger than 500 kB");
+
+ if (!packet_finished()) return;
+
+ buffer::const_interval recv_buffer = receive_buffer();
+ recv_buffer.begin += 2;
+ int type = detail::read_uint8(recv_buffer.begin);
+
+ switch (type)
+ {
+ case 0: // request
+ {
+ int start = detail::read_uint8(recv_buffer.begin);
+ int size = detail::read_uint8(recv_buffer.begin) + 1;
+
+ if (packet_size() != 5)
+ {
+ // invalid metadata request
+ throw protocol_error("invalid metadata request");
+ }
+
+ write_metadata(std::make_pair(start, size));
+ }
+ break;
+ case 1: // data
+ {
+ if (recv_buffer.end - recv_buffer.begin < 8) return;
+ int total_size = detail::read_int32(recv_buffer.begin);
+ int offset = detail::read_int32(recv_buffer.begin);
+ int data_size = packet_size() - 2 - 9;
+
+ if (total_size > 500 * 1024)
+ throw protocol_error("metadata size larger than 500 kB");
+ if (total_size <= 0)
+ throw protocol_error("invalid metadata size");
+ if (offset > total_size || offset < 0)
+ throw protocol_error("invalid metadata offset");
+ if (offset + data_size > total_size)
+ throw protocol_error("invalid metadata message");
+
+ t->metadata_progress(total_size
+ , recv_buffer.left() - m_metadata_progress);
+ m_metadata_progress = recv_buffer.left();
+ if (!packet_finished()) return;
+
+#ifdef TORRENT_VERBOSE_LOGGING
+ using namespace boost::posix_time;
+ (*m_logger) << to_simple_string(second_clock::universal_time())
+ << " <== METADATA [ tot: " << total_size << " offset: "
+ << offset << " size: " << data_size << " ]\n";
+#endif
+
+ m_waiting_metadata_request = false;
+ t->received_metadata(recv_buffer.begin, data_size
+ , offset, total_size);
+ m_metadata_progress = 0;
+ }
+ break;
+ case 2: // have no data
+ if (!packet_finished()) return;
+
+ m_no_metadata = second_clock::universal_time();
+ if (m_waiting_metadata_request)
+ t->cancel_metadata_request(m_last_metadata_request);
+ m_waiting_metadata_request = false;
+ break;
+ default:
+ throw protocol_error("unknown metadata extension message: "
+ + boost::lexical_cast<std::string>(type));
+ }
+
+ }
+
+ // -----------------------------
+ // ------ PEER EXCHANGE --------
+ // -----------------------------
+
+ void bt_peer_connection::on_peer_exchange()
+ {
+
+ }
+
+ bool bt_peer_connection::has_metadata() const
+ {
+ using namespace boost::posix_time;
+ return second_clock::universal_time() - m_no_metadata > minutes(5);
+ }
+
+ bool bt_peer_connection::dispatch_message(int received)
+ {
+ INVARIANT_CHECK;
+
+ assert(received > 0);
+
+ // this means the connection has been closed already
+ if (associated_torrent().expired()) return false;
+
+ buffer::const_interval recv_buffer = receive_buffer();
+
+ int packet_type = recv_buffer[0];
+ if (packet_type < 0
+ || packet_type >= num_supported_messages
+ || m_message_handler[packet_type] == 0)
+ {
+ throw protocol_error("unknown message id: "
+ + boost::lexical_cast<std::string>(packet_type)
+ + " size: " + boost::lexical_cast<std::string>(packet_size()));
+ }
+
+ assert(m_message_handler[packet_type] != 0);
+
+ // call the correct handler for this packet type
+ (this->*m_message_handler[packet_type])(received);
+
+ if (!packet_finished()) return false;
+
+ return true;
+ }
+
+ void bt_peer_connection::write_keepalive()
+ {
+ INVARIANT_CHECK;
+
+ char buf[] = {0,0,0,0};
+ send_buffer(buf, buf + sizeof(buf));
+ }
+
+ void bt_peer_connection::write_cancel(peer_request const& r)
+ {
+ INVARIANT_CHECK;
+
+ assert(associated_torrent().lock()->valid_metadata());
+
+ char buf[] = {0,0,0,13, msg_cancel};
+
+ buffer::interval i = allocate_send_buffer(17);
+
+ std::copy(buf, buf + 5, i.begin);
+ i.begin += 5;
+
+ // index
+ detail::write_int32(r.piece, i.begin);
+ // begin
+ detail::write_int32(r.start, i.begin);
+ // length
+ detail::write_int32(r.length, i.begin);
+ assert(i.begin == i.end);
+
+ setup_send();
+ }
+
+ void bt_peer_connection::write_request(peer_request const& r)
+ {
+ INVARIANT_CHECK;
+
+ assert(associated_torrent().lock()->valid_metadata());
+
+ char buf[] = {0,0,0,13, msg_request};
+
+ buffer::interval i = allocate_send_buffer(17);
+
+ std::copy(buf, buf + 5, i.begin);
+ i.begin += 5;
+
+ // index
+ detail::write_int32(r.piece, i.begin);
+ // begin
+ detail::write_int32(r.start, i.begin);
+ // length
+ detail::write_int32(r.length, i.begin);
+ assert(i.begin == i.end);
+
+ setup_send();
+ }
+
+ void bt_peer_connection::write_metadata(std::pair<int, int> req)
+ {
+ assert(req.first >= 0);
+ assert(req.second > 0);
+ assert(req.second <= 256);
+ assert(req.first + req.second <= 256);
+ assert(!associated_torrent().expired());
+ INVARIANT_CHECK;
+
+ // abort if the peer doesn't support the metadata extension
+ if (!supports_extension(extended_metadata_message)) return;
+
+ boost::shared_ptr<torrent> t = associated_torrent().lock();
+ assert(t);
+
+ if (t->valid_metadata())
+ {
+ std::pair<int, int> offset
+ = req_to_offset(req, (int)t->metadata().size());
+
+ buffer::interval i = allocate_send_buffer(15 + offset.second);
+
+ // yes, we have metadata, send it
+ detail::write_uint32(11 + offset.second, i.begin);
+ detail::write_uint8(msg_extended, i.begin);
+ detail::write_uint8(m_extension_messages[extended_metadata_message]
+ , i.begin);
+ // means 'data packet'
+ detail::write_uint8(1, i.begin);
+ detail::write_uint32((int)t->metadata().size(), i.begin);
+ detail::write_uint32(offset.first, i.begin);
+ std::vector<char> const& metadata = t->metadata();
+ std::copy(metadata.begin() + offset.first
+ , metadata.begin() + offset.first + offset.second, i.begin);
+ i.begin += offset.second;
+ assert(i.begin == i.end);
+ }
+ else
+ {
+ buffer::interval i = allocate_send_buffer(4 + 3);
+ // we don't have the metadata, reply with
+ // don't have-message
+ detail::write_uint32(1 + 2, i.begin);
+ detail::write_uint8(msg_extended, i.begin);
+ detail::write_uint8(m_extension_messages[extended_metadata_message]
+ , i.begin);
+ // means 'have no data'
+ detail::write_uint8(2, i.begin);
+ assert(i.begin == i.end);
+ }
+ setup_send();
+ }
+
+ void bt_peer_connection::write_metadata_request(std::pair<int, int> req)
+ {
+ assert(req.first >= 0);
+ assert(req.second > 0);
+ assert(req.first + req.second <= 256);
+ assert(!associated_torrent().expired());
+ assert(!associated_torrent().lock()->valid_metadata());
+ INVARIANT_CHECK;
+
+ int start = req.first;
+ int size = req.second;
+
+ // abort if the peer doesn't support the metadata extension
+ if (!supports_extension(extended_metadata_message)) return;
+
+#ifdef TORRENT_VERBOSE_LOGGING
+ using namespace boost::posix_time;
+ (*m_logger) << to_simple_string(second_clock::universal_time())
+ << " ==> METADATA_REQUEST [ start: " << req.first
+ << " size: " << req.second << " ]\n";
+#endif
+
+ buffer::interval i = allocate_send_buffer(9);
+
+ detail::write_uint32(1 + 1 + 3, i.begin);
+ detail::write_uint8(msg_extended, i.begin);
+ detail::write_uint8(m_extension_messages[extended_metadata_message]
+ , i.begin);
+ // means 'request data'
+ detail::write_uint8(0, i.begin);
+ detail::write_uint8(start, i.begin);
+ detail::write_uint8(size - 1, i.begin);
+ assert(i.begin == i.end);
+ setup_send();
+ }
+
+ void bt_peer_connection::write_bitfield(std::vector<bool> const& bitfield)
+ {
+ INVARIANT_CHECK;
+
+ boost::shared_ptr<torrent> t = associated_torrent().lock();
+ assert(t);
+
+ if (t->num_pieces() == 0) return;
+
+#ifdef TORRENT_VERBOSE_LOGGING
+ using namespace boost::posix_time;
+ (*m_logger) << to_simple_string(second_clock::universal_time())
+ << " ==> BITFIELD ";
+
+ for (int i = 0; i < (int)get_bitfield().size(); ++i)
+ {
+ if (bitfield[i]) (*m_logger) << "1";
+ else (*m_logger) << "0";
+ }
+ (*m_logger) << "\n";
+#endif
+ const int packet_size = ((int)bitfield.size() + 7) / 8 + 5;
+
+ buffer::interval i = allocate_send_buffer(packet_size);
+
+ detail::write_int32(packet_size - 4, i.begin);
+ detail::write_uint8(msg_bitfield, i.begin);
+
+ std::fill(i.begin, i.end, 0);
+ for (int c = 0; c < (int)bitfield.size(); ++c)
+ {
+ if (bitfield[c])
+ i.begin[c >> 3] |= 1 << (7 - (c & 7));
+ }
+ assert(i.end - i.begin == ((int)bitfield.size() + 7) / 8);
+ setup_send();
+ }
+
+ void bt_peer_connection::write_extensions()
+ {
+ INVARIANT_CHECK;
+
+#ifdef TORRENT_VERBOSE_LOGGING
+ using namespace boost::posix_time;
+ (*m_logger) << to_simple_string(second_clock::universal_time())
+ << " ==> EXTENSIONS\n";
+#endif
+ assert(m_supports_extensions);
+
+ entry handshake(entry::dictionary_t);
+ entry extension_list(entry::dictionary_t);
+
+ for (int i = 1; i < num_supported_extensions; ++i)
+ {
+ // if this specific extension is disabled
+ // just don't add it to the supported set
+ if (!m_ses.extension_enabled(i)) continue;
+ extension_list[extension_names[i]] = i;
+ }
+
+ handshake["m"] = extension_list;
+ handshake["p"] = m_ses.listen_port();
+ handshake["v"] = m_ses.settings().user_agent;
+ std::string remote_address;
+ std::back_insert_iterator<std::string> out(remote_address);
+ detail::write_address(remote().address(), out);
+ handshake["ip"] = remote_address;
+ handshake["reqq"] = m_ses.settings().max_allowed_in_request_queue;
+
+ std::vector<char> msg;
+ bencode(std::back_inserter(msg), handshake);
+
+ // make room for message
+ buffer::interval i = allocate_send_buffer(6 + msg.size());
+
+ // write the length of the message
+ detail::write_int32((int)msg.size() + 2, i.begin);
+ detail::write_uint8(msg_extended, i.begin);
+ // signal handshake message
+ detail::write_uint8(extended_handshake, i.begin);
+
+ std::copy(msg.begin(), msg.end(), i.begin);
+ i.begin += msg.size();
+ assert(i.begin == i.end);
+
+#ifdef TORRENT_VERBOSE_LOGGING
+ std::stringstream ext;
+ handshake.print(ext);
+ (*m_logger) << "==> EXTENDED HANDSHAKE: \n" << ext.str();
+#endif
+
+ setup_send();
+ }
+
+ void bt_peer_connection::write_choke()
+ {
+ INVARIANT_CHECK;
+
+ if (is_choked()) return;
+ char msg[] = {0,0,0,1,msg_choke};
+ send_buffer(msg, msg + sizeof(msg));
+ }
+
+ void bt_peer_connection::write_unchoke()
+ {
+ INVARIANT_CHECK;
+
+ char msg[] = {0,0,0,1,msg_unchoke};
+ send_buffer(msg, msg + sizeof(msg));
+ }
+
+ void bt_peer_connection::write_interested()
+ {
+ INVARIANT_CHECK;
+
+ char msg[] = {0,0,0,1,msg_interested};
+ send_buffer(msg, msg + sizeof(msg));
+ }
+
+ void bt_peer_connection::write_not_interested()
+ {
+ INVARIANT_CHECK;
+
+ char msg[] = {0,0,0,1,msg_not_interested};
+ send_buffer(msg, msg + sizeof(msg));
+ }
+
+ void bt_peer_connection::write_have(int index)
+ {
+ assert(associated_torrent().lock()->valid_metadata());
+ assert(index >= 0);
+ assert(index < associated_torrent().lock()->torrent_file().num_pieces());
+ INVARIANT_CHECK;
+
+ const int packet_size = 9;
+ char msg[packet_size] = {0,0,0,5,msg_have};
+ char* ptr = msg + 5;
+ detail::write_int32(index, ptr);
+ send_buffer(msg, msg + packet_size);
+ }
+
+ void bt_peer_connection::write_piece(peer_request const& r)
+ {
+ INVARIANT_CHECK;
+
+ const int packet_size = 4 + 5 + 4 + r.length;
+
+ boost::shared_ptr<torrent> t = associated_torrent().lock();
+ assert(t);
+
+ buffer::interval i = allocate_send_buffer(packet_size);
+
+ detail::write_int32(packet_size-4, i.begin);
+ 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);
+
+ assert(i.begin + r.length == i.end);
+
+ m_payloads.push_back(range(send_buffer_size() - r.length, r.length));
+ setup_send();
+ }
+
+ // --------------------------
+ // RECEIVE DATA
+ // --------------------------
+
+ // throws exception when the client should be disconnected
+ void bt_peer_connection::on_receive(const asio::error& error
+ , std::size_t bytes_transferred)
+ {
+ INVARIANT_CHECK;
+
+ if (error) return;
+
+ buffer::const_interval recv_buffer = receive_buffer();
+
+ boost::shared_ptr<torrent> t = associated_torrent().lock();
+
+ switch(m_state)
+ {
+ case read_protocol_length:
+ {
+ m_statistics.received_bytes(0, bytes_transferred);
+ if (!packet_finished()) break;
+
+ int packet_size = recv_buffer[0];
+
+#ifdef TORRENT_VERBOSE_LOGGING
+ (*m_logger) << " protocol length: " << packet_size << "\n";
+#endif
+ if (packet_size > 100 || packet_size <= 0)
+ {
+ std::stringstream s;
+ s << "incorrect protocol length ("
+ << packet_size
+ << ") should be 19.";
+ throw std::runtime_error(s.str());
+ }
+ m_state = read_protocol_string;
+ reset_recv_buffer(packet_size);
+ }
+ break;
+
+ case read_protocol_string:
+ {
+ m_statistics.received_bytes(0, bytes_transferred);
+ if (!packet_finished()) break;
+
+#ifdef TORRENT_VERBOSE_LOGGING
+ (*m_logger) << " protocol: '" << std::string(recv_buffer.begin
+ , recv_buffer.end) << "'\n";
+#endif
+ const char protocol_string[] = "BitTorrent protocol";
+ if (!std::equal(recv_buffer.begin, recv_buffer.end
+ , protocol_string))
+ {
+ const char cmd[] = "version";
+ if (recv_buffer.end - recv_buffer.begin == 7 && std::equal(
+ recv_buffer.begin, recv_buffer.end, cmd))
+ {
+#ifdef TORRENT_VERBOSE_LOGGING
+ (*m_logger) << "sending libtorrent version\n";
+#endif
+ asio::write(*get_socket(), asio::buffer("libtorrent version " LIBTORRENT_VERSION "\n", 27));
+ throw std::runtime_error("closing");
+ }
+#ifdef TORRENT_VERBOSE_LOGGING
+ (*m_logger) << "incorrect protocol name\n";
+#endif
+ std::stringstream s;
+ s << "got invalid protocol name: '"
+ << std::string(recv_buffer.begin, recv_buffer.end)
+ << "'";
+ throw std::runtime_error(s.str());
+ }
+
+ m_state = read_info_hash;
+ reset_recv_buffer(28);
+ }
+ break;
+
+ case read_info_hash:
+ {
+ m_statistics.received_bytes(0, bytes_transferred);
+ if (!packet_finished()) break;
+
+// MassaRoddel
+#ifdef TORRENT_VERBOSE_LOGGING
+ for (int i=0; i < 8; ++i)
+ {
+ for (int j=0; j < 8; ++j)
+ {
+ if (recv_buffer[i] & (0x80 >> j)) (*m_logger) << "1";
+ else (*m_logger) << "0";
+ }
+ }
+ (*m_logger) << "\n";
+ if (recv_buffer[7] & 0x01)
+ (*m_logger) << "supports DHT port message\n";
+ if (recv_buffer[7] & 0x02)
+ (*m_logger) << "supports XBT peer exchange message\n";
+ if (recv_buffer[5] & 0x10)
+ (*m_logger) << "supports LT/uT extensions\n";
+#endif
+
+ if ((recv_buffer[5] & 0x10) && m_ses.extensions_enabled())
+ m_supports_extensions = true;
+ if (recv_buffer[7] & 0x01)
+ m_supports_dht_port = true;
+
+ // ok, now we have got enough of the handshake. Is this connection
+ // attached to a torrent?
+ if (!t)
+ {
+ // now, we have to see if there's a torrent with the
+ // info_hash we got from the peer
+ sha1_hash info_hash;
+ std::copy(recv_buffer.begin + 8, recv_buffer.begin + 28
+ , (char*)info_hash.begin());
+
+ attach_to_torrent(info_hash);
+ t = associated_torrent().lock();
+ assert(t);
+
+ assert(t->get_policy().has_connection(this));
+
+ // yes, we found the torrent
+ // reply with our handshake
+ write_handshake();
+ write_bitfield(t->pieces());
+ }
+ else
+ {
+ // verify info hash
+ if (!std::equal(recv_buffer.begin + 8, recv_buffer.begin + 28
+ , (const char*)t->torrent_file().info_hash().begin()))
+ {
+#ifdef TORRENT_VERBOSE_LOGGING
+ (*m_logger) << " received invalid info_hash\n";
+#endif
+ throw std::runtime_error("invalid info-hash in handshake");
+ }
+ }
+
+#ifndef TORRENT_DISABLE_DHT
+ if (m_supports_dht_port && m_ses.m_dht)
+ write_dht_port(m_ses.kad_settings().service_port);
+#endif
+
+ m_state = read_peer_id;
+ reset_recv_buffer(20);
+#ifdef TORRENT_VERBOSE_LOGGING
+ (*m_logger) << " info_hash received\n";
+#endif
+ }
+ break;
+
+ case read_peer_id:
+ {
+ if (!t) return;
+ m_statistics.received_bytes(0, bytes_transferred);
+ if (!packet_finished()) break;
+ assert(packet_size() == 20);
+
+#ifdef TORRENT_VERBOSE_LOGGING
+ {
+ peer_id tmp;
+ std::copy(recv_buffer.begin, recv_buffer.begin + 20, (char*)tmp.begin());
+ std::stringstream s;
+ s << "received peer_id: " << tmp << " client: " << identify_client(tmp) << "\n";
+ s << "as ascii: ";
+ for (peer_id::iterator i = tmp.begin(); i != tmp.end(); ++i)
+ {
+ if (std::isprint(*i)) s << *i;
+ else s << ".";
+ }
+ s << "\n";
+ (*m_logger) << s.str();
+ }
+#endif
+ peer_id pid;
+ std::copy(recv_buffer.begin, recv_buffer.begin + 20, (char*)pid.begin());
+ set_pid(pid);
+
+ m_client_version = identify_client(pid);
+ boost::optional<fingerprint> f = client_fingerprint(pid);
+ if (f && std::equal(f->name, f->name + 2, "BC"))
+ {
+ // if this is a bitcomet client, lower the request queue size limit
+ if (m_max_out_request_queue > 50) m_max_out_request_queue = 50;
+ }
+
+ // disconnect if the peer has the same peer-id as ourself
+ // since it most likely is ourself then
+ if (pid == m_ses.get_peer_id())
+ throw std::runtime_error("closing connection to ourself");
+
+ if (m_supports_extensions) write_extensions();
+/*
+ if (!m_active)
+ {
+ m_attached_to_torrent = true;
+ assert(m_torrent->get_policy().has_connection(this));
+ }
+*/
+ m_state = read_packet_size;
+ reset_recv_buffer(4);
+ }
+ break;
+
+ case read_packet_size:
+ {
+ if (!t) return;
+ m_statistics.received_bytes(0, bytes_transferred);
+ if (!packet_finished()) break;
+
+ const char* ptr = recv_buffer.begin;
+ int packet_size = detail::read_int32(ptr);
+
+ // don't accept packets larger than 1 MB
+ if (packet_size > 1024*1024 || packet_size < 0)
+ {
+ // packet too large
+ throw std::runtime_error("packet > 1 MB ("
+ + boost::lexical_cast<std::string>(
+ (unsigned int)packet_size) + " bytes)");
+ }
+
+ if (packet_size == 0)
+ {
+ incoming_keepalive();
+ // keepalive message
+ m_state = read_packet_size;
+ reset_recv_buffer(4);
+ }
+ else
+ {
+ m_state = read_packet;
+ reset_recv_buffer(packet_size);
+ }
+ }
+ break;
+
+ case read_packet:
+ {
+ if (!t) return;
+ if (dispatch_message(bytes_transferred))
+ {
+ m_state = read_packet_size;
+ reset_recv_buffer(4);
+ }
+ }
+ break;
+
+ }
+ }
+
+ // --------------------------
+ // SEND DATA
+ // --------------------------
+
+ // throws exception when the client should be disconnected
+ void bt_peer_connection::on_sent(asio::error const& error
+ , std::size_t bytes_transferred)
+ {
+ INVARIANT_CHECK;
+
+ if (error) return;
+
+ // manage the payload markers
+ int amount_payload = 0;
+ if (!m_payloads.empty())
+ {
+ for (std::deque<range>::iterator i = m_payloads.begin();
+ i != m_payloads.end(); ++i)
+ {
+ i->start -= bytes_transferred;
+ if (i->start < 0)
+ {
+ if (i->start + i->length <= 0)
+ {
+ amount_payload += i->length;
+ }
+ else
+ {
+ amount_payload += -i->start;
+ i->length -= -i->start;
+ i->start = 0;
+ }
+ }
+ }
+ }
+
+ // TODO: move the erasing into the loop above
+ // remove all payload ranges that has been sent
+ m_payloads.erase(
+ std::remove_if(m_payloads.begin(), m_payloads.end(), range_below_zero)
+ , m_payloads.end());
+
+ assert(amount_payload <= (int)bytes_transferred);
+ m_statistics.sent_bytes(amount_payload, bytes_transferred - amount_payload);
+ }
+
+
+ void bt_peer_connection::on_tick()
+ {
+ boost::shared_ptr<torrent> t = associated_torrent().lock();
+ if (!t) return;
+
+ // if we don't have any metadata, and this peer
+ // supports the request metadata extension
+ // and we aren't currently waiting for a request
+ // reply. Then, send a request for some metadata.
+ if (!t->valid_metadata()
+ && supports_extension(extended_metadata_message)
+ && !m_waiting_metadata_request
+ && has_metadata())
+ {
+ m_last_metadata_request = t->metadata_request();
+ write_metadata_request(m_last_metadata_request);
+ m_waiting_metadata_request = true;
+ m_metadata_request = second_clock::universal_time();
+ }
+ }
+
+#ifndef NDEBUG
+ void bt_peer_connection::check_invariant() const
+ {
+ if (!m_in_constructor)
+ peer_connection::check_invariant();
+
+ if (!m_payloads.empty())
+ {
+ for (std::deque<range>::const_iterator i = m_payloads.begin();
+ i != m_payloads.end() - 1; ++i)
+ {
+ assert(i->start + i->length <= (i+1)->start);
+ }
+ }
+ }
+#endif
+
+}
+
diff --git a/library/control b/library/control
new file mode 100644
index 000000000..ac29e820e
--- /dev/null
+++ b/library/control
@@ -0,0 +1,9 @@
+Package: python-libtorrent
+Version: 0.2.99-1-i386
+Section: base
+Priority: optional
+Architecture: all
+Depends: python, libboost-filesystem1.33.1, libboost-date-time1.33.1, libboost-program-options1.33.1, libboost-regex1.33.1, libboost-thread1.33.1, libc6-dev, zlib1g-dev
+Maintainer: A. Zakai ('Kripken') <kripkensteiner@gmail.com>
+Description: A Python wrapper for the (Sourceforge, not Rakshasa!) libtorrent C++ library.
+ URL: http://code.google.com/p/python-libtorrent/
diff --git a/library/debianit b/library/debianit
new file mode 100755
index 000000000..463b266fd
--- /dev/null
+++ b/library/debianit
@@ -0,0 +1,6 @@
+#svn rm ./debs/*.deb
+mkdir ./debian/usr/lib/python2.4/site-packages/
+mkdir debs
+cp ./build/lib.linux-i686-2.4/* ./debian/usr/lib/python2.4/site-packages/
+dpkg-deb --build debian ./debs/python-libtorrent_0.3.2_i386.deb
+#svn add ./debs/*.deb
diff --git a/library/entry.cpp b/library/entry.cpp
new file mode 100755
index 000000000..02d86a80f
--- /dev/null
+++ b/library/entry.cpp
@@ -0,0 +1,344 @@
+/*
+
+Copyright (c) 2003, 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.
+
+*/
+
+#include <algorithm>
+#include <iomanip>
+#include "libtorrent/entry.hpp"
+#include "libtorrent/config.hpp"
+#include <boost/bind.hpp>
+#include <boost/next_prior.hpp>
+
+#if defined(_MSC_VER)
+namespace std
+{
+ using ::isprint;
+}
+#define for if (false) {} else for
+#endif
+
+namespace
+{
+ template <class T>
+ void call_destructor(T* o)
+ {
+ assert(o);
+ o->~T();
+ }
+
+ struct compare_string
+ {
+ compare_string(char const* s): m_str(s) {}
+
+ bool operator()(
+ std::pair<std::string
+ , libtorrent::entry> const& e) const
+ {
+ return m_str && e.first == m_str;
+ }
+ char const* m_str;
+ };
+}
+
+namespace libtorrent
+{
+ namespace detail
+ {
+ TORRENT_EXPORT char const* integer_to_str(char* buf, int size, entry::integer_type val)
+ {
+ int sign = 0;
+ if (val < 0)
+ {
+ sign = 1;
+ val = -val;
+ }
+ buf[--size] = '\0';
+ if (val == 0) buf[--size] = '0';
+ for (; size > sign && val != 0;)
+ {
+ buf[--size] = '0' + char(val % 10);
+ val /= 10;
+ }
+ if (sign) buf[--size] = '-';
+ return buf + size;
+ }
+ }
+
+ entry& entry::operator[](char const* key)
+ {
+ dictionary_type::iterator i = dict().find(key);
+ if (i != dict().end()) return i->second;
+ dictionary_type::iterator ret = dict().insert(
+ dict().begin()
+ , std::make_pair(std::string(key), entry()));
+ return ret->second;
+ }
+
+
+ entry& entry::operator[](std::string const& key)
+ {
+ return (*this)[key.c_str()];
+ }
+
+ entry* entry::find_key(char const* key)
+ {
+ dictionary_type::iterator i = std::find_if(
+ dict().begin()
+ , dict().end()
+ , compare_string(key));
+ if (i == dict().end()) return 0;
+ return &i->second;
+
+ }
+
+ entry const* entry::find_key(char const* key) const
+ {
+ dictionary_type::const_iterator i = dict().find(key);
+ if (i == dict().end()) return 0;
+ return &i->second;
+ }
+
+ const entry& entry::operator[](char const* key) const
+ {
+ dictionary_type::const_iterator i = dict().find(key);
+ if (i == dict().end()) throw type_error(
+ (std::string("key not found: ") + key).c_str());
+ return i->second;
+ }
+
+ const entry& entry::operator[](std::string const& key) const
+ {
+ return (*this)[key.c_str()];
+ }
+
+ entry::entry(const dictionary_type& v)
+ {
+ new(data) dictionary_type(v);
+ m_type = dictionary_t;
+ }
+
+ entry::entry(const string_type& v)
+ {
+ new(data) string_type(v);
+ m_type = string_t;
+ }
+
+ entry::entry(const list_type& v)
+ {
+ new(data) list_type(v);
+ m_type = list_t;
+ }
+
+ entry::entry(const integer_type& v)
+ {
+ new(data) integer_type(v);
+ m_type = int_t;
+ }
+
+ void entry::operator=(const dictionary_type& v)
+ {
+ destruct();
+ new(data) dictionary_type(v);
+ m_type = dictionary_t;
+ }
+
+ void entry::operator=(const string_type& v)
+ {
+ destruct();
+ new(data) string_type(v);
+ m_type = string_t;
+ }
+
+ void entry::operator=(const list_type& v)
+ {
+ destruct();
+ new(data) list_type(v);
+ m_type = list_t;
+ }
+
+ void entry::operator=(const integer_type& v)
+ {
+ destruct();
+ new(data) integer_type(v);
+ m_type = int_t;
+ }
+
+ bool entry::operator==(entry const& e) const
+ {
+ if (m_type != e.m_type) return false;
+
+ switch(m_type)
+ {
+ case int_t:
+ return integer() == e.integer();
+ case string_t:
+ return string() == e.string();
+ case list_t:
+ return list() == e.list();
+ case dictionary_t:
+ return dict() == e.dict();
+ default:
+ assert(m_type == undefined_t);
+ return true;
+ }
+ }
+
+ void entry::construct(data_type t)
+ {
+ m_type = t;
+ switch(m_type)
+ {
+ case int_t:
+ new(data) integer_type;
+ break;
+ case string_t:
+ new(data) string_type;
+ break;
+ case list_t:
+ new(data) list_type;
+ break;
+ case dictionary_t:
+ new (data) dictionary_type;
+ break;
+ default:
+ assert(m_type == undefined_t);
+ m_type = undefined_t;
+ }
+ }
+
+ void entry::copy(const entry& e)
+ {
+ m_type = e.m_type;
+ switch(m_type)
+ {
+ case int_t:
+ new(data) integer_type(e.integer());
+ break;
+ case string_t:
+ new(data) string_type(e.string());
+ break;
+ case list_t:
+ new(data) list_type(e.list());
+ break;
+ case dictionary_t:
+ new (data) dictionary_type(e.dict());
+ break;
+ default:
+ m_type = undefined_t;
+ }
+ }
+
+ void entry::destruct()
+ {
+ switch(m_type)
+ {
+ case int_t:
+ call_destructor(reinterpret_cast<integer_type*>(data));
+ break;
+ case string_t:
+ call_destructor(reinterpret_cast<string_type*>(data));
+ break;
+ case list_t:
+ call_destructor(reinterpret_cast<list_type*>(data));
+ break;
+ case dictionary_t:
+ call_destructor(reinterpret_cast<dictionary_type*>(data));
+ break;
+ default:
+ assert(m_type == undefined_t);
+ break;
+ }
+ }
+
+ void entry::print(std::ostream& os, int indent) const
+ {
+ assert(indent >= 0);
+ for (int i = 0; i < indent; ++i) os << " ";
+ switch (m_type)
+ {
+ case int_t:
+ os << integer() << "\n";
+ break;
+ case string_t:
+ {
+ bool binary_string = false;
+ for (std::string::const_iterator i = string().begin(); i != string().end(); ++i)
+ {
+ if (!std::isprint(static_cast<unsigned char>(*i)))
+ {
+ binary_string = true;
+ break;
+ }
+ }
+ if (binary_string)
+ {
+ os.unsetf(std::ios_base::dec);
+ os.setf(std::ios_base::hex);
+ for (std::string::const_iterator i = string().begin(); i != string().end(); ++i)
+ os << std::setfill('0') << std::setw(2)
+ << static_cast<unsigned int>((unsigned char)*i);
+ os.unsetf(std::ios_base::hex);
+ os.setf(std::ios_base::dec);
+ os << "\n";
+ }
+ else
+ {
+ os << string() << "\n";
+ }
+ } break;
+ case list_t:
+ {
+ os << "list\n";
+ for (list_type::const_iterator i = list().begin(); i != list().end(); ++i)
+ {
+ i->print(os, indent+1);
+ }
+ } break;
+ case dictionary_t:
+ {
+ os << "dictionary\n";
+ for (dictionary_type::const_iterator i = dict().begin(); i != dict().end(); ++i)
+ {
+ for (int j = 0; j < indent+1; ++j) os << " ";
+ os << "[" << i->first << "]";
+ if (i->second.type() != entry::string_t
+ && i->second.type() != entry::int_t)
+ os << "\n";
+ else os << " ";
+ i->second.print(os, indent+2);
+ }
+ } break;
+ default:
+ os << "<uninitialized>\n";
+ }
+ }
+}
+
diff --git a/library/errorlog.txt b/library/errorlog.txt
new file mode 100644
index 000000000..310f1be0e
--- /dev/null
+++ b/library/errorlog.txt
@@ -0,0 +1,6 @@
+Saturday, November, 04, 2006 11:38 AM: Unable to open state.txt, using defaults.
+Saturday, November, 04, 2006 11:38 AM: Cannot find preset.txt, using only standard E tuning
+Saturday, November, 04, 2006 11:38 AM: Cannot find file APSmall.bmp
+Saturday, November, 04, 2006 11:38 AM: Unable to open state.txt, using defaults.
+Saturday, November, 04, 2006 11:38 AM: Cannot find preset.txt, using only standard E tuning
+Saturday, November, 04, 2006 11:38 AM: Cannot find file APSmall.bmp
diff --git a/library/escape_string.cpp b/library/escape_string.cpp
new file mode 100755
index 000000000..80d134ed7
--- /dev/null
+++ b/library/escape_string.cpp
@@ -0,0 +1,148 @@
+/*
+
+Copyright (c) 2003, 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.
+
+*/
+
+#include <string>
+#include <cassert>
+#include <stdexcept>
+#include <sstream>
+#include <iomanip>
+#include <cctype>
+#include <algorithm>
+
+namespace libtorrent
+{
+ std::string unescape_string(std::string const& s)
+ {
+ std::string ret;
+ for (std::string::const_iterator i = s.begin(); i != s.end(); ++i)
+ {
+ if(*i == '+')
+ {
+ ret += ' ';
+ }
+ else if (*i != '%')
+ {
+ ret += *i;
+ }
+ else
+ {
+ ++i;
+ if (i == s.end())
+ throw std::runtime_error("invalid escaped string");
+
+ int high;
+ if(*i >= '0' && *i <= '9') high = *i - '0';
+ else if(*i >= 'A' && *i <= 'F') high = *i + 10 - 'A';
+ else if(*i >= 'a' && *i <= 'f') high = *i + 10 - 'a';
+ else throw std::runtime_error("invalid escaped string");
+
+ ++i;
+ if (i == s.end())
+ throw std::runtime_error("invalid escaped string");
+
+ int low;
+ if(*i >= '0' && *i <= '9') low = *i - '0';
+ else if(*i >= 'A' && *i <= 'F') low = *i + 10 - 'A';
+ else if(*i >= 'a' && *i <= 'f') low = *i + 10 - 'a';
+ else throw std::runtime_error("invalid escaped string");
+
+ ret += char(high * 16 + low);
+ }
+ }
+ return ret;
+ }
+
+
+ std::string escape_string(const char* str, int len)
+ {
+ assert(str != 0);
+ assert(len >= 0);
+ // http://www.ietf.org/rfc/rfc2396.txt
+ // section 2.3
+ // some trackers seems to require that ' is escaped
+// static const char unreserved_chars[] = "-_.!~*'()";
+ static const char unreserved_chars[] = "-_.!~*()"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
+ "0123456789";
+
+ std::stringstream ret;
+ ret << std::hex << std::setfill('0');
+ for (int i = 0; i < len; ++i)
+ {
+ if (std::count(
+ unreserved_chars
+ , unreserved_chars+sizeof(unreserved_chars)-1
+ , *str))
+ {
+ ret << *str;
+ }
+ else
+ {
+ ret << '%'
+ << std::setw(2)
+ << (int)static_cast<unsigned char>(*str);
+ }
+ ++str;
+ }
+ return ret.str();
+ }
+
+ std::string escape_path(const char* str, int len)
+ {
+ assert(str != 0);
+ assert(len >= 0);
+ static const char unreserved_chars[] = "/-_.!~*()"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
+ "0123456789";
+
+ std::stringstream ret;
+ ret << std::hex << std::setfill('0');
+ for (int i = 0; i < len; ++i)
+ {
+ if (std::count(
+ unreserved_chars
+ , unreserved_chars+sizeof(unreserved_chars)-1
+ , *str))
+ {
+ ret << *str;
+ }
+ else
+ {
+ ret << '%'
+ << std::setw(2)
+ << (int)static_cast<unsigned char>(*str);
+ }
+ ++str;
+ }
+ return ret.str();
+ }
+}
diff --git a/library/file.cpp b/library/file.cpp
new file mode 100755
index 000000000..af8404660
--- /dev/null
+++ b/library/file.cpp
@@ -0,0 +1,313 @@
+/*
+
+Copyright (c) 2003, 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.
+
+*/
+
+#ifdef _WIN32
+// windows part
+#include "libtorrent/utf8.hpp"
+
+#include <io.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#ifndef _MODE_T_
+typedef int mode_t;
+#endif
+
+#ifdef UNICODE
+#include "libtorrent/storage.hpp"
+#endif
+
+#else
+// unix part
+#define _FILE_OFFSET_BITS 64
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <errno.h>
+
+#include <boost/static_assert.hpp>
+// make sure the _FILE_OFFSET_BITS define worked
+// on this platform
+BOOST_STATIC_ASSERT(sizeof(lseek(0, 0, 0)) >= 8);
+
+#endif
+
+#include <boost/filesystem/operations.hpp>
+#include "libtorrent/file.hpp"
+#include <sstream>
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
+#ifndef O_RANDOM
+#define O_RANDOM 0
+#endif
+
+#ifdef UNICODE
+#include "libtorrent/storage.hpp"
+#endif
+
+
+namespace fs = boost::filesystem;
+
+namespace
+{
+ enum { mode_in = 1, mode_out = 2 };
+
+ mode_t map_open_mode(int m)
+ {
+ if (m == (mode_in | mode_out)) return O_RDWR | O_CREAT | O_BINARY | O_RANDOM;
+ if (m == mode_out) return O_WRONLY | O_CREAT | O_BINARY | O_RANDOM;
+ if (m == mode_in) return O_RDONLY | O_BINARY | O_RANDOM;
+ assert(false);
+ return 0;
+ }
+
+#ifdef WIN32
+ std::string utf8_native(std::string const& s)
+ {
+ try
+ {
+ std::wstring ws;
+ libtorrent::utf8_wchar(s, ws);
+ std::size_t size = wcstombs(0, ws.c_str(), 0);
+ if (size == std::size_t(-1)) return s;
+ std::string ret;
+ ret.resize(size);
+ size = wcstombs(&ret[0], ws.c_str(), size + 1);
+ if (size == wchar_t(-1)) return s;
+ ret.resize(size);
+ return ret;
+ }
+ catch(std::exception)
+ {
+ return s;
+ }
+ }
+#else
+ std::string utf8_native(std::string const& s)
+ {
+ return s;
+ }
+#endif
+
+}
+
+namespace libtorrent
+{
+
+ const file::open_mode file::in(mode_in);
+ const file::open_mode file::out(mode_out);
+
+ const file::seek_mode file::begin(1);
+ const file::seek_mode file::end(2);
+
+ struct file::impl
+ {
+ impl()
+ : m_fd(-1)
+ , m_open_mode(0)
+ {}
+
+ impl(fs::path const& path, int mode)
+ : m_fd(-1)
+ , m_open_mode(0)
+ {
+ open(path, mode);
+ }
+
+ ~impl()
+ {
+ close();
+ }
+
+ void open(fs::path const& path, int mode)
+ {
+ assert(path.is_complete());
+ close();
+#if defined(_WIN32) && defined(UNICODE)
+ std::wstring wpath(safe_convert(path.native_file_string()));
+ m_fd = ::_wopen(
+ wpath.c_str()
+ , map_open_mode(mode)
+ , S_IREAD | S_IWRITE);
+#else
+ m_fd = ::open(
+ utf8_native(path.native_file_string()).c_str()
+ , map_open_mode(mode)
+#ifdef _WIN32
+ , S_IREAD | S_IWRITE);
+#else
+ , S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+#endif
+#endif
+ if (m_fd == -1)
+ {
+ std::stringstream msg;
+ msg << "open failed: '" << path.native_file_string() << "'. "
+ << strerror(errno);
+ throw file_error(msg.str());
+ }
+ m_open_mode = mode;
+ }
+
+ void close()
+ {
+ if (m_fd == -1) return;
+
+ ::close(m_fd);
+ m_fd = -1;
+ m_open_mode = 0;
+ }
+
+ size_type read(char* buf, size_type num_bytes)
+ {
+ assert(m_open_mode & mode_in);
+ assert(m_fd != -1);
+
+ size_type ret = ::read(m_fd, buf, num_bytes);
+ if (ret == -1)
+ {
+ std::stringstream msg;
+ msg << "read failed: " << strerror(errno);
+ throw file_error(msg.str());
+ }
+ return ret;
+ }
+
+ size_type write(const char* buf, size_type num_bytes)
+ {
+ assert(m_open_mode & mode_out);
+ assert(m_fd != -1);
+
+ // TODO: Test this a bit more, what happens with random failures in
+ // the files?
+// if ((rand() % 100) > 80)
+// throw file_error("debug");
+
+ size_type ret = ::write(m_fd, buf, num_bytes);
+ if (ret == -1)
+ {
+ std::stringstream msg;
+ msg << "write failed: " << strerror(errno);
+ throw file_error(msg.str());
+ }
+ return ret;
+ }
+
+ size_type seek(size_type offset, int m)
+ {
+ assert(m_open_mode);
+ assert(m_fd != -1);
+
+ int seekdir = (m == 1)?SEEK_SET:SEEK_END;
+#ifdef _WIN32
+ size_type ret = _lseeki64(m_fd, offset, seekdir);
+#else
+ size_type ret = lseek(m_fd, offset, seekdir);
+#endif
+
+ // For some strange reason this fails
+ // on win32. Use windows specific file
+ // wrapper instead.
+ if (ret == -1)
+ {
+ std::stringstream msg;
+ msg << "seek failed: '" << strerror(errno)
+ << "' fd: " << m_fd
+ << " offset: " << offset
+ << " seekdir: " << seekdir;
+ throw file_error(msg.str());
+ }
+ return ret;
+ }
+
+ size_type tell()
+ {
+ assert(m_open_mode);
+ assert(m_fd != -1);
+
+#ifdef _WIN32
+ return _telli64(m_fd);
+#else
+ return lseek(m_fd, 0, SEEK_CUR);
+#endif
+ }
+
+ int m_fd;
+ int m_open_mode;
+ };
+
+ // pimpl forwardings
+
+ file::file() : m_impl(new impl()) {}
+
+ file::file(boost::filesystem::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)
+ {
+ m_impl->open(p, m.m_mask);
+ }
+
+ void file::close()
+ {
+ m_impl->close();
+ }
+
+ size_type file::write(const char* buf, size_type num_bytes)
+ {
+ return m_impl->write(buf, num_bytes);
+ }
+
+ size_type file::read(char* buf, size_type num_bytes)
+ {
+ return m_impl->read(buf, num_bytes);
+ }
+
+ size_type file::seek(size_type pos, file::seek_mode m)
+ {
+ return m_impl->seek(pos, m.m_val);
+ }
+
+ size_type file::tell()
+ {
+ return m_impl->tell();
+ }
+
+}
diff --git a/library/http_tracker_connection.cpp b/library/http_tracker_connection.cpp
new file mode 100755
index 000000000..3299cf5b8
--- /dev/null
+++ b/library/http_tracker_connection.cpp
@@ -0,0 +1,845 @@
+/*
+
+Copyright (c) 2003, 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.
+
+*/
+
+#include <vector>
+#include <iostream>
+#include <cctype>
+#include <iomanip>
+#include <sstream>
+
+#include "zlib.h"
+
+#ifdef _MSC_VER
+#pragma warning(push, 1)
+#endif
+
+#include <boost/bind.hpp>
+
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+#include "libtorrent/tracker_manager.hpp"
+#include "libtorrent/http_tracker_connection.hpp"
+#include "libtorrent/entry.hpp"
+#include "libtorrent/bencode.hpp"
+#include "libtorrent/torrent.hpp"
+#include "libtorrent/io.hpp"
+
+using namespace libtorrent;
+using boost::bind;
+
+namespace
+{
+ enum
+ {
+ minimum_tracker_response_length = 3,
+ http_buffer_size = 2048
+ };
+
+
+ enum
+ {
+ FTEXT = 0x01,
+ FHCRC = 0x02,
+ FEXTRA = 0x04,
+ FNAME = 0x08,
+ FCOMMENT = 0x10,
+ FRESERVED = 0xe0,
+
+ GZIP_MAGIC0 = 0x1f,
+ GZIP_MAGIC1 = 0x8b
+ };
+
+}
+
+using namespace boost::posix_time;
+
+namespace libtorrent
+{
+ http_parser::http_parser()
+ : m_recv_pos(0)
+ , m_status_code(-1)
+ , m_content_length(-1)
+ , m_content_encoding(plain)
+ , m_state(read_status)
+ , m_recv_buffer(0, 0)
+ , m_body_start_pos(0)
+ , m_finished(false)
+ {}
+
+ boost::tuple<int, int> http_parser::incoming(buffer::const_interval recv_buffer)
+ {
+ m_recv_buffer = recv_buffer;
+ boost::tuple<int, int> ret(0, 0);
+
+ char const* pos = recv_buffer.begin + m_recv_pos;
+ if (m_state == read_status)
+ {
+ assert(!m_finished);
+ char const* newline = std::find(pos, recv_buffer.end, '\n');
+ // if we don't have a full line yet, wait.
+ if (newline == recv_buffer.end) return ret;
+
+ if (newline == pos)
+ throw std::runtime_error("unexpected newline in HTTP response");
+
+ std::istringstream line(std::string(pos, newline - 1));
+ ++newline;
+ int incoming = (int)std::distance(pos, newline);
+ m_recv_pos += incoming;
+ boost::get<1>(ret) += incoming;
+ pos = newline;
+
+ line >> m_protocol;
+ if (m_protocol.substr(0, 5) != "HTTP/")
+ {
+ throw std::runtime_error("unknown protocol in HTTP response: "
+ + m_protocol);
+ }
+ line >> m_status_code;
+ std::getline(line, m_server_message);
+ m_state = read_header;
+ }
+
+ if (m_state == read_header)
+ {
+ assert(!m_finished);
+ char const* newline = std::find(pos, recv_buffer.end, '\n');
+ std::string line;
+
+ while (newline != recv_buffer.end && m_state == read_header)
+ {
+ if (newline == pos)
+ throw std::runtime_error("unexpected newline in HTTP response");
+
+ line.assign(pos, newline - 1);
+ m_recv_pos += newline - pos;
+ boost::get<1>(ret) += newline - pos;
+ pos = newline;
+
+ std::string::size_type separator = line.find(": ");
+ if (separator == std::string::npos)
+ {
+ ++pos;
+ ++m_recv_pos;
+ boost::get<1>(ret) += 1;
+
+ m_state = read_body;
+ m_body_start_pos = m_recv_pos;
+ break;
+ }
+
+ std::string name = line.substr(0, separator);
+ std::string value = line.substr(separator + 2, std::string::npos);
+ m_header.insert(std::make_pair(name, value));
+
+ if (name == "Content-Length")
+ {
+ try
+ {
+ m_content_length = boost::lexical_cast<int>(value);
+ }
+ catch(boost::bad_lexical_cast&) {}
+ }
+ else if (name == "Content-Encoding")
+ {
+ if (value == "gzip" || value == "x-gzip")
+ {
+ m_content_encoding = gzip;
+ }
+ else
+ {
+ std::string error_str = "unknown content encoding in response: \"";
+ error_str += value;
+ error_str += "\"";
+ throw std::runtime_error(error_str);
+ }
+ }
+ // TODO: make sure we don't step outside of the buffer
+ ++pos;
+ ++m_recv_pos;
+ assert(m_recv_pos <= (int)recv_buffer.left());
+ newline = std::find(pos, recv_buffer.end, '\n');
+ }
+ }
+
+ if (m_state == read_body)
+ {
+ int incoming = recv_buffer.end - pos;
+ if (m_recv_pos - m_body_start_pos + incoming > m_content_length
+ && m_content_length >= 0)
+ incoming = m_content_length - m_recv_pos + m_body_start_pos;
+
+ assert(incoming >= 0);
+ m_recv_pos += incoming;
+ boost::get<0>(ret) += incoming;
+
+ if (m_content_length >= 0
+ && m_recv_pos - m_body_start_pos >= m_content_length)
+ {
+ m_finished = true;
+ }
+ }
+ return ret;
+ }
+
+ buffer::const_interval http_parser::get_body()
+ {
+ char const* body_begin = m_recv_buffer.begin + m_body_start_pos;
+ char const* body_end = m_recv_buffer.begin + m_recv_pos;
+
+ m_recv_pos = 0;
+ m_body_start_pos = 0;
+ m_status_code = -1;
+ m_content_length = -1;
+ m_finished = false;
+ m_state = read_status;
+ m_header.clear();
+
+ return buffer::const_interval(body_begin, body_end);
+ }
+
+ http_tracker_connection::http_tracker_connection(
+ demuxer& d
+ , tracker_manager& man
+ , tracker_request const& req
+ , std::string const& hostname
+ , unsigned short port
+ , std::string request
+ , boost::weak_ptr<request_callback> c
+ , session_settings const& stn
+ , std::string const& auth)
+ : tracker_connection(man, req, d, c)
+ , m_man(man)
+ , m_state(read_status)
+ , m_content_encoding(plain)
+ , m_content_length(0)
+ , m_name_lookup(d)
+ , m_port(port)
+ , m_recv_pos(0)
+ , m_buffer(http_buffer_size)
+ , m_settings(stn)
+ , m_password(auth)
+ , m_code(0)
+ , m_timed_out(false)
+ {
+ const std::string* connect_to_host;
+ bool using_proxy = false;
+
+ m_send_buffer.assign("GET ");
+
+ // should we use the proxy?
+ if (!m_settings.proxy_ip.empty())
+ {
+ connect_to_host = &m_settings.proxy_ip;
+ using_proxy = true;
+ m_send_buffer += "http://";
+ m_send_buffer += hostname;
+ if (port != 80)
+ {
+ m_send_buffer += ":";
+ m_send_buffer += boost::lexical_cast<std::string>(port);
+ }
+ m_port = m_settings.proxy_port != 0
+ ? m_settings.proxy_port : 80 ;
+ }
+ else
+ {
+ connect_to_host = &hostname;
+ }
+
+ if (tracker_req().kind == tracker_request::scrape_request)
+ {
+ // find and replace "announce" with "scrape"
+ // in request
+
+ std::size_t pos = request.find("announce");
+ if (pos == std::string::npos)
+ throw std::runtime_error("scrape is not available on url: '"
+ + tracker_req().url +"'");
+ request.replace(pos, 8, "scrape");
+ }
+
+ m_send_buffer += request;
+
+ // if request-string already contains
+ // some parameters, append an ampersand instead
+ // of a question mark
+ if (request.find('?') != std::string::npos)
+ m_send_buffer += "&";
+ else
+ m_send_buffer += "?";
+
+ m_send_buffer += "info_hash=";
+ m_send_buffer += escape_string(
+ reinterpret_cast<const char*>(req.info_hash.begin()), 20);
+
+ if (tracker_req().kind == tracker_request::announce_request)
+ {
+ m_send_buffer += "&peer_id=";
+ m_send_buffer += escape_string(
+ reinterpret_cast<const char*>(req.pid.begin()), 20);
+
+ m_send_buffer += "&port=";
+ m_send_buffer += boost::lexical_cast<std::string>(req.listen_port);
+
+ m_send_buffer += "&uploaded=";
+ m_send_buffer += boost::lexical_cast<std::string>(req.uploaded);
+
+ m_send_buffer += "&downloaded=";
+ m_send_buffer += boost::lexical_cast<std::string>(req.downloaded);
+
+ m_send_buffer += "&left=";
+ m_send_buffer += boost::lexical_cast<std::string>(req.left);
+
+ if (req.event != tracker_request::none)
+ {
+ const char* event_string[] = {"completed", "started", "stopped"};
+ m_send_buffer += "&event=";
+ m_send_buffer += event_string[req.event - 1];
+ }
+ m_send_buffer += "&key=";
+ std::stringstream key_string;
+ key_string << std::hex << req.key;
+ m_send_buffer += key_string.str();
+ m_send_buffer += "&compact=1";
+ m_send_buffer += "&numwant=";
+ m_send_buffer += boost::lexical_cast<std::string>(
+ std::min(req.num_want, 999));
+
+ // extension that tells the tracker that
+ // we don't need any peer_id's in the response
+ m_send_buffer += "&no_peer_id=1";
+ }
+
+ m_send_buffer += " HTTP/1.0\r\nAccept-Encoding: gzip\r\n"
+ "User-Agent: ";
+ m_send_buffer += m_settings.user_agent;
+ m_send_buffer += "\r\n"
+ "Host: ";
+ m_send_buffer += hostname;
+ if (port != 80)
+ {
+ m_send_buffer += ':';
+ m_send_buffer += boost::lexical_cast<std::string>(port);
+ }
+ if (using_proxy && !m_settings.proxy_login.empty())
+ {
+ m_send_buffer += "\r\nProxy-Authorization: Basic ";
+ m_send_buffer += base64encode(m_settings.proxy_login + ":" + m_settings.proxy_password);
+ }
+ if (auth != "")
+ {
+ m_send_buffer += "\r\nAuthorization: Basic ";
+ m_send_buffer += base64encode(auth);
+ }
+ m_send_buffer += "\r\n\r\n";
+#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
+ if (has_requester())
+ {
+ requester().debug_log("==> TRACKER_REQUEST [ str: " + m_send_buffer + " ]");
+ std::stringstream info_hash_str;
+ info_hash_str << req.info_hash;
+ requester().debug_log("info_hash: " + info_hash_str.str() + "\n");
+ }
+#endif
+
+ tcp::resolver::query q(*connect_to_host, "0");
+ m_name_lookup.async_resolve(q
+ , boost::bind(&http_tracker_connection::name_lookup, self(), _1, _2));
+ set_timeout(m_settings.tracker_completion_timeout
+ , m_settings.tracker_receive_timeout);
+ }
+
+ void http_tracker_connection::on_timeout()
+ {
+ m_timed_out = true;
+ m_socket.reset();
+ m_name_lookup.cancel();
+ fail_timeout();
+ }
+
+ void http_tracker_connection::name_lookup(asio::error const& error
+ , tcp::resolver::iterator i) try
+ {
+ if (error == asio::error::operation_aborted) return;
+ if (m_timed_out) return;
+
+ if (error || i == tcp::resolver::iterator())
+ {
+ fail(-1, error.what());
+ return;
+ }
+
+#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
+ if (has_requester()) requester().debug_log("tracker name lookup successful");
+#endif
+ restart_read_timeout();
+ m_socket.reset(new stream_socket(m_name_lookup.io_service()));
+ tcp::endpoint a(i->endpoint().address(), m_port);
+ if (has_requester()) requester().m_tracker_address = a;
+ m_socket->async_connect(a, bind(&http_tracker_connection::connected, self(), _1));
+ }
+ catch (std::exception& e)
+ {
+ assert(false);
+ fail(-1, e.what());
+ };
+
+ void http_tracker_connection::connected(asio::error const& error) try
+ {
+ if (error == asio::error::operation_aborted) return;
+ if (m_timed_out) return;
+ if (error)
+ {
+ fail(-1, error.what());
+ return;
+ }
+
+#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
+ if (has_requester()) requester().debug_log("tracker connection successful");
+#endif
+
+ restart_read_timeout();
+ async_write(*m_socket, asio::buffer(m_send_buffer.c_str()
+ , m_send_buffer.size()), bind(&http_tracker_connection::sent
+ , self(), _1));
+ }
+ catch (std::exception& e)
+ {
+ assert(false);
+ fail(-1, e.what());
+ }
+
+ void http_tracker_connection::sent(asio::error const& error) try
+ {
+ if (error == asio::error::operation_aborted) return;
+ if (m_timed_out) return;
+ if (error)
+ {
+ fail(-1, error.what());
+ return;
+ }
+
+#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
+ if (has_requester()) requester().debug_log("tracker send data completed");
+#endif
+ restart_read_timeout();
+ assert(m_buffer.size() - m_recv_pos > 0);
+ m_socket->async_read_some(asio::buffer(&m_buffer[m_recv_pos]
+ , m_buffer.size() - m_recv_pos), bind(&http_tracker_connection::receive
+ , self(), _1, _2));
+ }
+ catch (std::exception& e)
+ {
+ assert(false);
+ fail(-1, e.what());
+ }; // msvc 7.1 seems to require this semi-colon
+
+
+ void http_tracker_connection::receive(asio::error const& error
+ , std::size_t bytes_transferred) try
+ {
+ if (error == asio::error::operation_aborted) return;
+ if (m_timed_out) return;
+
+ if (error)
+ {
+ if (error == asio::error::eof)
+ {
+ on_response();
+ close();
+ return;
+ }
+
+ fail(-1, error.what());
+ return;
+ }
+
+ restart_read_timeout();
+ assert(bytes_transferred > 0);
+#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
+ if (has_requester()) requester().debug_log("tracker connection reading "
+ + boost::lexical_cast<std::string>(bytes_transferred));
+#endif
+
+ m_recv_pos += bytes_transferred;
+
+ // if the receive buffer is full, expand it with http_buffer_size
+ if ((int)m_buffer.size() == m_recv_pos)
+ {
+ if ((int)m_buffer.size() >= m_settings.tracker_maximum_response_length)
+ {
+ fail(200, "too large tracker response");
+ return;
+ }
+ assert(http_buffer_size > 0);
+ if ((int)m_buffer.size() + http_buffer_size
+ > m_settings.tracker_maximum_response_length)
+ m_buffer.resize(m_settings.tracker_maximum_response_length);
+ else
+ m_buffer.resize(m_buffer.size() + http_buffer_size);
+ }
+
+ if (m_state == read_status)
+ {
+ std::vector<char>::iterator end = m_buffer.begin()+m_recv_pos;
+ std::vector<char>::iterator newline = std::find(m_buffer.begin(), end, '\n');
+ // if we don't have a full line yet, wait.
+ if (newline != end)
+ {
+
+#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
+ if (has_requester()) requester().debug_log(std::string(m_buffer.begin(), newline));
+#endif
+
+ std::istringstream line(std::string(m_buffer.begin(), newline));
+ ++newline;
+ m_recv_pos -= (int)std::distance(m_buffer.begin(), newline);
+ m_buffer.erase(m_buffer.begin(), newline);
+
+ std::string protocol;
+ line >> m_server_protocol;
+ if (m_server_protocol.substr(0, 5) != "HTTP/")
+ {
+ std::string error_msg = "unknown protocol in response: " + m_server_protocol;
+ fail(-1, error_msg.c_str());
+ return;
+ }
+ line >> m_code;
+ std::getline(line, m_server_message);
+ m_state = read_header;
+ }
+ }
+
+ if (m_state == read_header)
+ {
+ std::vector<char>::iterator end = m_buffer.begin() + m_recv_pos;
+ std::vector<char>::iterator newline
+ = std::find(m_buffer.begin(), end, '\n');
+ std::string line;
+
+ while (newline != end && m_state == read_header)
+ {
+ line.assign(m_buffer.begin(), newline);
+
+#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
+ if (has_requester()) requester().debug_log(line);
+#endif
+
+ if (line.substr(0, 16) == "Content-Length: ")
+ {
+ try
+ {
+ m_content_length = boost::lexical_cast<int>(
+ line.substr(16, line.length() - 17));
+ }
+ catch(boost::bad_lexical_cast&)
+ {
+ fail(-1, "invalid content-length in tracker response");
+ return;
+ }
+ if (m_content_length > m_settings.tracker_maximum_response_length)
+ {
+ fail(-1, "content-length is greater than maximum response length");
+ return;
+ }
+
+ if (m_content_length < minimum_tracker_response_length && m_code == 200)
+ {
+ fail(-1, "content-length is smaller than minimum response length");
+ return;
+ }
+ }
+ else if (line.substr(0, 18) == "Content-Encoding: ")
+ {
+ if (line.substr(18, 4) == "gzip" || line.substr(18, 6) == "x-gzip")
+ {
+ m_content_encoding = gzip;
+ }
+ else
+ {
+ std::string error_str = "unknown content encoding in response: \"";
+ error_str += line.substr(18, line.length() - 18 - 2);
+ error_str += "\"";
+ fail(-1, error_str.c_str());
+ return;
+ }
+ }
+ else if (line.substr(0, 10) == "Location: ")
+ {
+ m_location.assign(line.begin() + 10, line.end());
+ }
+ else if (line.substr(0, 7) == "Server: ")
+ {
+ m_server.assign(line.begin() + 7, line.end());
+ }
+ else if (line.size() < 3)
+ {
+ m_state = read_body;
+#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
+ if (has_requester()) requester().debug_log("end of http header");
+#endif
+ if (m_code >= 300 && m_code < 400)
+ {
+ if (m_location.empty())
+ {
+ std::string error_str = "got redirection response (";
+ error_str += boost::lexical_cast<std::string>(m_code);
+ error_str += ") without 'Location' header";
+ fail(-1, error_str.c_str());
+ return;
+ }
+
+#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
+ if (has_requester()) requester().debug_log("Redirecting to \"" + m_location + "\"");
+#endif
+ tracker_request req = tracker_req();
+ std::string::size_type i = m_location.find('?');
+ if (i == std::string::npos)
+ req.url = m_location;
+ else
+ req.url.assign(m_location.begin(), m_location.begin() + i);
+
+ m_man.queue_request(m_socket->io_service(), req
+ , m_password, m_requester);
+ close();
+ return;
+ }
+ }
+
+ ++newline;
+ assert(m_recv_pos <= (int)m_buffer.size());
+ m_recv_pos -= (int)std::distance(m_buffer.begin(), newline);
+ m_buffer.erase(m_buffer.begin(), newline);
+ assert(m_recv_pos <= (int)m_buffer.size());
+ end = m_buffer.begin() + m_recv_pos;
+ newline = std::find(m_buffer.begin(), end, '\n');
+ }
+
+ }
+
+ if (m_state == read_body)
+ {
+ if (m_recv_pos == m_content_length)
+ {
+ on_response();
+ close();
+ return;
+ }
+ }
+ else if (m_recv_pos > m_content_length && m_content_length > 0)
+ {
+ fail(-1, "invalid tracker response (body > content_length)");
+ return;
+ }
+
+ assert(m_buffer.size() - m_recv_pos > 0);
+ m_socket->async_read_some(asio::buffer(&m_buffer[m_recv_pos]
+ , m_buffer.size() - m_recv_pos), bind(&http_tracker_connection::receive
+ , self(), _1, _2));
+ }
+ catch (std::exception& e)
+ {
+ assert(false);
+ fail(-1, e.what());
+ };
+
+ void http_tracker_connection::on_response()
+ {
+ // GZIP
+ if (m_content_encoding == gzip)
+ {
+ boost::shared_ptr<request_callback> r = m_requester.lock();
+
+ if (!r)
+ {
+ close();
+ return;
+ }
+ if (inflate_gzip(m_buffer, tracker_request(), r.get(),
+ m_settings.tracker_maximum_response_length))
+ {
+ close();
+ return;
+ }
+ }
+
+ // handle tracker response
+ try
+ {
+ entry e = bdecode(m_buffer.begin(), m_buffer.end());
+ parse(e);
+ }
+ catch (std::exception& e)
+ {
+ std::string error_str(e.what());
+ error_str += ": ";
+ error_str.append(m_buffer.begin(), m_buffer.end());
+ fail(m_code, error_str.c_str());
+ }
+ #ifndef NDEBUG
+ catch (...)
+ {
+ assert(false);
+ }
+ #endif
+ }
+
+ peer_entry http_tracker_connection::extract_peer_info(const entry& info)
+ {
+ peer_entry ret;
+
+ // extract peer id (if any)
+ entry const* i = info.find_key("peer id");
+ if (i != 0)
+ {
+ if (i->string().length() != 20)
+ throw std::runtime_error("invalid response from tracker");
+ std::copy(i->string().begin(), i->string().end(), ret.pid.begin());
+ }
+ else
+ {
+ // if there's no peer_id, just initialize it to a bunch of zeroes
+ std::fill_n(ret.pid.begin(), 20, 0);
+ }
+
+ // extract ip
+ i = info.find_key("ip");
+ if (i == 0) throw std::runtime_error("invalid response from tracker");
+ ret.ip = i->string();
+
+ // extract port
+ i = info.find_key("port");
+ if (i == 0) throw std::runtime_error("invalid response from tracker");
+ ret.port = (unsigned short)i->integer();
+
+ return ret;
+ }
+
+ void http_tracker_connection::parse(entry const& e)
+ {
+ if (!has_requester()) return;
+
+ try
+ {
+ // parse the response
+ try
+ {
+ entry const& failure = e["failure reason"];
+
+ fail(m_code, failure.string().c_str());
+ return;
+ }
+ catch (type_error const&) {}
+
+ try
+ {
+ entry const& warning = e["warning message"];
+ if (has_requester())
+ requester().tracker_warning(warning.string());
+ }
+ catch(type_error const&) {}
+
+ std::vector<peer_entry> peer_list;
+
+ if (tracker_req().kind == tracker_request::scrape_request)
+ {
+ std::string ih;
+ std::copy(tracker_req().info_hash.begin(), tracker_req().info_hash.end()
+ , std::back_inserter(ih));
+ entry scrape_data = e["files"][ih];
+ int complete = scrape_data["complete"].integer();
+ int incomplete = scrape_data["incomplete"].integer();
+ requester().tracker_response(tracker_request(), peer_list, 0, complete
+ , incomplete);
+ return;
+ }
+
+ int interval = (int)e["interval"].integer();
+
+ if (e["peers"].type() == entry::string_t)
+ {
+ std::string const& peers = e["peers"].string();
+ for (std::string::const_iterator i = peers.begin();
+ i != peers.end();)
+ {
+ if (std::distance(i, peers.end()) < 6) break;
+
+ peer_entry p;
+ p.pid.clear();
+ std::stringstream ip_str;
+ ip_str << (int)detail::read_uint8(i) << ".";
+ ip_str << (int)detail::read_uint8(i) << ".";
+ ip_str << (int)detail::read_uint8(i) << ".";
+ ip_str << (int)detail::read_uint8(i);
+ p.ip = ip_str.str();
+ p.port = detail::read_uint16(i);
+ peer_list.push_back(p);
+ }
+ }
+ else
+ {
+ entry::list_type const& l = e["peers"].list();
+ for(entry::list_type::const_iterator i = l.begin(); i != l.end(); ++i)
+ {
+ peer_entry p = extract_peer_info(*i);
+ peer_list.push_back(p);
+ }
+ }
+
+ // look for optional scrape info
+ int complete = -1;
+ int incomplete = -1;
+
+ try { complete = e["complete"].integer(); }
+ catch(type_error&) {}
+
+ try { incomplete = e["incomplete"].integer(); }
+ catch(type_error&) {}
+
+ requester().tracker_response(tracker_request(), peer_list, interval, complete
+ , incomplete);
+ }
+ catch(type_error& e)
+ {
+ requester().tracker_request_error(tracker_request(), m_code, e.what());
+ }
+ catch(std::runtime_error& e)
+ {
+ requester().tracker_request_error(tracker_request(), m_code, e.what());
+ }
+ }
+
+}
+
diff --git a/library/identify_client.cpp b/library/identify_client.cpp
new file mode 100755
index 000000000..96039f2b0
--- /dev/null
+++ b/library/identify_client.cpp
@@ -0,0 +1,326 @@
+/*
+
+Copyright (c) 2003, 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.
+
+*/
+
+#include <cctype>
+#include <algorithm>
+
+#ifdef _MSC_VER
+#pragma warning(push, 1)
+#endif
+
+#include <boost/optional.hpp>
+
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+#include "libtorrent/identify_client.hpp"
+#include "libtorrent/fingerprint.hpp"
+
+namespace
+{
+
+ using namespace libtorrent;
+
+ int decode_digit(char c)
+ {
+ if (std::isdigit(c)) return c - '0';
+ return unsigned(c) - 'A' + 10;
+ }
+
+ // takes a peer id and returns a valid boost::optional
+ // object if the peer id matched the azureus style encoding
+ // the returned fingerprint contains information about the
+ // client's id
+ boost::optional<fingerprint> parse_az_style(const peer_id& id)
+ {
+ fingerprint ret("..", 0, 0, 0, 0);
+
+ if (id[0] != '-' || !std::isprint(id[1]) || (id[2] < '0')
+ || (id[3] < '0') || (id[4] < '0')
+ || (id[5] < '0') || (id[6] < '0')
+ || id[7] != '-')
+ return boost::optional<fingerprint>();
+
+ ret.name[0] = id[1];
+ ret.name[1] = id[2];
+ ret.major_version = decode_digit(id[3]);
+ ret.minor_version = decode_digit(id[4]);
+ ret.revision_version = decode_digit(id[5]);
+ ret.tag_version = decode_digit(id[6]);
+
+ return boost::optional<fingerprint>(ret);
+ }
+
+ // checks if a peer id can possibly contain a shadow-style
+ // identification
+ boost::optional<fingerprint> parse_shadow_style(const peer_id& id)
+ {
+ fingerprint ret("..", 0, 0, 0, 0);
+
+ if (!std::isalnum(id[0]))
+ return boost::optional<fingerprint>();
+
+ if (std::equal(id.begin()+4, id.begin()+6, "--"))
+ {
+ if ((id[1] < '0') || (id[2] < '0')
+ || (id[3] < '0'))
+ return boost::optional<fingerprint>();
+ ret.major_version = decode_digit(id[1]);
+ ret.minor_version = decode_digit(id[2]);
+ ret.revision_version = decode_digit(id[3]);
+ }
+ else
+ {
+ if (id[8] != 0 || id[1] > 127 || id[2] > 127 || id[3] > 127)
+ return boost::optional<fingerprint>();
+ ret.major_version = id[1];
+ ret.minor_version = id[2];
+ ret.revision_version = id[3];
+ }
+
+ ret.name[0] = id[0];
+ ret.name[1] = 0;
+
+ ret.tag_version = 0;
+ return boost::optional<fingerprint>(ret);
+ }
+
+ // checks if a peer id can possibly contain a mainline-style
+ // identification
+ boost::optional<fingerprint> parse_mainline_style(const peer_id& id)
+ {
+ char ids[21];
+ std::copy(id.begin(), id.end(), ids);
+ ids[20] = 0;
+ fingerprint ret("..", 0, 0, 0, 0);
+ ret.name[1] = 0;
+ ret.tag_version = 0;
+ if (sscanf(ids, "%c%d-%d-%d--", &ret.name[0], &ret.major_version, &ret.minor_version
+ , &ret.revision_version) != 4
+ || !std::isprint(ret.name[0]))
+ return boost::optional<fingerprint>();
+
+ return boost::optional<fingerprint>(ret);
+ }
+
+ typedef std::pair<char const*, char const*> map_entry;
+
+ // only support BitTorrentSpecification
+ // must be ordered alphabetically
+ map_entry name_map[] =
+ {
+ map_entry("A", "ABC")
+ , map_entry("AR", "Arctic Torrent")
+ , map_entry("AX", "BitPump")
+ , map_entry("AZ", "Azureus")
+ , map_entry("BB", "BitBuddy")
+ , map_entry("BC", "BitComet")
+ , map_entry("BS", "BTSlave")
+ , map_entry("BX", "BittorrentX")
+ , map_entry("CD", "Enhanced CTorrent")
+ , map_entry("CT", "CTorrent")
+ , map_entry("DE", "Deluge")
+ , map_entry("ES", "electric sheep")
+ , map_entry("KT", "KTorrent")
+ , map_entry("LP", "lphant")
+ , map_entry("LT", "libtorrent")
+ , map_entry("M", "Mainline")
+ , map_entry("MP", "MooPolice")
+ , map_entry("MT", "Moonlight Torrent")
+ , map_entry("O", "Osprey Permaseed")
+ , map_entry("R", "Tribler")
+ , map_entry("S", "Shadow")
+ , map_entry("SB", "Swiftbit")
+ , map_entry("SN", "ShareNet")
+ , map_entry("SS", "SwarmScope")
+ , map_entry("SZ", "Shareaza")
+ , map_entry("T", "BitTornado")
+ , map_entry("TN", "Torrent.NET")
+ , map_entry("TR", "Transmission")
+ , map_entry("TS", "TorrentStorm")
+ , map_entry("U", "UPnP")
+ , map_entry("UL", "uLeecher")
+ , map_entry("UT", "MicroTorrent")
+ , map_entry("XT", "XanTorrent")
+ , map_entry("ZT", "ZipTorrent")
+ , map_entry("lt", "libTorrent (libtorrent.rakshasa.no/)")
+ , map_entry("pX", "pHoeniX")
+ , map_entry("qB", "qBittorrent")
+ };
+
+ bool compare_first_string(map_entry const& lhs, map_entry const& rhs)
+ {
+ return lhs.first[0] < rhs.first[0]
+ || ((lhs.first[0] == rhs.first[0]) && (lhs.first[1] < rhs.first[1]));
+ }
+
+ std::string lookup(fingerprint const& f)
+ {
+ std::stringstream identity;
+
+ const int size = sizeof(name_map)/sizeof(name_map[0]);
+ map_entry* i =
+ std::lower_bound(name_map, name_map + size
+ , map_entry(f.name, ""), &compare_first_string);
+
+#ifndef NDEBUG
+ for (int i = 1; i < size; ++i)
+ {
+ assert(compare_first_string(name_map[i-1]
+ , name_map[i]));
+ }
+#endif
+
+ if (i < name_map + size && std::equal(f.name, f.name + 2, i->first))
+ identity << i->second;
+ else
+ {
+ identity << f.name[0];
+ if (f.name[1] != 0) identity << f.name[1];
+ }
+
+ identity << " " << (int)f.major_version
+ << "." << (int)f.minor_version
+ << "." << (int)f.revision_version;
+
+ if (f.name[1] != 0)
+ identity << "." << (int)f.tag_version;
+
+ return identity.str();
+ }
+
+ bool find_string(unsigned char const* id, char const* search)
+ {
+ return std::equal(search, search + std::strlen(search), id);
+ }
+}
+
+namespace libtorrent
+{
+
+ boost::optional<fingerprint> client_fingerprint(peer_id const& p)
+ {
+ // look for azureus style id
+ boost::optional<fingerprint> f;
+ f = parse_az_style(p);
+ if (f) return f;
+
+ // look for shadow style id
+ f = parse_shadow_style(p);
+ if (f) return f;
+
+ // look for mainline style id
+ f = parse_mainline_style(p);
+ if (f) return f;
+ return f;
+ }
+
+ std::string identify_client(peer_id const& p)
+ {
+ peer_id::const_iterator PID = p.begin();
+ boost::optional<fingerprint> f;
+
+ if (p.is_all_zeros()) return "Unknown";
+
+ // ----------------------
+ // non standard encodings
+ // ----------------------
+
+ if (find_string(PID, "Deadman Walking-")) return "Deadman";
+ if (find_string(PID + 5, "Azureus")) return "Azureus 2.0.3.2";
+ if (find_string(PID, "DansClient")) return "XanTorrent";
+ if (find_string(PID + 4, "btfans")) return "SimpleBT";
+ if (find_string(PID, "PRC.P---")) return "Bittorrent Plus! II";
+ if (find_string(PID, "P87.P---")) return "Bittorrent Plus!";
+ if (find_string(PID, "S587Plus")) return "Bittorrent Plus!";
+ if (find_string(PID, "martini")) return "Martini Man";
+ if (find_string(PID, "Plus---")) return "Bittorrent Plus";
+ if (find_string(PID, "turbobt")) return "TurboBT";
+ if (find_string(PID, "a00---0")) return "Swarmy";
+ if (find_string(PID, "a02---0")) return "Swarmy";
+ if (find_string(PID, "T00---0")) return "Teeweety";
+ if (find_string(PID, "BTDWV-")) return "Deadman Walking";
+ if (find_string(PID + 2, "BS")) return "BitSpirit";
+ if (find_string(PID, "btuga")) return "BTugaXP";
+ if (find_string(PID, "oernu")) return "BTugaXP";
+ if (find_string(PID, "Mbrst")) return "Burst!";
+ if (find_string(PID, "Plus")) return "Plus!";
+ if (find_string(PID, "-Qt-")) return "Qt";
+ if (find_string(PID, "exbc")) return "BitComet";
+ if (find_string(PID, "-G3")) return "G3 Torrent";
+ if (find_string(PID, "XBT")) return "XBT";
+ if (find_string(PID, "OP")) return "Opera";
+
+ if (find_string(PID, "-BOW") && PID[7] == '-')
+ return "Bits on Wheels " + std::string(PID + 4, PID + 7);
+
+
+ if (find_string(PID, "eX"))
+ {
+ std::string user(PID + 2, PID + 14);
+ return std::string("eXeem ('") + user.c_str() + "')";
+ }
+
+ if (std::equal(PID, PID + 13, "\0\0\0\0\0\0\0\0\0\0\0\0\x97"))
+ return "Experimental 3.2.1b2";
+
+ if (std::equal(PID, PID + 13, "\0\0\0\0\0\0\0\0\0\0\0\0\0"))
+ return "Experimental 3.1";
+
+
+ // look for azureus style id
+ f = parse_az_style(p);
+ if (f) return lookup(*f);
+
+ // look for shadow style id
+ f = parse_shadow_style(p);
+ if (f) return lookup(*f);
+
+ // look for mainline style id
+ f = parse_mainline_style(p);
+ if (f) return lookup(*f);
+
+
+ if (std::equal(PID, PID + 12, "\0\0\0\0\0\0\0\0\0\0\0\0"))
+ return "Generic";
+
+ std::string unknown("Unknown [");
+ for (peer_id::const_iterator i = p.begin(); i != p.end(); ++i)
+ {
+ unknown += std::isprint(*i)?*i:'.';
+ }
+ unknown += "]";
+ return unknown;
+ }
+
+}
diff --git a/library/include/libtorrent/alert.hpp b/library/include/libtorrent/alert.hpp
new file mode 100755
index 000000000..a820ef225
--- /dev/null
+++ b/library/include/libtorrent/alert.hpp
@@ -0,0 +1,174 @@
+/*
+
+Copyright (c) 2003, Arvid Norberg, Daniel Wallin
+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_ALERT_HPP_INCLUDED
+#define TORRENT_ALERT_HPP_INCLUDED
+
+#include <memory>
+#include <queue>
+#include <string>
+#include <cassert>
+#include <typeinfo>
+
+#ifdef _MSC_VER
+#pragma warning(push, 1)
+#endif
+
+#include <boost/thread/mutex.hpp>
+
+#include <boost/preprocessor/repetition/enum_params_with_a_default.hpp>
+#include <boost/preprocessor/repetition/enum.hpp>
+#include <boost/preprocessor/repetition/enum_params.hpp>
+#include <boost/preprocessor/repetition/enum_shifted_params.hpp>
+#include <boost/date_time/posix_time/posix_time.hpp>
+
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+#include "libtorrent/config.hpp"
+
+#define TORRENT_MAX_ALERT_TYPES 10
+
+namespace libtorrent {
+
+ class TORRENT_EXPORT alert
+ {
+ public:
+ enum severity_t { debug, info, warning, critical, fatal, none };
+
+ alert(severity_t severity, const std::string& msg);
+ virtual ~alert();
+
+ // a timestamp is automatically created in the constructor
+ boost::posix_time::ptime timestamp() const;
+
+ const std::string& msg() const;
+
+ severity_t severity() const;
+
+ virtual std::auto_ptr<alert> clone() const = 0;
+
+ private:
+ std::string m_msg;
+ severity_t m_severity;
+ boost::posix_time::ptime m_timestamp;
+ };
+
+ class TORRENT_EXPORT alert_manager
+ {
+ public:
+ alert_manager();
+ ~alert_manager();
+
+ void post_alert(const alert& alert_);
+ bool pending() const;
+ std::auto_ptr<alert> get();
+
+ void set_severity(alert::severity_t severity);
+ bool should_post(alert::severity_t severity) const;
+
+ private:
+ std::queue<alert*> m_alerts;
+ alert::severity_t m_severity;
+ mutable boost::mutex m_mutex;
+ };
+
+ struct TORRENT_EXPORT unhandled_alert : std::exception
+ {
+ unhandled_alert() {}
+ };
+
+ namespace detail {
+
+ struct void_;
+
+ template<
+ class Handler
+ , BOOST_PP_ENUM_PARAMS(TORRENT_MAX_ALERT_TYPES, class T)
+ >
+ void handle_alert_dispatch(
+ const std::auto_ptr<alert>& alert_
+ , const Handler& handler
+ , const std::type_info& typeid_
+ , BOOST_PP_ENUM_BINARY_PARAMS(TORRENT_MAX_ALERT_TYPES, T, *p))
+ {
+ if (typeid_ == typeid(T0))
+ handler(*static_cast<T0*>(alert_.get()));
+ else
+ handle_alert_dispatch(
+ alert_
+ , handler
+ , typeid_
+ , BOOST_PP_ENUM_SHIFTED_PARAMS(TORRENT_MAX_ALERT_TYPES, p), (void_*)0
+ );
+ }
+
+ template<class Handler>
+ void handle_alert_dispatch(
+ const std::auto_ptr<alert>& alert_
+ , const Handler& handler
+ , const std::type_info& typeid_
+ , BOOST_PP_ENUM_PARAMS(TORRENT_MAX_ALERT_TYPES, void_* BOOST_PP_INTERCEPT))
+ {
+ throw unhandled_alert();
+ }
+
+ } // namespace detail
+
+ template<
+ BOOST_PP_ENUM_PARAMS_WITH_A_DEFAULT(TORRENT_MAX_ALERT_TYPES, class T, detail::void_)
+ >
+ struct TORRENT_EXPORT handle_alert
+ {
+ template<class Handler>
+ handle_alert(
+ const std::auto_ptr<alert>& alert_
+ , const Handler& handler)
+ {
+ #define ALERT_POINTER_TYPE(z, n, text) (BOOST_PP_CAT(T, n)*)0
+
+ detail::handle_alert_dispatch(
+ alert_
+ , handler
+ , typeid(*alert_)
+ , BOOST_PP_ENUM(TORRENT_MAX_ALERT_TYPES, ALERT_POINTER_TYPE, _)
+ );
+
+ #undef ALERT_POINTER_TYPE
+ }
+ };
+
+} // namespace libtorrent
+
+#endif // TORRENT_ALERT_HPP_INCLUDED
+
diff --git a/library/include/libtorrent/alert_types.hpp b/library/include/libtorrent/alert_types.hpp
new file mode 100755
index 000000000..7a5cb7fd3
--- /dev/null
+++ b/library/include/libtorrent/alert_types.hpp
@@ -0,0 +1,300 @@
+/*
+
+Copyright (c) 2003, 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_ALERT_TYPES_HPP_INCLUDED
+#define TORRENT_ALERT_TYPES_HPP_INCLUDED
+
+#include "libtorrent/alert.hpp"
+#include "libtorrent/torrent_handle.hpp"
+#include "libtorrent/socket.hpp"
+#include "libtorrent/peer_connection.hpp"
+#include "libtorrent/config.hpp"
+
+namespace libtorrent
+{
+ struct TORRENT_EXPORT tracker_alert: alert
+ {
+ tracker_alert(torrent_handle const& h
+ , int times
+ , int status
+ , std::string const& msg)
+ : alert(alert::warning, msg)
+ , handle(h)
+ , times_in_row(times)
+ , status_code(status)
+ {}
+
+ virtual std::auto_ptr<alert> clone() const
+ { return std::auto_ptr<alert>(new tracker_alert(*this)); }
+
+ torrent_handle handle;
+ int times_in_row;
+ int status_code;
+ };
+
+ struct TORRENT_EXPORT tracker_warning_alert: alert
+ {
+ tracker_warning_alert(torrent_handle const& h
+ , std::string const& msg)
+ : alert(alert::warning, msg)
+ , handle(h)
+ {}
+
+ virtual std::auto_ptr<alert> clone() const
+ { return std::auto_ptr<alert>(new tracker_warning_alert(*this)); }
+
+ torrent_handle handle;
+ };
+
+
+
+ struct TORRENT_EXPORT tracker_reply_alert: alert
+ {
+ tracker_reply_alert(torrent_handle const& h
+ , std::string const& msg)
+ : alert(alert::info, msg)
+ , handle(h)
+ {}
+
+ virtual std::auto_ptr<alert> clone() const
+ { return std::auto_ptr<alert>(new tracker_reply_alert(*this)); }
+
+ torrent_handle handle;
+ };
+
+ struct TORRENT_EXPORT tracker_announce_alert: alert
+ {
+ tracker_announce_alert(torrent_handle const& h, std::string const& msg)
+ : alert(alert::info, msg)
+ , handle(h)
+ {}
+
+ virtual std::auto_ptr<alert> clone() const
+ { return std::auto_ptr<alert>(new tracker_announce_alert(*this)); }
+
+ torrent_handle handle;
+ };
+
+ struct TORRENT_EXPORT hash_failed_alert: alert
+ {
+ hash_failed_alert(
+ torrent_handle const& h
+ , int index
+ , std::string const& msg)
+ : alert(alert::info, msg)
+ , handle(h)
+ , piece_index(index)
+ { assert(index >= 0);}
+
+ virtual std::auto_ptr<alert> clone() const
+ { return std::auto_ptr<alert>(new hash_failed_alert(*this)); }
+
+ torrent_handle handle;
+ int piece_index;
+ };
+
+ struct TORRENT_EXPORT peer_ban_alert: alert
+ {
+ peer_ban_alert(tcp::endpoint const& pip, torrent_handle h, std::string const& msg)
+ : alert(alert::info, msg)
+ , ip(pip)
+ , handle(h)
+ {}
+
+ virtual std::auto_ptr<alert> clone() const
+ { return std::auto_ptr<alert>(new peer_ban_alert(*this)); }
+
+ tcp::endpoint ip;
+ torrent_handle handle;
+ };
+
+ struct TORRENT_EXPORT peer_error_alert: alert
+ {
+ peer_error_alert(tcp::endpoint const& pip, peer_id const& pid_, std::string const& msg)
+ : alert(alert::debug, msg)
+ , ip(pip)
+ , pid(pid_)
+ {}
+
+ virtual std::auto_ptr<alert> clone() const
+ { return std::auto_ptr<alert>(new peer_error_alert(*this)); }
+
+ tcp::endpoint ip;
+ peer_id pid;
+ };
+
+ struct TORRENT_EXPORT chat_message_alert: alert
+ {
+ chat_message_alert(
+ const torrent_handle& h
+ , const tcp::endpoint& sender
+ , const std::string& msg)
+ : alert(alert::critical, msg)
+ , handle(h)
+ , ip(sender)
+ {}
+
+ virtual std::auto_ptr<alert> clone() const
+ { return std::auto_ptr<alert>(new chat_message_alert(*this)); }
+
+ torrent_handle handle;
+ tcp::endpoint ip;
+ };
+
+ struct TORRENT_EXPORT invalid_request_alert: alert
+ {
+ invalid_request_alert(
+ peer_request const& r
+ , torrent_handle const& h
+ , tcp::endpoint const& sender
+ , peer_id const& pid_
+ , std::string const& msg)
+ : alert(alert::debug, msg)
+ , handle(h)
+ , ip(sender)
+ , request(r)
+ , pid(pid_)
+ {}
+
+ virtual std::auto_ptr<alert> clone() const
+ { return std::auto_ptr<alert>(new invalid_request_alert(*this)); }
+
+ torrent_handle handle;
+ tcp::endpoint ip;
+ peer_request request;
+ peer_id pid;
+ };
+
+ struct TORRENT_EXPORT torrent_finished_alert: alert
+ {
+ torrent_finished_alert(
+ const torrent_handle& h
+ , const std::string& msg)
+ : alert(alert::warning, msg)
+ , handle(h)
+ {}
+
+ virtual std::auto_ptr<alert> clone() const
+ { return std::auto_ptr<alert>(new torrent_finished_alert(*this)); }
+
+ torrent_handle handle;
+ };
+
+ struct TORRENT_EXPORT url_seed_alert: alert
+ {
+ url_seed_alert(
+ const std::string& url_
+ , const std::string& msg)
+ : alert(alert::warning, msg)
+ , url(url_)
+ {}
+
+ virtual std::auto_ptr<alert> clone() const
+ { return std::auto_ptr<alert>(new url_seed_alert(*this)); }
+
+ std::string url;
+ };
+
+ struct TORRENT_EXPORT file_error_alert: alert
+ {
+ file_error_alert(
+ const torrent_handle& h
+ , const std::string& msg)
+ : alert(alert::fatal, msg)
+ , handle(h)
+ {}
+
+ virtual std::auto_ptr<alert> clone() const
+ { return std::auto_ptr<alert>(new file_error_alert(*this)); }
+
+ torrent_handle handle;
+ };
+
+ struct TORRENT_EXPORT metadata_failed_alert: alert
+ {
+ metadata_failed_alert(
+ const torrent_handle& h
+ , const std::string& msg)
+ : alert(alert::info, msg)
+ , handle(h)
+ {}
+
+ virtual std::auto_ptr<alert> clone() const
+ { return std::auto_ptr<alert>(new metadata_failed_alert(*this)); }
+
+ torrent_handle handle;
+ };
+
+ struct TORRENT_EXPORT metadata_received_alert: alert
+ {
+ metadata_received_alert(
+ const torrent_handle& h
+ , const std::string& msg)
+ : alert(alert::info, msg)
+ , handle(h)
+ {}
+
+ virtual std::auto_ptr<alert> clone() const
+ { return std::auto_ptr<alert>(new metadata_received_alert(*this)); }
+
+ torrent_handle handle;
+ };
+
+ struct TORRENT_EXPORT listen_failed_alert: alert
+ {
+ listen_failed_alert(
+ const std::string& msg)
+ : alert(alert::fatal, msg)
+ {}
+
+ virtual std::auto_ptr<alert> clone() const
+ { return std::auto_ptr<alert>(new listen_failed_alert(*this)); }
+ };
+
+ struct TORRENT_EXPORT fastresume_rejected_alert: alert
+ {
+ fastresume_rejected_alert(torrent_handle const& h
+ , std::string const& msg)
+ : alert(alert::warning, msg)
+ , handle(h)
+ {}
+
+ virtual std::auto_ptr<alert> clone() const
+ { return std::auto_ptr<alert>(new fastresume_rejected_alert(*this)); }
+
+ torrent_handle handle;
+ };
+
+}
+
+
+#endif
diff --git a/library/include/libtorrent/allocate_resources.hpp b/library/include/libtorrent/allocate_resources.hpp
new file mode 100644
index 000000000..b859b6b47
--- /dev/null
+++ b/library/include/libtorrent/allocate_resources.hpp
@@ -0,0 +1,72 @@
+/*
+
+Copyright (c) 2003, Magnus Jonsson
+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_ALLOCATE_RESOURCES_HPP_INCLUDED
+#define TORRENT_ALLOCATE_RESOURCES_HPP_INCLUDED
+
+#include <map>
+#include <utility>
+
+#include <boost/shared_ptr.hpp>
+
+#include "libtorrent/resource_request.hpp"
+#include "libtorrent/peer_id.hpp"
+#include "libtorrent/socket.hpp"
+
+namespace libtorrent
+{
+ class peer_connection;
+ class torrent;
+
+ int saturated_add(int a, int b);
+
+ // Function to allocate a limited resource fairly among many consumers.
+ // It takes into account the current use, and the consumer's desired use.
+ // Should be invoked periodically to allow it adjust to the situation (make
+ // sure "used" is updated between calls!).
+ // If resources = std::numeric_limits<int>::max() it means there is an infinite
+ // supply of resources (so everyone can get what they want).
+
+ void allocate_resources(
+ int resources
+ , std::map<sha1_hash, boost::shared_ptr<torrent> >& torrents
+ , resource_request torrent::* res);
+
+ void allocate_resources(
+ int resources
+ , std::map<tcp::endpoint, peer_connection*>& connections
+ , resource_request peer_connection::* res);
+
+}
+
+
+#endif
diff --git a/library/include/libtorrent/asio.hpp b/library/include/libtorrent/asio.hpp
new file mode 100644
index 000000000..3bd10de70
--- /dev/null
+++ b/library/include/libtorrent/asio.hpp
@@ -0,0 +1,71 @@
+//
+// asio.hpp
+// ~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_HPP
+#define ASIO_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/basic_datagram_socket.hpp"
+#include "asio/basic_deadline_timer.hpp"
+#include "asio/basic_io_object.hpp"
+#include "asio/basic_resolver.hpp"
+#include "asio/basic_socket_acceptor.hpp"
+#include "asio/basic_socket_iostream.hpp"
+#include "asio/basic_socket_streambuf.hpp"
+#include "asio/basic_stream_socket.hpp"
+#include "asio/basic_streambuf.hpp"
+#include "asio/buffer.hpp"
+#include "asio/buffered_read_stream_fwd.hpp"
+#include "asio/buffered_read_stream.hpp"
+#include "asio/buffered_stream_fwd.hpp"
+#include "asio/buffered_stream.hpp"
+#include "asio/buffered_write_stream_fwd.hpp"
+#include "asio/buffered_write_stream.hpp"
+#include "asio/completion_condition.hpp"
+#include "asio/datagram_socket_service.hpp"
+#include "asio/deadline_timer_service.hpp"
+#include "asio/deadline_timer.hpp"
+#include "asio/error_handler.hpp"
+#include "asio/error.hpp"
+#include "asio/handler_alloc_hook.hpp"
+#include "asio/handler_invoke_hook.hpp"
+#include "asio/io_service.hpp"
+#include "asio/ip/address.hpp"
+#include "asio/ip/address_v4.hpp"
+#include "asio/ip/address_v6.hpp"
+#include "asio/ip/basic_endpoint.hpp"
+#include "asio/ip/basic_resolver_entry.hpp"
+#include "asio/ip/basic_resolver_iterator.hpp"
+#include "asio/ip/basic_resolver_query.hpp"
+#include "asio/ip/host_name.hpp"
+#include "asio/ip/multicast.hpp"
+#include "asio/ip/resolver_query_base.hpp"
+#include "asio/ip/tcp.hpp"
+#include "asio/ip/udp.hpp"
+#include "asio/is_read_buffered.hpp"
+#include "asio/is_write_buffered.hpp"
+#include "asio/placeholders.hpp"
+#include "asio/read.hpp"
+#include "asio/read_until.hpp"
+#include "asio/resolver_service.hpp"
+#include "asio/socket_acceptor_service.hpp"
+#include "asio/socket_base.hpp"
+#include "asio/strand.hpp"
+#include "asio/stream_socket_service.hpp"
+#include "asio/streambuf.hpp"
+#include "asio/system_exception.hpp"
+#include "asio/thread.hpp"
+#include "asio/time_traits.hpp"
+#include "asio/write.hpp"
+
+#endif // ASIO_HPP
diff --git a/library/include/libtorrent/asio/basic_datagram_socket.hpp b/library/include/libtorrent/asio/basic_datagram_socket.hpp
new file mode 100644
index 000000000..f555ae506
--- /dev/null
+++ b/library/include/libtorrent/asio/basic_datagram_socket.hpp
@@ -0,0 +1,802 @@
+//
+// basic_datagram_socket.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_BASIC_DATAGRAM_SOCKET_HPP
+#define ASIO_BASIC_DATAGRAM_SOCKET_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/detail/push_options.hpp"
+#include <cstddef>
+#include <boost/config.hpp>
+#include "asio/detail/pop_options.hpp"
+
+#include "asio/basic_socket.hpp"
+#include "asio/datagram_socket_service.hpp"
+#include "asio/error_handler.hpp"
+
+namespace asio {
+
+/// Provides datagram-oriented socket functionality.
+/**
+ * The basic_datagram_socket class template provides asynchronous and blocking
+ * datagram-oriented socket functionality.
+ *
+ * @par Thread Safety:
+ * @e Distinct @e objects: Safe.@n
+ * @e Shared @e objects: Unsafe.
+ *
+ * @par Concepts:
+ * Async_Object, Error_Source.
+ */
+template <typename Protocol,
+ typename Service = datagram_socket_service<Protocol> >
+class basic_datagram_socket
+ : public basic_socket<Protocol, Service>
+{
+public:
+ /// The native representation of a socket.
+ typedef typename Service::native_type native_type;
+
+ /// The protocol type.
+ typedef Protocol protocol_type;
+
+ /// The endpoint type.
+ typedef typename Protocol::endpoint endpoint_type;
+
+ /// Construct a basic_datagram_socket without opening it.
+ /**
+ * This constructor creates a datagram socket without opening it. The open()
+ * function must be called before data can be sent or received on the socket.
+ *
+ * @param io_service The io_service object that the datagram socket will use
+ * to dispatch handlers for any asynchronous operations performed on the
+ * socket.
+ */
+ explicit basic_datagram_socket(asio::io_service& io_service)
+ : basic_socket<Protocol, Service>(io_service)
+ {
+ }
+
+ /// Construct and open a basic_datagram_socket.
+ /**
+ * This constructor creates and opens a datagram socket.
+ *
+ * @param io_service The io_service object that the datagram socket will use
+ * to dispatch handlers for any asynchronous operations performed on the
+ * socket.
+ *
+ * @param protocol An object specifying protocol parameters to be used.
+ *
+ * @throws asio::error Thrown on failure.
+ */
+ basic_datagram_socket(asio::io_service& io_service,
+ const protocol_type& protocol)
+ : basic_socket<Protocol, Service>(io_service, protocol)
+ {
+ }
+
+ /// Construct a basic_datagram_socket, opening it and binding it to the given
+ /// local endpoint.
+ /**
+ * This constructor creates a datagram socket and automatically opens it bound
+ * to the specified endpoint on the local machine. The protocol used is the
+ * protocol associated with the given endpoint.
+ *
+ * @param io_service The io_service object that the datagram socket will use
+ * to dispatch handlers for any asynchronous operations performed on the
+ * socket.
+ *
+ * @param endpoint An endpoint on the local machine to which the datagram
+ * socket will be bound.
+ *
+ * @throws asio::error Thrown on failure.
+ */
+ basic_datagram_socket(asio::io_service& io_service,
+ const endpoint_type& endpoint)
+ : basic_socket<Protocol, Service>(io_service, endpoint)
+ {
+ }
+
+ /// Construct a basic_datagram_socket on an existing native socket.
+ /**
+ * This constructor creates a datagram socket object to hold an existing
+ * native socket.
+ *
+ * @param io_service The io_service object that the datagram socket will use
+ * to dispatch handlers for any asynchronous operations performed on the
+ * socket.
+ *
+ * @param protocol An object specifying protocol parameters to be used.
+ *
+ * @param native_socket The new underlying socket implementation.
+ *
+ * @throws asio::error Thrown on failure.
+ */
+ basic_datagram_socket(asio::io_service& io_service,
+ const protocol_type& protocol, const native_type& native_socket)
+ : basic_socket<Protocol, Service>(io_service, protocol, native_socket)
+ {
+ }
+
+ /// Send some data on a connected socket.
+ /**
+ * This function is used to send data on the datagram socket. The function
+ * call will block until the data has been sent successfully or an error
+ * occurs.
+ *
+ * @param buffers One ore more data buffers to be sent on the socket.
+ *
+ * @returns The number of bytes sent.
+ *
+ * @throws asio::error Thrown on failure.
+ *
+ * @note The send operation can only be used with a connected socket. Use
+ * the send_to function to send data on an unconnected datagram socket.
+ *
+ * @par Example:
+ * To send a single data buffer use the @ref buffer function as follows:
+ * @code socket.send(asio::buffer(data, size)); @endcode
+ * See the @ref buffer documentation for information on sending multiple
+ * buffers in one go, and how to use it with arrays, boost::array or
+ * std::vector.
+ */
+ template <typename Const_Buffers>
+ std::size_t send(const Const_Buffers& buffers)
+ {
+ return this->service.send(this->implementation, buffers, 0, throw_error());
+ }
+
+ /// Send some data on a connected socket.
+ /**
+ * This function is used to send data on the datagram socket. The function
+ * call will block until the data has been sent successfully or an error
+ * occurs.
+ *
+ * @param buffers One ore more data buffers to be sent on the socket.
+ *
+ * @param flags Flags specifying how the send call is to be made.
+ *
+ * @returns The number of bytes sent.
+ *
+ * @throws asio::error Thrown on failure.
+ *
+ * @note The send operation can only be used with a connected socket. Use
+ * the send_to function to send data on an unconnected datagram socket.
+ */
+ template <typename Const_Buffers>
+ std::size_t send(const Const_Buffers& buffers,
+ socket_base::message_flags flags)
+ {
+ return this->service.send(this->implementation, buffers, flags,
+ throw_error());
+ }
+
+ /// Send some data on a connected socket.
+ /**
+ * This function is used to send data on the datagram socket. The function
+ * call will block until the data has been sent successfully or an error
+ * occurs.
+ *
+ * @param buffers One or more data buffers to be sent on the socket.
+ *
+ * @param flags Flags specifying how the send call is to be made.
+ *
+ * @param error_handler A handler to be called when the operation completes,
+ * to indicate whether or not an error has occurred. Copies will be made of
+ * the handler as required. The function signature of the handler must be:
+ * @code void error_handler(
+ * const asio::error& error // Result of operation.
+ * ); @endcode
+ *
+ * @returns The number of bytes sent.
+ *
+ * @note The send operation can only be used with a connected socket. Use
+ * the send_to function to send data on an unconnected datagram socket.
+ */
+ template <typename Const_Buffers, typename Error_Handler>
+ std::size_t send(const Const_Buffers& buffers,
+ socket_base::message_flags flags, Error_Handler error_handler)
+ {
+ return this->service.send(this->implementation, buffers, flags,
+ error_handler);
+ }
+
+ /// Start an asynchronous send on a connected socket.
+ /**
+ * This function is used to send data on the datagram socket. The function
+ * call will block until the data has been sent successfully or an error
+ * occurs.
+ *
+ * @param buffers One or more data buffers to be sent on the socket. Although
+ * the buffers object may be copied as necessary, ownership of the underlying
+ * memory blocks is retained by the caller, which must guarantee that they
+ * remain valid until the handler is called.
+ *
+ * @param handler The handler to be called when the send operation completes.
+ * Copies will be made of the handler as required. The function signature of
+ * the handler must be:
+ * @code void handler(
+ * const asio::error& error, // Result of operation.
+ * std::size_t bytes_transferred // Number of bytes sent.
+ * ); @endcode
+ * Regardless of whether the asynchronous operation completes immediately or
+ * not, the handler will not be invoked from within this function. Invocation
+ * of the handler will be performed in a manner equivalent to using
+ * asio::io_service::post().
+ *
+ * @note The async_send operation can only be used with a connected socket.
+ * Use the async_send_to function to send data on an unconnected datagram
+ * socket.
+ *
+ * @par Example:
+ * To send a single data buffer use the @ref buffer function as follows:
+ * @code
+ * socket.async_send(asio::buffer(data, size), handler);
+ * @endcode
+ * See the @ref buffer documentation for information on sending multiple
+ * buffers in one go, and how to use it with arrays, boost::array or
+ * std::vector.
+ */
+ template <typename Const_Buffers, typename Handler>
+ void async_send(const Const_Buffers& buffers, Handler handler)
+ {
+ this->service.async_send(this->implementation, buffers, 0, handler);
+ }
+
+ /// Start an asynchronous send on a connected socket.
+ /**
+ * This function is used to send data on the datagram socket. The function
+ * call will block until the data has been sent successfully or an error
+ * occurs.
+ *
+ * @param buffers One or more data buffers to be sent on the socket. Although
+ * the buffers object may be copied as necessary, ownership of the underlying
+ * memory blocks is retained by the caller, which must guarantee that they
+ * remain valid until the handler is called.
+ *
+ * @param flags Flags specifying how the send call is to be made.
+ *
+ * @param handler The handler to be called when the send operation completes.
+ * Copies will be made of the handler as required. The function signature of
+ * the handler must be:
+ * @code void handler(
+ * const asio::error& error, // Result of operation.
+ * std::size_t bytes_transferred // Number of bytes sent.
+ * ); @endcode
+ * Regardless of whether the asynchronous operation completes immediately or
+ * not, the handler will not be invoked from within this function. Invocation
+ * of the handler will be performed in a manner equivalent to using
+ * asio::io_service::post().
+ *
+ * @note The async_send operation can only be used with a connected socket.
+ * Use the async_send_to function to send data on an unconnected datagram
+ * socket.
+ */
+ template <typename Const_Buffers, typename Handler>
+ void async_send(const Const_Buffers& buffers,
+ socket_base::message_flags flags, Handler handler)
+ {
+ this->service.async_send(this->implementation, buffers, flags, handler);
+ }
+
+ /// Send a datagram to the specified endpoint.
+ /**
+ * This function is used to send a datagram to the specified remote endpoint.
+ * The function call will block until the data has been sent successfully or
+ * an error occurs.
+ *
+ * @param buffers One or more data buffers to be sent to the remote endpoint.
+ *
+ * @param destination The remote endpoint to which the data will be sent.
+ *
+ * @returns The number of bytes sent.
+ *
+ * @throws asio::error Thrown on failure.
+ *
+ * @par Example:
+ * To send a single data buffer use the @ref buffer function as follows:
+ * @code
+ * asio::ip::udp::endpoint destination(
+ * asio::ip::address::from_string("1.2.3.4"), 12345);
+ * socket.send_to(asio::buffer(data, size), destination);
+ * @endcode
+ * See the @ref buffer documentation for information on sending multiple
+ * buffers in one go, and how to use it with arrays, boost::array or
+ * std::vector.
+ */
+ template <typename Const_Buffers>
+ std::size_t send_to(const Const_Buffers& buffers,
+ const endpoint_type& destination)
+ {
+ return this->service.send_to(this->implementation, buffers, destination, 0,
+ throw_error());
+ }
+
+ /// Send a datagram to the specified endpoint.
+ /**
+ * This function is used to send a datagram to the specified remote endpoint.
+ * The function call will block until the data has been sent successfully or
+ * an error occurs.
+ *
+ * @param buffers One or more data buffers to be sent to the remote endpoint.
+ *
+ * @param destination The remote endpoint to which the data will be sent.
+ *
+ * @param flags Flags specifying how the send call is to be made.
+ *
+ * @returns The number of bytes sent.
+ *
+ * @throws asio::error Thrown on failure.
+ */
+ template <typename Const_Buffers>
+ std::size_t send_to(const Const_Buffers& buffers,
+ const endpoint_type& destination, socket_base::message_flags flags)
+ {
+ return this->service.send_to(this->implementation, buffers, destination,
+ flags, throw_error());
+ }
+
+ /// Send a datagram to the specified endpoint.
+ /**
+ * This function is used to send a datagram to the specified remote endpoint.
+ * The function call will block until the data has been sent successfully or
+ * an error occurs.
+ *
+ * @param buffers One or more data buffers to be sent to the remote endpoint.
+ *
+ * @param destination The remote endpoint to which the data will be sent.
+ *
+ * @param flags Flags specifying how the send call is to be made.
+ *
+ * @param error_handler A handler to be called when the operation completes,
+ * to indicate whether or not an error has occurred. Copies will be made of
+ * the handler as required. The function signature of the handler must be:
+ * @code void error_handler(
+ * const asio::error& error // Result of operation.
+ * ); @endcode
+ *
+ * @returns The number of bytes sent.
+ */
+ template <typename Const_Buffers, typename Error_Handler>
+ std::size_t send_to(const Const_Buffers& buffers,
+ const endpoint_type& destination, socket_base::message_flags flags,
+ Error_Handler error_handler)
+ {
+ return this->service.send_to(this->implementation, buffers, destination,
+ flags, error_handler);
+ }
+
+ /// Start an asynchronous send.
+ /**
+ * This function is used to asynchronously send a datagram to the specified
+ * remote endpoint. The function call always returns immediately.
+ *
+ * @param buffers One or more data buffers to be sent to the remote endpoint.
+ * Although the buffers object may be copied as necessary, ownership of the
+ * underlying memory blocks is retained by the caller, which must guarantee
+ * that they remain valid until the handler is called.
+ *
+ * @param destination The remote endpoint to which the data will be sent.
+ * Copies will be made of the endpoint as required.
+ *
+ * @param handler The handler to be called when the send operation completes.
+ * Copies will be made of the handler as required. The function signature of
+ * the handler must be:
+ * @code void handler(
+ * const asio::error& error, // Result of operation.
+ * std::size_t bytes_transferred // Number of bytes sent.
+ * ); @endcode
+ * Regardless of whether the asynchronous operation completes immediately or
+ * not, the handler will not be invoked from within this function. Invocation
+ * of the handler will be performed in a manner equivalent to using
+ * asio::io_service::post().
+ *
+ * @par Example:
+ * To send a single data buffer use the @ref buffer function as follows:
+ * @code
+ * asio::ip::udp::endpoint destination(
+ * asio::ip::address::from_string("1.2.3.4"), 12345);
+ * socket.async_send_to(
+ * asio::buffer(data, size), destination, handler);
+ * @endcode
+ * See the @ref buffer documentation for information on sending multiple
+ * buffers in one go, and how to use it with arrays, boost::array or
+ * std::vector.
+ */
+ template <typename Const_Buffers, typename Handler>
+ void async_send_to(const Const_Buffers& buffers,
+ const endpoint_type& destination, Handler handler)
+ {
+ this->service.async_send_to(this->implementation, buffers, destination, 0,
+ handler);
+ }
+
+ /// Start an asynchronous send.
+ /**
+ * This function is used to asynchronously send a datagram to the specified
+ * remote endpoint. The function call always returns immediately.
+ *
+ * @param buffers One or more data buffers to be sent to the remote endpoint.
+ * Although the buffers object may be copied as necessary, ownership of the
+ * underlying memory blocks is retained by the caller, which must guarantee
+ * that they remain valid until the handler is called.
+ *
+ * @param flags Flags specifying how the send call is to be made.
+ *
+ * @param destination The remote endpoint to which the data will be sent.
+ * Copies will be made of the endpoint as required.
+ *
+ * @param handler The handler to be called when the send operation completes.
+ * Copies will be made of the handler as required. The function signature of
+ * the handler must be:
+ * @code void handler(
+ * const asio::error& error, // Result of operation.
+ * std::size_t bytes_transferred // Number of bytes sent.
+ * ); @endcode
+ * Regardless of whether the asynchronous operation completes immediately or
+ * not, the handler will not be invoked from within this function. Invocation
+ * of the handler will be performed in a manner equivalent to using
+ * asio::io_service::post().
+ */
+ template <typename Const_Buffers, typename Handler>
+ void async_send_to(const Const_Buffers& buffers,
+ const endpoint_type& destination, socket_base::message_flags flags,
+ Handler handler)
+ {
+ this->service.async_send_to(this->implementation, buffers, destination,
+ flags, handler);
+ }
+
+ /// Receive some data on a connected socket.
+ /**
+ * This function is used to receive data on the datagram socket. The function
+ * call will block until data has been received successfully or an error
+ * occurs.
+ *
+ * @param buffers One or more buffers into which the data will be received.
+ *
+ * @returns The number of bytes received.
+ *
+ * @throws asio::error Thrown on failure.
+ *
+ * @note The receive operation can only be used with a connected socket. Use
+ * the receive_from function to receive data on an unconnected datagram
+ * socket.
+ *
+ * @par Example:
+ * To receive into a single data buffer use the @ref buffer function as
+ * follows:
+ * @code socket.receive(asio::buffer(data, size)); @endcode
+ * See the @ref buffer documentation for information on receiving into
+ * multiple buffers in one go, and how to use it with arrays, boost::array or
+ * std::vector.
+ */
+ template <typename Mutable_Buffers>
+ std::size_t receive(const Mutable_Buffers& buffers)
+ {
+ return this->service.receive(this->implementation, buffers, 0,
+ throw_error());
+ }
+
+ /// Receive some data on a connected socket.
+ /**
+ * This function is used to receive data on the datagram socket. The function
+ * call will block until data has been received successfully or an error
+ * occurs.
+ *
+ * @param buffers One or more buffers into which the data will be received.
+ *
+ * @param flags Flags specifying how the receive call is to be made.
+ *
+ * @returns The number of bytes received.
+ *
+ * @throws asio::error Thrown on failure.
+ *
+ * @note The receive operation can only be used with a connected socket. Use
+ * the receive_from function to receive data on an unconnected datagram
+ * socket.
+ */
+ template <typename Mutable_Buffers>
+ std::size_t receive(const Mutable_Buffers& buffers,
+ socket_base::message_flags flags)
+ {
+ return this->service.receive(this->implementation, buffers, flags,
+ throw_error());
+ }
+
+ /// Receive some data on a connected socket.
+ /**
+ * This function is used to receive data on the datagram socket. The function
+ * call will block until data has been received successfully or an error
+ * occurs.
+ *
+ * @param buffers One or more buffers into which the data will be received.
+ *
+ * @param flags Flags specifying how the receive call is to be made.
+ *
+ * @param error_handler A handler to be called when the operation completes,
+ * to indicate whether or not an error has occurred. Copies will be made of
+ * the handler as required. The function signature of the handler must be:
+ * @code void error_handler(
+ * const asio::error& error // Result of operation.
+ * ); @endcode
+ *
+ * @returns The number of bytes received.
+ *
+ * @note The receive operation can only be used with a connected socket. Use
+ * the receive_from function to receive data on an unconnected datagram
+ * socket.
+ */
+ template <typename Mutable_Buffers, typename Error_Handler>
+ std::size_t receive(const Mutable_Buffers& buffers,
+ socket_base::message_flags flags, Error_Handler error_handler)
+ {
+ return this->service.receive(this->implementation, buffers, flags,
+ error_handler);
+ }
+
+ /// Start an asynchronous receive on a connected socket.
+ /**
+ * This function is used to asynchronously receive data from the datagram
+ * socket. The function call always returns immediately.
+ *
+ * @param buffers One or more buffers into which the data will be received.
+ * Although the buffers object may be copied as necessary, ownership of the
+ * underlying memory blocks is retained by the caller, which must guarantee
+ * that they remain valid until the handler is called.
+ *
+ * @param handler The handler to be called when the receive operation
+ * completes. Copies will be made of the handler as required. The function
+ * signature of the handler must be:
+ * @code void handler(
+ * const asio::error& error, // Result of operation.
+ * std::size_t bytes_transferred // Number of bytes received.
+ * ); @endcode
+ * Regardless of whether the asynchronous operation completes immediately or
+ * not, the handler will not be invoked from within this function. Invocation
+ * of the handler will be performed in a manner equivalent to using
+ * asio::io_service::post().
+ *
+ * @note The async_receive operation can only be used with a connected socket.
+ * Use the async_receive_from function to receive data on an unconnected
+ * datagram socket.
+ *
+ * @par Example:
+ * To receive into a single data buffer use the @ref buffer function as
+ * follows:
+ * @code
+ * socket.async_receive(asio::buffer(data, size), handler);
+ * @endcode
+ * See the @ref buffer documentation for information on receiving into
+ * multiple buffers in one go, and how to use it with arrays, boost::array or
+ * std::vector.
+ */
+ template <typename Mutable_Buffers, typename Handler>
+ void async_receive(const Mutable_Buffers& buffers, Handler handler)
+ {
+ this->service.async_receive(this->implementation, buffers, 0, handler);
+ }
+
+ /// Start an asynchronous receive on a connected socket.
+ /**
+ * This function is used to asynchronously receive data from the datagram
+ * socket. The function call always returns immediately.
+ *
+ * @param buffers One or more buffers into which the data will be received.
+ * Although the buffers object may be copied as necessary, ownership of the
+ * underlying memory blocks is retained by the caller, which must guarantee
+ * that they remain valid until the handler is called.
+ *
+ * @param flags Flags specifying how the receive call is to be made.
+ *
+ * @param handler The handler to be called when the receive operation
+ * completes. Copies will be made of the handler as required. The function
+ * signature of the handler must be:
+ * @code void handler(
+ * const asio::error& error, // Result of operation.
+ * std::size_t bytes_transferred // Number of bytes received.
+ * ); @endcode
+ * Regardless of whether the asynchronous operation completes immediately or
+ * not, the handler will not be invoked from within this function. Invocation
+ * of the handler will be performed in a manner equivalent to using
+ * asio::io_service::post().
+ *
+ * @note The async_receive operation can only be used with a connected socket.
+ * Use the async_receive_from function to receive data on an unconnected
+ * datagram socket.
+ */
+ template <typename Mutable_Buffers, typename Handler>
+ void async_receive(const Mutable_Buffers& buffers,
+ socket_base::message_flags flags, Handler handler)
+ {
+ this->service.async_receive(this->implementation, buffers, flags, handler);
+ }
+
+ /// Receive a datagram with the endpoint of the sender.
+ /**
+ * This function is used to receive a datagram. The function call will block
+ * until data has been received successfully or an error occurs.
+ *
+ * @param buffers One or more buffers into which the data will be received.
+ *
+ * @param sender_endpoint An endpoint object that receives the endpoint of
+ * the remote sender of the datagram.
+ *
+ * @returns The number of bytes received.
+ *
+ * @throws asio::error Thrown on failure.
+ *
+ * @par Example:
+ * To receive into a single data buffer use the @ref buffer function as
+ * follows:
+ * @code
+ * asio::ip::udp::endpoint sender_endpoint;
+ * socket.receive_from(
+ * asio::buffer(data, size), sender_endpoint);
+ * @endcode
+ * See the @ref buffer documentation for information on receiving into
+ * multiple buffers in one go, and how to use it with arrays, boost::array or
+ * std::vector.
+ */
+ template <typename Mutable_Buffers>
+ std::size_t receive_from(const Mutable_Buffers& buffers,
+ endpoint_type& sender_endpoint)
+ {
+ return this->service.receive_from(this->implementation, buffers,
+ sender_endpoint, 0, throw_error());
+ }
+
+ /// Receive a datagram with the endpoint of the sender.
+ /**
+ * This function is used to receive a datagram. The function call will block
+ * until data has been received successfully or an error occurs.
+ *
+ * @param buffers One or more buffers into which the data will be received.
+ *
+ * @param sender_endpoint An endpoint object that receives the endpoint of
+ * the remote sender of the datagram.
+ *
+ * @param flags Flags specifying how the receive call is to be made.
+ *
+ * @returns The number of bytes received.
+ *
+ * @throws asio::error Thrown on failure.
+ */
+ template <typename Mutable_Buffers>
+ std::size_t receive_from(const Mutable_Buffers& buffers,
+ endpoint_type& sender_endpoint, socket_base::message_flags flags)
+ {
+ return this->service.receive_from(this->implementation, buffers,
+ sender_endpoint, flags, throw_error());
+ }
+
+ /// Receive a datagram with the endpoint of the sender.
+ /**
+ * This function is used to receive a datagram. The function call will block
+ * until data has been received successfully or an error occurs.
+ *
+ * @param buffers One or more buffers into which the data will be received.
+ *
+ * @param sender_endpoint An endpoint object that receives the endpoint of
+ * the remote sender of the datagram.
+ *
+ * @param flags Flags specifying how the receive call is to be made.
+ *
+ * @param error_handler A handler to be called when the operation completes,
+ * to indicate whether or not an error has occurred. Copies will be made of
+ * the handler as required. The function signature of the handler must be:
+ * @code void error_handler(
+ * const asio::error& error // Result of operation.
+ * ); @endcode
+ *
+ * @returns The number of bytes received.
+ */
+ template <typename Mutable_Buffers, typename Error_Handler>
+ std::size_t receive_from(const Mutable_Buffers& buffers,
+ endpoint_type& sender_endpoint, socket_base::message_flags flags,
+ Error_Handler error_handler)
+ {
+ return this->service.receive_from(this->implementation, buffers,
+ sender_endpoint, flags, error_handler);
+ }
+
+ /// Start an asynchronous receive.
+ /**
+ * This function is used to asynchronously receive a datagram. The function
+ * call always returns immediately.
+ *
+ * @param buffers One or more buffers into which the data will be received.
+ * Although the buffers object may be copied as necessary, ownership of the
+ * underlying memory blocks is retained by the caller, which must guarantee
+ * that they remain valid until the handler is called.
+ *
+ * @param sender_endpoint An endpoint object that receives the endpoint of
+ * the remote sender of the datagram. Ownership of the sender_endpoint object
+ * is retained by the caller, which must guarantee that it is valid until the
+ * handler is called.
+ *
+ * @param handler The handler to be called when the receive operation
+ * completes. Copies will be made of the handler as required. The function
+ * signature of the handler must be:
+ * @code void handler(
+ * const asio::error& error, // Result of operation.
+ * std::size_t bytes_transferred // Number of bytes received.
+ * ); @endcode
+ * Regardless of whether the asynchronous operation completes immediately or
+ * not, the handler will not be invoked from within this function. Invocation
+ * of the handler will be performed in a manner equivalent to using
+ * asio::io_service::post().
+ *
+ * @par Example:
+ * To receive into a single data buffer use the @ref buffer function as
+ * follows:
+ * @code socket.async_receive_from(
+ * asio::buffer(data, size), 0, sender_endpoint, handler); @endcode
+ * See the @ref buffer documentation for information on receiving into
+ * multiple buffers in one go, and how to use it with arrays, boost::array or
+ * std::vector.
+ */
+ template <typename Mutable_Buffers, typename Handler>
+ void async_receive_from(const Mutable_Buffers& buffers,
+ endpoint_type& sender_endpoint, Handler handler)
+ {
+ this->service.async_receive_from(this->implementation, buffers,
+ sender_endpoint, 0, handler);
+ }
+
+ /// Start an asynchronous receive.
+ /**
+ * This function is used to asynchronously receive a datagram. The function
+ * call always returns immediately.
+ *
+ * @param buffers One or more buffers into which the data will be received.
+ * Although the buffers object may be copied as necessary, ownership of the
+ * underlying memory blocks is retained by the caller, which must guarantee
+ * that they remain valid until the handler is called.
+ *
+ * @param sender_endpoint An endpoint object that receives the endpoint of
+ * the remote sender of the datagram. Ownership of the sender_endpoint object
+ * is retained by the caller, which must guarantee that it is valid until the
+ * handler is called.
+ *
+ * @param flags Flags specifying how the receive call is to be made.
+ *
+ * @param handler The handler to be called when the receive operation
+ * completes. Copies will be made of the handler as required. The function
+ * signature of the handler must be:
+ * @code void handler(
+ * const asio::error& error, // Result of operation.
+ * std::size_t bytes_transferred // Number of bytes received.
+ * ); @endcode
+ * Regardless of whether the asynchronous operation completes immediately or
+ * not, the handler will not be invoked from within this function. Invocation
+ * of the handler will be performed in a manner equivalent to using
+ * asio::io_service::post().
+ */
+ template <typename Mutable_Buffers, typename Handler>
+ void async_receive_from(const Mutable_Buffers& buffers,
+ endpoint_type& sender_endpoint, socket_base::message_flags flags,
+ Handler handler)
+ {
+ this->service.async_receive_from(this->implementation, buffers,
+ sender_endpoint, flags, handler);
+ }
+};
+
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_BASIC_DATAGRAM_SOCKET_HPP
diff --git a/library/include/libtorrent/asio/basic_deadline_timer.hpp b/library/include/libtorrent/asio/basic_deadline_timer.hpp
new file mode 100644
index 000000000..7a2765a20
--- /dev/null
+++ b/library/include/libtorrent/asio/basic_deadline_timer.hpp
@@ -0,0 +1,309 @@
+//
+// basic_deadline_timer.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_BASIC_DEADLINE_TIMER_HPP
+#define ASIO_BASIC_DEADLINE_TIMER_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/detail/push_options.hpp"
+#include <cstddef>
+#include <boost/config.hpp>
+#include "asio/detail/pop_options.hpp"
+
+#include "asio/basic_io_object.hpp"
+#include "asio/deadline_timer_service.hpp"
+#include "asio/error.hpp"
+
+namespace asio {
+
+/// Provides waitable timer functionality.
+/**
+ * The basic_deadline_timer class template provides the ability to perform a
+ * blocking or asynchronous wait for a timer to expire.
+ *
+ * Most applications will use the asio::deadline_timer typedef.
+ *
+ * @par Thread Safety:
+ * @e Distinct @e objects: Safe.@n
+ * @e Shared @e objects: Unsafe.
+ *
+ * @par Concepts:
+ * Async_Object, Error_Source.
+ *
+ * @sa @ref deadline_timer_reset
+ *
+ * @par Examples:
+ * Performing a blocking wait:
+ * @code
+ * // Construct a timer without setting an expiry time.
+ * asio::deadline_timer timer(io_service);
+ *
+ * // Set an expiry time relative to now.
+ * timer.expires_from_now(boost::posix_time::seconds(5));
+ *
+ * // Wait for the timer to expire.
+ * timer.wait();
+ * @endcode
+ *
+ * @par
+ * Performing an asynchronous wait:
+ * @code
+ * void handler(const asio::error& error)
+ * {
+ * if (!error)
+ * {
+ * // Timer expired.
+ * }
+ * }
+ *
+ * ...
+ *
+ * // Construct a timer with an absolute expiry time.
+ * asio::deadline_timer timer(io_service,
+ * boost::posix_time::time_from_string("2005-12-07 23:59:59.000"));
+ *
+ * // Start an asynchronous wait.
+ * timer.async_wait(handler);
+ * @endcode
+ */
+template <typename Time_Type,
+ typename Time_Traits = asio::time_traits<Time_Type>,
+ typename Service = deadline_timer_service<Time_Type, Time_Traits> >
+class basic_deadline_timer
+ : public basic_io_object<Service>
+{
+public:
+ /// The type used for reporting errors.
+ typedef asio::error error_type;
+
+ /// The time traits type.
+ typedef Time_Traits traits_type;
+
+ /// The time type.
+ typedef typename traits_type::time_type time_type;
+
+ /// The duration type.
+ typedef typename traits_type::duration_type duration_type;
+
+ /// Constructor.
+ /**
+ * This constructor creates a timer without setting an expiry time. The
+ * expires_at() or expires_from_now() functions must be called to set an
+ * expiry time before the timer can be waited on.
+ *
+ * @param io_service The io_service object that the timer will use to dispatch
+ * handlers for any asynchronous operations performed on the timer.
+ */
+ explicit basic_deadline_timer(asio::io_service& io_service)
+ : basic_io_object<Service>(io_service)
+ {
+ }
+
+ /// Constructor to set a particular expiry time as an absolute time.
+ /**
+ * This constructor creates a timer and sets the expiry time.
+ *
+ * @param io_service The io_service object that the timer will use to dispatch
+ * handlers for any asynchronous operations performed on the timer.
+ *
+ * @param expiry_time The expiry time to be used for the timer, expressed
+ * as an absolute time.
+ */
+ basic_deadline_timer(asio::io_service& io_service,
+ const time_type& expiry_time)
+ : basic_io_object<Service>(io_service)
+ {
+ this->service.expires_at(this->implementation, expiry_time);
+ }
+
+ /// Constructor to set a particular expiry time relative to now.
+ /**
+ * This constructor creates a timer and sets the expiry time.
+ *
+ * @param io_service The io_service object that the timer will use to dispatch
+ * handlers for any asynchronous operations performed on the timer.
+ *
+ * @param expiry_time The expiry time to be used for the timer, relative to
+ * now.
+ */
+ basic_deadline_timer(asio::io_service& io_service,
+ const duration_type& expiry_time)
+ : basic_io_object<Service>(io_service)
+ {
+ this->service.expires_from_now(this->implementation, expiry_time);
+ }
+
+ /// Cancel any asynchronous operations that are waiting on the timer.
+ /**
+ * This function forces the completion of any pending asynchronous wait
+ * operations against the timer. The handler for each cancelled operation will
+ * be invoked with the asio::error::operation_aborted error code.
+ *
+ * Cancelling the timer does not change the expiry time.
+ *
+ * @return The number of asynchronous operations that were cancelled.
+ */
+ std::size_t cancel()
+ {
+ return this->service.cancel(this->implementation);
+ }
+
+ /// Get the timer's expiry time as an absolute time.
+ /**
+ * This function may be used to obtain the timer's current expiry time.
+ * Whether the timer has expired or not does not affect this value.
+ */
+ time_type expires_at() const
+ {
+ return this->service.expires_at(this->implementation);
+ }
+
+ /// Set the timer's expiry time as an absolute time.
+ /**
+ * This function sets the expiry time. Any pending asynchronous wait
+ * 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.
+ */
+ std::size_t expires_at(const time_type& expiry_time)
+ {
+ return this->service.expires_at(this->implementation, expiry_time);
+ }
+
+ /// Get the timer's expiry time relative to now.
+ /**
+ * This function may be used to obtain the timer's current expiry time.
+ * Whether the timer has expired or not does not affect this value.
+ */
+ duration_type expires_from_now() const
+ {
+ return this->service.expires_from_now(this->implementation);
+ }
+
+ /// Set the timer's expiry time relative to now.
+ /**
+ * This function sets the expiry time. Any pending asynchronous wait
+ * 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.
+ */
+ std::size_t expires_from_now(const duration_type& expiry_time)
+ {
+ return this->service.expires_from_now(this->implementation, expiry_time);
+ }
+
+ /// Perform a blocking wait on the timer.
+ /**
+ * This function is used to wait for the timer to expire. This function
+ * blocks and does not return until the timer has expired.
+ *
+ * @throws asio::error Thrown on failure.
+ */
+ void wait()
+ {
+ this->service.wait(this->implementation);
+ }
+
+ /// Start an asynchronous wait on the timer.
+ /**
+ * This function may be used to initiate an asynchronous wait against the
+ * timer. It always returns immediately.
+ *
+ * For each call to async_wait(), the supplied handler will be called exactly
+ * once. The handler will be called when:
+ *
+ * @li The timer has expired.
+ *
+ * @li The timer was cancelled, in which case the handler is passed the error
+ * code asio::error::operation_aborted.
+ *
+ * @param handler The handler to be called when the timer expires. Copies
+ * will be made of the handler as required. The function signature of the
+ * handler must be:
+ * @code void handler(
+ * const asio::error& error // Result of operation
+ * ); @endcode
+ * Regardless of whether the asynchronous operation completes immediately or
+ * not, the handler will not be invoked from within this function. Invocation
+ * of the handler will be performed in a manner equivalent to using
+ * asio::io_service::post().
+ */
+ template <typename Handler>
+ void async_wait(Handler handler)
+ {
+ this->service.async_wait(this->implementation, handler);
+ }
+};
+
+/**
+ * @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& 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 passed to it
+ * contains the value asio::error::operation_aborted.
+ *
+ * @sa asio::basic_deadline_timer
+ */
+
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_BASIC_DEADLINE_TIMER_HPP
diff --git a/library/include/libtorrent/asio/basic_io_object.hpp b/library/include/libtorrent/asio/basic_io_object.hpp
new file mode 100644
index 000000000..99d8e567f
--- /dev/null
+++ b/library/include/libtorrent/asio/basic_io_object.hpp
@@ -0,0 +1,75 @@
+//
+// basic_io_object.hpp
+// ~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_BASIC_IO_OBJECT_HPP
+#define ASIO_BASIC_IO_OBJECT_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/io_service.hpp"
+#include "asio/detail/noncopyable.hpp"
+
+namespace asio {
+
+/// Base class for all I/O objects.
+template <typename Service>
+class basic_io_object
+ : private noncopyable
+{
+public:
+ /// The type of the service that will be used to provide I/O operations.
+ typedef Service service_type;
+
+ /// The underlying implementation type of I/O object.
+ typedef typename service_type::implementation_type implementation_type;
+
+ /// Get the io_service associated with the object.
+ /**
+ * This function may be used to obtain the io_service object that the I/O
+ * object uses to dispatch handlers for asynchronous operations.
+ *
+ * @return A reference to the io_service object that the I/O object will use
+ * to dispatch handlers. Ownership is not transferred to the caller.
+ */
+ asio::io_service& io_service()
+ {
+ return service.io_service();
+ }
+
+protected:
+ /// Construct a basic_io_object.
+ explicit basic_io_object(asio::io_service& io_service)
+ : service(asio::use_service<Service>(io_service))
+ {
+ service.construct(implementation);
+ }
+
+ /// Protected destructor to prevent deletion through this type.
+ ~basic_io_object()
+ {
+ service.destroy(implementation);
+ }
+
+ // The backend service implementation.
+ service_type& service;
+
+ // The underlying native implementation.
+ implementation_type implementation;
+};
+
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_BASIC_IO_OBJECT_HPP
diff --git a/library/include/libtorrent/asio/basic_resolver.hpp b/library/include/libtorrent/asio/basic_resolver.hpp
new file mode 100644
index 000000000..5df89d545
--- /dev/null
+++ b/library/include/libtorrent/asio/basic_resolver.hpp
@@ -0,0 +1,252 @@
+//
+// basic_resolver.hpp
+// ~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_BASIC_RESOLVER_HPP
+#define ASIO_BASIC_RESOLVER_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/basic_io_object.hpp"
+#include "asio/error.hpp"
+#include "asio/error_handler.hpp"
+#include "asio/resolver_service.hpp"
+
+namespace asio {
+
+/// Provides endpoint resolution functionality.
+/**
+ * The basic_resolver class template provides the ability to resolve a query
+ * to a list of endpoints.
+ *
+ * @par Thread Safety:
+ * @e Distinct @e objects: Safe.@n
+ * @e Shared @e objects: Unsafe.
+ *
+ * @par Concepts:
+ * Async_Object, Error_Source.
+ */
+template <typename Protocol, typename Service = resolver_service<Protocol> >
+class basic_resolver
+ : public basic_io_object<Service>
+{
+public:
+ /// The protocol type.
+ typedef Protocol protocol_type;
+
+ /// The endpoint type.
+ typedef typename Protocol::endpoint endpoint_type;
+
+ /// The query type.
+ typedef typename Protocol::resolver_query query;
+
+ /// The iterator type.
+ typedef typename Protocol::resolver_iterator iterator;
+
+ /// The type used for reporting errors.
+ typedef asio::error error_type;
+
+ /// Constructor.
+ /**
+ * This constructor creates a basic_resolver.
+ *
+ * @param io_service The io_service object that the resolver will use to
+ * dispatch handlers for any asynchronous operations performed on the timer.
+ */
+ explicit basic_resolver(asio::io_service& io_service)
+ : basic_io_object<Service>(io_service)
+ {
+ }
+
+ /// Cancel any asynchronous operations that are waiting on the resolver.
+ /**
+ * This function forces the completion of any pending asynchronous
+ * operations on the host resolver. The handler for each cancelled operation
+ * will be invoked with the asio::error::operation_aborted error code.
+ */
+ void cancel()
+ {
+ return this->service.cancel(this->implementation);
+ }
+
+ /// Resolve a query to a list of entries.
+ /**
+ * This function is used to resolve a query into a list of endpoint entries.
+ *
+ * @param q A query object that determines what endpoints will be returned.
+ *
+ * @returns A forward-only iterator that can be used to traverse the list
+ * of endpoint entries.
+ *
+ * @throws asio::error Thrown on failure.
+ *
+ * @note A default constructed iterator represents the end of the list.
+ *
+ * @note A successful call to this function is guaranteed to return at least
+ * one entry.
+ */
+ iterator resolve(const query& q)
+ {
+ return this->service.resolve(this->implementation, q, throw_error());
+ }
+
+ /// Resolve a query to a list of entries.
+ /**
+ * This function is used to resolve a query into a list of endpoint entries.
+ *
+ * @param q A query object that determines what endpoints will be returned.
+ *
+ * @returns A forward-only iterator that can be used to traverse the list
+ * of endpoint entries. Returns a default constructed iterator if an error
+ * occurs.
+ *
+ * @param error_handler A handler to be called when the operation completes,
+ * to indicate whether or not an error has occurred. Copies will be made of
+ * the handler as required. The function signature of the handler must be:
+ * @code void error_handler(
+ * const asio::error& error // Result of operation.
+ * ); @endcode
+ *
+ * @note A default constructed iterator represents the end of the list.
+ *
+ * @note A successful call to this function is guaranteed to return at least
+ * one entry.
+ */
+ template <typename Error_Handler>
+ iterator resolve(const query& q, Error_Handler error_handler)
+ {
+ return this->service.resolve(this->implementation, q, error_handler);
+ }
+
+ /// Asynchronously resolve a query to a list of entries.
+ /**
+ * This function is used to asynchronously resolve a query into a list of
+ * endpoint entries.
+ *
+ * @param q A query object that determines what endpoints will be returned.
+ *
+ * @param handler The handler to be called when the resolve operation
+ * completes. Copies will be made of the handler as required. The function
+ * signature of the handler must be:
+ * @code void handler(
+ * const asio::error& error, // Result of operation.
+ * resolver::iterator iterator // Forward-only iterator that can be used to
+ * // traverse the list of endpoint entries.
+ * ); @endcode
+ * Regardless of whether the asynchronous operation completes immediately or
+ * not, the handler will not be invoked from within this function. Invocation
+ * of the handler will be performed in a manner equivalent to using
+ * asio::io_service::post().
+ *
+ * @note A default constructed iterator represents the end of the list.
+ *
+ * @note A successful resolve operation is guaranteed to pass at least one
+ * entry to the handler.
+ */
+ template <typename Handler>
+ void async_resolve(const query& q, Handler handler)
+ {
+ return this->service.async_resolve(this->implementation, q, handler);
+ }
+
+ /// Resolve an endpoint to a list of entries.
+ /**
+ * This function is used to resolve an endpoint into a list of endpoint
+ * entries.
+ *
+ * @param e An endpoint object that determines what endpoints will be
+ * returned.
+ *
+ * @returns A forward-only iterator that can be used to traverse the list
+ * of endpoint entries.
+ *
+ * @throws asio::error Thrown on failure.
+ *
+ * @note A default constructed iterator represents the end of the list.
+ *
+ * @note A successful call to this function is guaranteed to return at least
+ * one entry.
+ */
+ iterator resolve(const endpoint_type& e)
+ {
+ return this->service.resolve(this->implementation, e, throw_error());
+ }
+
+ /// Resolve an endpoint to a list of entries.
+ /**
+ * This function is used to resolve an endpoint into a list of endpoint
+ * entries.
+ *
+ * @param e An endpoint object that determines what endpoints will be
+ * returned.
+ *
+ * @returns A forward-only iterator that can be used to traverse the list
+ * of endpoint entries. Returns a default constructed iterator if an error
+ * occurs.
+ *
+ * @param error_handler A handler to be called when the operation completes,
+ * to indicate whether or not an error has occurred. Copies will be made of
+ * the handler as required. The function signature of the handler must be:
+ * @code void error_handler(
+ * const asio::error& error // Result of operation.
+ * ); @endcode
+ *
+ * @note A default constructed iterator represents the end of the list.
+ *
+ * @note A successful call to this function is guaranteed to return at least
+ * one entry.
+ */
+ template <typename Error_Handler>
+ iterator resolve(const endpoint_type& e, Error_Handler error_handler)
+ {
+ return this->service.resolve(this->implementation, e, error_handler);
+ }
+
+ /// Asynchronously resolve an endpoint to a list of entries.
+ /**
+ * This function is used to asynchronously resolve an endpoint into a list of
+ * endpoint entries.
+ *
+ * @param e An endpoint object that determines what endpoints will be
+ * returned.
+ *
+ * @param handler The handler to be called when the resolve operation
+ * completes. Copies will be made of the handler as required. The function
+ * signature of the handler must be:
+ * @code void handler(
+ * const asio::error& error, // Result of operation.
+ * resolver::iterator iterator // Forward-only iterator that can be used to
+ * // traverse the list of endpoint entries.
+ * ); @endcode
+ * Regardless of whether the asynchronous operation completes immediately or
+ * not, the handler will not be invoked from within this function. Invocation
+ * of the handler will be performed in a manner equivalent to using
+ * asio::io_service::post().
+ *
+ * @note A default constructed iterator represents the end of the list.
+ *
+ * @note A successful resolve operation is guaranteed to pass at least one
+ * entry to the handler.
+ */
+ template <typename Handler>
+ void async_resolve(const endpoint_type& e, Handler handler)
+ {
+ return this->service.async_resolve(this->implementation, e, handler);
+ }
+};
+
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_BASIC_RESOLVER_HPP
diff --git a/library/include/libtorrent/asio/basic_socket.hpp b/library/include/libtorrent/asio/basic_socket.hpp
new file mode 100644
index 000000000..2cc31404c
--- /dev/null
+++ b/library/include/libtorrent/asio/basic_socket.hpp
@@ -0,0 +1,919 @@
+//
+// basic_socket.hpp
+// ~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_BASIC_SOCKET_HPP
+#define ASIO_BASIC_SOCKET_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/basic_io_object.hpp"
+#include "asio/error.hpp"
+#include "asio/error_handler.hpp"
+#include "asio/socket_base.hpp"
+
+namespace asio {
+
+/// Provides socket functionality.
+/**
+ * The basic_socket class template provides functionality that is common to both
+ * stream-oriented and datagram-oriented sockets.
+ *
+ * @par Thread Safety:
+ * @e Distinct @e objects: Safe.@n
+ * @e Shared @e objects: Unsafe.
+ *
+ * @par Concepts:
+ * Error_Source, IO_Object.
+ */
+template <typename Protocol, typename Service>
+class basic_socket
+ : public basic_io_object<Service>,
+ public socket_base
+{
+public:
+ /// The native representation of a socket.
+ typedef typename Service::native_type native_type;
+
+ /// The protocol type.
+ typedef Protocol protocol_type;
+
+ /// The endpoint type.
+ typedef typename Protocol::endpoint endpoint_type;
+
+ /// The type used for reporting errors.
+ typedef asio::error error_type;
+
+ /// A basic_socket is always the lowest layer.
+ typedef basic_socket<Protocol, Service> lowest_layer_type;
+
+ /// Construct a basic_socket without opening it.
+ /**
+ * This constructor creates a socket without opening it.
+ *
+ * @param io_service The io_service object that the socket will use to
+ * dispatch handlers for any asynchronous operations performed on the socket.
+ */
+ explicit basic_socket(asio::io_service& io_service)
+ : basic_io_object<Service>(io_service)
+ {
+ }
+
+ /// Construct and open a basic_socket.
+ /**
+ * This constructor creates and opens a socket.
+ *
+ * @param io_service The io_service object that the socket will use to
+ * dispatch handlers for any asynchronous operations performed on the socket.
+ *
+ * @param protocol An object specifying protocol parameters to be used.
+ *
+ * @throws asio::error Thrown on failure.
+ */
+ basic_socket(asio::io_service& io_service,
+ const protocol_type& protocol)
+ : basic_io_object<Service>(io_service)
+ {
+ this->service.open(this->implementation, protocol, throw_error());
+ }
+
+ /// Construct a basic_socket, opening it and binding it to the given local
+ /// endpoint.
+ /**
+ * This constructor creates a socket and automatically opens it bound to the
+ * specified endpoint on the local machine. The protocol used is the protocol
+ * associated with the given endpoint.
+ *
+ * @param io_service The io_service object that the socket will use to
+ * dispatch handlers for any asynchronous operations performed on the socket.
+ *
+ * @param endpoint An endpoint on the local machine to which the socket will
+ * be bound.
+ *
+ * @throws asio::error Thrown on failure.
+ */
+ basic_socket(asio::io_service& io_service,
+ const endpoint_type& endpoint)
+ : basic_io_object<Service>(io_service)
+ {
+ this->service.open(this->implementation, endpoint.protocol(),
+ throw_error());
+ this->service.bind(this->implementation, endpoint, throw_error());
+ }
+
+ /// Construct a basic_socket on an existing native socket.
+ /**
+ * This constructor creates a socket object to hold an existing native socket.
+ *
+ * @param io_service The io_service object that the socket will use to
+ * dispatch handlers for any asynchronous operations performed on the socket.
+ *
+ * @param protocol An object specifying protocol parameters to be used.
+ *
+ * @param native_socket A native socket.
+ *
+ * @throws asio::error Thrown on failure.
+ */
+ basic_socket(asio::io_service& io_service,
+ const protocol_type& protocol, const native_type& native_socket)
+ : basic_io_object<Service>(io_service)
+ {
+ this->service.assign(this->implementation, protocol, native_socket,
+ throw_error());
+ }
+
+ /// Get a reference to the lowest layer.
+ /**
+ * This function returns a reference to the lowest layer in a stack of
+ * layers. Since a basic_socket cannot contain any further layers, it simply
+ * returns a reference to itself.
+ *
+ * @return A reference to the lowest layer in the stack of layers. Ownership
+ * is not transferred to the caller.
+ */
+ lowest_layer_type& lowest_layer()
+ {
+ return *this;
+ }
+
+ /// Open the socket using the specified protocol.
+ /**
+ * This function opens the socket so that it will use the specified protocol.
+ *
+ * @param protocol An object specifying protocol parameters to be used.
+ *
+ * @throws asio::error Thrown on failure.
+ *
+ * @par Example:
+ * @code
+ * asio::ip::tcp::socket socket(io_service);
+ * socket.open(asio::ip::tcp::v4());
+ * @endcode
+ */
+ void open(const protocol_type& protocol = protocol_type())
+ {
+ this->service.open(this->implementation, protocol, throw_error());
+ }
+
+ /// Open the socket using the specified protocol.
+ /**
+ * This function opens the socket so that it will use the specified protocol.
+ *
+ * @param protocol An object specifying which protocol is to be used.
+ *
+ * @param error_handler A handler to be called when the operation completes,
+ * to indicate whether or not an error has occurred. Copies will be made of
+ * the handler as required. The function signature of the handler must be:
+ * @code void error_handler(
+ * const asio::error& error // Result of operation
+ * ); @endcode
+ *
+ * @par Example:
+ * @code
+ * asio::ip::tcp::socket socket(io_service);
+ * asio::error error;
+ * socket.open(asio::ip::tcp::v4(), asio::assign_error(error));
+ * if (error)
+ * {
+ * // An error occurred.
+ * }
+ * @endcode
+ */
+ template <typename Error_Handler>
+ void open(const protocol_type& protocol, Error_Handler error_handler)
+ {
+ this->service.open(this->implementation, protocol, error_handler);
+ }
+
+ /// Assign an existing native socket to the socket.
+ /*
+ * This function opens the socket to hold an existing native socket.
+ *
+ * @param protocol An object specifying which protocol is to be used.
+ *
+ * @param native_socket A native socket.
+ *
+ * @throws asio::error Thrown on failure.
+ */
+ void assign(const protocol_type& protocol, const native_type& native_socket)
+ {
+ this->service.assign(this->implementation, protocol, native_socket,
+ throw_error());
+ }
+
+ /// Assign an existing native socket to the socket.
+ /*
+ * This function opens the socket to hold an existing native socket.
+ *
+ * @param protocol An object specifying which protocol is to be used.
+ *
+ * @param native_socket A native socket.
+ *
+ * @param error_handler A handler to be called when the operation completes,
+ * to indicate whether or not an error has occurred. Copies will be made of
+ * the handler as required. The function signature of the handler must be:
+ * @code void error_handler(
+ * const asio::error& error // Result of operation
+ * ); @endcode
+ */
+ template <typename Error_Handler>
+ void assign(const protocol_type& protocol, const native_type& native_socket,
+ Error_Handler error_handler)
+ {
+ this->service.assign(this->implementation, protocol, native_socket,
+ error_handler);
+ }
+
+ /// Close the socket.
+ /**
+ * This function is used to close the socket. Any asynchronous send, receive
+ * or connect operations will be cancelled immediately, and will complete
+ * with the asio::error::operation_aborted error.
+ *
+ * @throws asio::error Thrown on failure.
+ */
+ void close()
+ {
+ this->service.close(this->implementation, throw_error());
+ }
+
+ /// Close the socket.
+ /**
+ * This function is used to close the socket. Any asynchronous send, receive
+ * or connect operations will be cancelled immediately, and will complete
+ * with the asio::error::operation_aborted error.
+ *
+ * @param error_handler A handler to be called when the operation completes,
+ * to indicate whether or not an error has occurred. Copies will be made of
+ * the handler as required. The function signature of the handler must be:
+ * @code void error_handler(
+ * const asio::error& error // Result of operation
+ * ); @endcode
+ *
+ * @par Example:
+ * @code
+ * asio::ip::tcp::socket socket(io_service);
+ * ...
+ * asio::error error;
+ * socket.close(asio::assign_error(error));
+ * if (error)
+ * {
+ * // An error occurred.
+ * }
+ * @endcode
+ */
+ template <typename Error_Handler>
+ void close(Error_Handler error_handler)
+ {
+ this->service.close(this->implementation, error_handler);
+ }
+
+ /// Get the native socket representation.
+ /**
+ * This function may be used to obtain the underlying representation of the
+ * socket. This is intended to allow access to native socket functionality
+ * that is not otherwise provided.
+ */
+ native_type native()
+ {
+ return this->service.native(this->implementation);
+ }
+
+ /// Cancel all asynchronous operations associated with the socket.
+ /**
+ * This function causes all outstanding asynchronous connect, send and receive
+ * operations to finish immediately, and the handlers for cancelled operations
+ * will be passed the asio::error::operation_aborted error.
+ *
+ * @throws asio::error Thrown on failure.
+ */
+ void cancel()
+ {
+ this->service.cancel(this->implementation, throw_error());
+ }
+
+ /// Cancel all asynchronous operations associated with the socket.
+ /**
+ * This function causes all outstanding asynchronous connect, send and receive
+ * operations to finish immediately, and the handlers for cancelled operations
+ * will be passed the asio::error::operation_aborted error.
+ *
+ * @param error_handler A handler to be called when the operation completes,
+ * to indicate whether or not an error has occurred. Copies will be made of
+ * the handler as required. The function signature of the handler must be:
+ * @code void error_handler(
+ * const asio::error& error // Result of operation
+ * ); @endcode
+ */
+ template <typename Error_Handler>
+ void cancel(Error_Handler error_handler)
+ {
+ this->service.cancel(this->implementation, error_handler);
+ }
+
+ /// Bind the socket to the given local endpoint.
+ /**
+ * This function binds the socket to the specified endpoint on the local
+ * machine.
+ *
+ * @param endpoint An endpoint on the local machine to which the socket will
+ * be bound.
+ *
+ * @throws asio::error Thrown on failure.
+ *
+ * @par Example:
+ * @code
+ * asio::ip::tcp::socket socket(io_service);
+ * socket.open(asio::ip::tcp::v4());
+ * socket.bind(asio::ip::tcp::endpoint(
+ * asio::ip::tcp::v4(), 12345));
+ * @endcode
+ */
+ void bind(const endpoint_type& endpoint)
+ {
+ this->service.bind(this->implementation, endpoint, throw_error());
+ }
+
+ /// Bind the socket to the given local endpoint.
+ /**
+ * This function binds the socket to the specified endpoint on the local
+ * machine.
+ *
+ * @param endpoint An endpoint on the local machine to which the socket will
+ * be bound.
+ *
+ * @param error_handler A handler to be called when the operation completes,
+ * to indicate whether or not an error has occurred. Copies will be made of
+ * the handler as required. The function signature of the handler must be:
+ * @code void error_handler(
+ * const asio::error& error // Result of operation
+ * ); @endcode
+ *
+ * @par Example:
+ * @code
+ * asio::ip::tcp::socket socket(io_service);
+ * socket.open(asio::ip::tcp::v4());
+ * asio::error error;
+ * socket.bind(asio::ip::tcp::endpoint(
+ * asio::ip::tcp::v4(), 12345),
+ * asio::assign_error(error));
+ * if (error)
+ * {
+ * // An error occurred.
+ * }
+ * @endcode
+ */
+ template <typename Error_Handler>
+ void bind(const endpoint_type& endpoint, Error_Handler error_handler)
+ {
+ this->service.bind(this->implementation, endpoint, error_handler);
+ }
+
+ /// Connect the socket to the specified endpoint.
+ /**
+ * This function is used to connect a socket to the specified remote endpoint.
+ * The function call will block until the connection is successfully made or
+ * an error occurs.
+ *
+ * The socket is automatically opened if it is not already open. If the
+ * connect fails, and the socket was automatically opened, the socket is
+ * returned to the closed state.
+ *
+ * @param peer_endpoint The remote endpoint to which the socket will be
+ * connected.
+ *
+ * @throws asio::error Thrown on failure.
+ *
+ * @par Example:
+ * @code
+ * asio::ip::tcp::socket socket(io_service);
+ * asio::ip::tcp::endpoint endpoint(
+ * asio::ip::address::from_string("1.2.3.4"), 12345);
+ * socket.connect(endpoint);
+ * @endcode
+ */
+ void connect(const endpoint_type& peer_endpoint)
+ {
+ this->service.connect(this->implementation, peer_endpoint, throw_error());
+ }
+
+ /// Connect the socket to the specified endpoint.
+ /**
+ * This function is used to connect a socket to the specified remote endpoint.
+ * The function call will block until the connection is successfully made or
+ * an error occurs.
+ *
+ * The socket is automatically opened if it is not already open. If the
+ * connect fails, and the socket was automatically opened, the socket is
+ * returned to the closed state.
+ *
+ * @param peer_endpoint The remote endpoint to which the socket will be
+ * connected.
+ *
+ * @param error_handler A handler to be called when the operation completes,
+ * to indicate whether or not an error has occurred. Copies will be made of
+ * the handler as required. The function signature of the handler must be:
+ * @code void error_handler(
+ * const asio::error& error // Result of operation
+ * ); @endcode
+ *
+ * @par Example:
+ * @code
+ * asio::ip::tcp::socket socket(io_service);
+ * asio::ip::tcp::endpoint endpoint(
+ * asio::ip::address::from_string("1.2.3.4"), 12345);
+ * asio::error error;
+ * socket.connect(endpoint, asio::assign_error(error));
+ * if (error)
+ * {
+ * // An error occurred.
+ * }
+ * @endcode
+ */
+ template <typename Error_Handler>
+ void connect(const endpoint_type& peer_endpoint, Error_Handler error_handler)
+ {
+ this->service.connect(this->implementation, peer_endpoint, error_handler);
+ }
+
+ /// Start an asynchronous connect.
+ /**
+ * This function is used to asynchronously connect a socket to the specified
+ * remote endpoint. The function call always returns immediately.
+ *
+ * The socket is automatically opened if it is not already open. If the
+ * connect fails, and the socket was automatically opened, the socket is
+ * returned to the closed state.
+ *
+ * @param peer_endpoint The remote endpoint to which the socket will be
+ * connected. Copies will be made of the endpoint object as required.
+ *
+ * @param handler The handler to be called when the connection operation
+ * completes. Copies will be made of the handler as required. The function
+ * signature of the handler must be:
+ * @code void handler(
+ * const asio::error& error // Result of operation
+ * ); @endcode
+ * Regardless of whether the asynchronous operation completes immediately or
+ * not, the handler will not be invoked from within this function. Invocation
+ * of the handler will be performed in a manner equivalent to using
+ * asio::io_service::post().
+ *
+ * @par Example:
+ * @code
+ * void connect_handler(const asio::error& error)
+ * {
+ * if (!error)
+ * {
+ * // Connect succeeded.
+ * }
+ * }
+ *
+ * ...
+ *
+ * asio::ip::tcp::socket socket(io_service);
+ * asio::ip::tcp::endpoint endpoint(
+ * asio::ip::address::from_string("1.2.3.4"), 12345);
+ * socket.async_connect(endpoint, connect_handler);
+ * @endcode
+ */
+ template <typename Handler>
+ void async_connect(const endpoint_type& peer_endpoint, Handler handler)
+ {
+ this->service.async_connect(this->implementation, peer_endpoint, handler);
+ }
+
+ /// Set an option on the socket.
+ /**
+ * This function is used to set an option on the socket.
+ *
+ * @param option The new option value to be set on the socket.
+ *
+ * @throws asio::error Thrown on failure.
+ *
+ * @sa Socket_Option @n
+ * asio::socket_base::broadcast @n
+ * asio::socket_base::do_not_route @n
+ * asio::socket_base::keep_alive @n
+ * asio::socket_base::linger @n
+ * asio::socket_base::receive_buffer_size @n
+ * asio::socket_base::receive_low_watermark @n
+ * asio::socket_base::reuse_address @n
+ * asio::socket_base::send_buffer_size @n
+ * asio::socket_base::send_low_watermark @n
+ * asio::ip::multicast::join_group @n
+ * asio::ip::multicast::leave_group @n
+ * asio::ip::multicast::enable_loopback @n
+ * asio::ip::multicast::outbound_interface @n
+ * asio::ip::multicast::hops @n
+ * asio::ip::tcp::no_delay
+ *
+ * @par Example:
+ * Setting the IPPROTO_TCP/TCP_NODELAY option:
+ * @code
+ * asio::ip::tcp::socket socket(io_service);
+ * ...
+ * asio::ip::tcp::no_delay option(true);
+ * socket.set_option(option);
+ * @endcode
+ */
+ template <typename Socket_Option>
+ void set_option(const Socket_Option& option)
+ {
+ this->service.set_option(this->implementation, option, throw_error());
+ }
+
+ /// Set an option on the socket.
+ /**
+ * This function is used to set an option on the socket.
+ *
+ * @param option The new option value to be set on the socket.
+ *
+ * @param error_handler A handler to be called when the operation completes,
+ * to indicate whether or not an error has occurred. Copies will be made of
+ * the handler as required. The function signature of the handler must be:
+ * @code void error_handler(
+ * const asio::error& error // Result of operation
+ * ); @endcode
+ *
+ * @sa Socket_Option @n
+ * asio::socket_base::broadcast @n
+ * asio::socket_base::do_not_route @n
+ * asio::socket_base::keep_alive @n
+ * asio::socket_base::linger @n
+ * asio::socket_base::receive_buffer_size @n
+ * asio::socket_base::receive_low_watermark @n
+ * asio::socket_base::reuse_address @n
+ * asio::socket_base::send_buffer_size @n
+ * asio::socket_base::send_low_watermark @n
+ * asio::ip::multicast::join_group @n
+ * asio::ip::multicast::leave_group @n
+ * asio::ip::multicast::enable_loopback @n
+ * asio::ip::multicast::outbound_interface @n
+ * asio::ip::multicast::hops @n
+ * asio::ip::tcp::no_delay
+ *
+ * @par Example:
+ * Setting the IPPROTO_TCP/TCP_NODELAY option:
+ * @code
+ * asio::ip::tcp::socket socket(io_service);
+ * ...
+ * asio::ip::tcp::no_delay option(true);
+ * asio::error error;
+ * socket.set_option(option, asio::assign_error(error));
+ * if (error)
+ * {
+ * // An error occurred.
+ * }
+ * @endcode
+ */
+ template <typename Socket_Option, typename Error_Handler>
+ void set_option(const Socket_Option& option, Error_Handler error_handler)
+ {
+ this->service.set_option(this->implementation, option, error_handler);
+ }
+
+ /// Get an option from the socket.
+ /**
+ * This function is used to get the current value of an option on the socket.
+ *
+ * @param option The option value to be obtained from the socket.
+ *
+ * @throws asio::error Thrown on failure.
+ *
+ * @sa Socket_Option @n
+ * asio::socket_base::broadcast @n
+ * asio::socket_base::do_not_route @n
+ * asio::socket_base::keep_alive @n
+ * asio::socket_base::linger @n
+ * asio::socket_base::receive_buffer_size @n
+ * asio::socket_base::receive_low_watermark @n
+ * asio::socket_base::reuse_address @n
+ * asio::socket_base::send_buffer_size @n
+ * asio::socket_base::send_low_watermark @n
+ * asio::ip::multicast::join_group @n
+ * asio::ip::multicast::leave_group @n
+ * asio::ip::multicast::enable_loopback @n
+ * asio::ip::multicast::outbound_interface @n
+ * asio::ip::multicast::hops @n
+ * asio::ip::tcp::no_delay
+ *
+ * @par Example:
+ * Getting the value of the SOL_SOCKET/SO_KEEPALIVE option:
+ * @code
+ * asio::ip::tcp::socket socket(io_service);
+ * ...
+ * asio::ip::tcp::socket::keep_alive option;
+ * socket.get_option(option);
+ * bool is_set = option.get();
+ * @endcode
+ */
+ template <typename Socket_Option>
+ void get_option(Socket_Option& option) const
+ {
+ this->service.get_option(this->implementation, option, throw_error());
+ }
+
+ /// Get an option from the socket.
+ /**
+ * This function is used to get the current value of an option on the socket.
+ *
+ * @param option The option value to be obtained from the socket.
+ *
+ * @param error_handler A handler to be called when the operation completes,
+ * to indicate whether or not an error has occurred. Copies will be made of
+ * the handler as required. The function signature of the handler must be:
+ * @code void error_handler(
+ * const asio::error& error // Result of operation
+ * ); @endcode
+ *
+ * @sa Socket_Option @n
+ * asio::socket_base::broadcast @n
+ * asio::socket_base::do_not_route @n
+ * asio::socket_base::keep_alive @n
+ * asio::socket_base::linger @n
+ * asio::socket_base::receive_buffer_size @n
+ * asio::socket_base::receive_low_watermark @n
+ * asio::socket_base::reuse_address @n
+ * asio::socket_base::send_buffer_size @n
+ * asio::socket_base::send_low_watermark @n
+ * asio::ip::multicast::join_group @n
+ * asio::ip::multicast::leave_group @n
+ * asio::ip::multicast::enable_loopback @n
+ * asio::ip::multicast::outbound_interface @n
+ * asio::ip::multicast::hops @n
+ * asio::ip::tcp::no_delay
+ *
+ * @par Example:
+ * Getting the value of the SOL_SOCKET/SO_KEEPALIVE option:
+ * @code
+ * asio::ip::tcp::socket socket(io_service);
+ * ...
+ * asio::ip::tcp::socket::keep_alive option;
+ * asio::error error;
+ * socket.get_option(option, asio::assign_error(error));
+ * if (error)
+ * {
+ * // An error occurred.
+ * }
+ * bool is_set = option.get();
+ * @endcode
+ */
+ template <typename Socket_Option, typename Error_Handler>
+ void get_option(Socket_Option& option, Error_Handler error_handler) const
+ {
+ this->service.get_option(this->implementation, option, error_handler);
+ }
+
+ /// Perform an IO control command on the socket.
+ /**
+ * This function is used to execute an IO control command on the socket.
+ *
+ * @param command The IO control command to be performed on the socket.
+ *
+ * @throws asio::error Thrown on failure.
+ *
+ * @sa IO_Control_Command @n
+ * asio::socket_base::bytes_readable @n
+ * asio::socket_base::non_blocking_io
+ *
+ * @par Example:
+ * Getting the number of bytes ready to read:
+ * @code
+ * asio::ip::tcp::socket socket(io_service);
+ * ...
+ * asio::ip::tcp::socket::bytes_readable command;
+ * socket.io_control(command);
+ * std::size_t bytes_readable = command.get();
+ * @endcode
+ */
+ template <typename IO_Control_Command>
+ void io_control(IO_Control_Command& command)
+ {
+ this->service.io_control(this->implementation, command, throw_error());
+ }
+
+ /// Perform an IO control command on the socket.
+ /**
+ * This function is used to execute an IO control command on the socket.
+ *
+ * @param command The IO control command to be performed on the socket.
+ *
+ * @param error_handler A handler to be called when the operation completes,
+ * to indicate whether or not an error has occurred. Copies will be made of
+ * the handler as required. The function signature of the handler must be:
+ * @code void error_handler(
+ * const asio::error& error // Result of operation
+ * ); @endcode
+ *
+ * @sa IO_Control_Command @n
+ * asio::socket_base::bytes_readable @n
+ * asio::socket_base::non_blocking_io
+ *
+ * @par Example:
+ * Getting the number of bytes ready to read:
+ * @code
+ * asio::ip::tcp::socket socket(io_service);
+ * ...
+ * asio::ip::tcp::socket::bytes_readable command;
+ * asio::error error;
+ * socket.io_control(command, asio::assign_error(error));
+ * if (error)
+ * {
+ * // An error occurred.
+ * }
+ * std::size_t bytes_readable = command.get();
+ * @endcode
+ */
+ template <typename IO_Control_Command, typename Error_Handler>
+ void io_control(IO_Control_Command& command, Error_Handler error_handler)
+ {
+ this->service.io_control(this->implementation, command, error_handler);
+ }
+
+ /// Get the local endpoint of the socket.
+ /**
+ * This function is used to obtain the locally bound endpoint of the socket.
+ *
+ * @returns An object that represents the local endpoint of the socket.
+ *
+ * @throws asio::error Thrown on failure.
+ *
+ * @par Example:
+ * @code
+ * asio::ip::tcp::socket socket(io_service);
+ * ...
+ * asio::ip::tcp::endpoint endpoint = socket.local_endpoint();
+ * @endcode
+ */
+ endpoint_type local_endpoint() const
+ {
+ return this->service.local_endpoint(this->implementation, throw_error());
+ }
+
+ /// Get the local endpoint of the socket.
+ /**
+ * This function is used to obtain the locally bound endpoint of the socket.
+ *
+ * @param error_handler A handler to be called when the operation completes,
+ * to indicate whether or not an error has occurred. Copies will be made of
+ * the handler as required. The function signature of the handler must be:
+ * @code void error_handler(
+ * const asio::error& error // Result of operation
+ * ); @endcode
+ *
+ * @returns An object that represents the local endpoint of the socket.
+ * Returns a default-constructed endpoint object if an error occurred and the
+ * error handler did not throw an exception.
+ *
+ * @par Example:
+ * @code
+ * asio::ip::tcp::socket socket(io_service);
+ * ...
+ * asio::error error;
+ * asio::ip::tcp::endpoint endpoint
+ * = socket.local_endpoint(asio::assign_error(error));
+ * if (error)
+ * {
+ * // An error occurred.
+ * }
+ * @endcode
+ */
+ template <typename Error_Handler>
+ endpoint_type local_endpoint(Error_Handler error_handler) const
+ {
+ return this->service.local_endpoint(this->implementation, error_handler);
+ }
+
+ /// Get the remote endpoint of the socket.
+ /**
+ * This function is used to obtain the remote endpoint of the socket.
+ *
+ * @returns An object that represents the remote endpoint of the socket.
+ *
+ * @throws asio::error Thrown on failure.
+ *
+ * @par Example:
+ * @code
+ * asio::ip::tcp::socket socket(io_service);
+ * ...
+ * asio::ip::tcp::endpoint endpoint = socket.remote_endpoint();
+ * @endcode
+ */
+ endpoint_type remote_endpoint() const
+ {
+ return this->service.remote_endpoint(this->implementation, throw_error());
+ }
+
+ /// Get the remote endpoint of the socket.
+ /**
+ * This function is used to obtain the remote endpoint of the socket.
+ *
+ * @param error_handler A handler to be called when the operation completes,
+ * to indicate whether or not an error has occurred. Copies will be made of
+ * the handler as required. The function signature of the handler must be:
+ * @code void error_handler(
+ * const asio::error& error // Result of operation
+ * ); @endcode
+ *
+ * @returns An object that represents the remote endpoint of the socket.
+ * Returns a default-constructed endpoint object if an error occurred and the
+ * error handler did not throw an exception.
+ *
+ * @par Example:
+ * @code
+ * asio::ip::tcp::socket socket(io_service);
+ * ...
+ * asio::error error;
+ * asio::ip::tcp::endpoint endpoint
+ * = socket.remote_endpoint(asio::assign_error(error));
+ * if (error)
+ * {
+ * // An error occurred.
+ * }
+ * @endcode
+ */
+ template <typename Error_Handler>
+ endpoint_type remote_endpoint(Error_Handler error_handler) const
+ {
+ return this->service.remote_endpoint(this->implementation, error_handler);
+ }
+
+ /// Disable sends or receives on the socket.
+ /**
+ * This function is used to disable send operations, receive operations, or
+ * both.
+ *
+ * @param what Determines what types of operation will no longer be allowed.
+ *
+ * @throws asio::error Thrown on failure.
+ *
+ * @par Example:
+ * Shutting down the send side of the socket:
+ * @code
+ * asio::ip::tcp::socket socket(io_service);
+ * ...
+ * socket.shutdown(asio::ip::tcp::socket::shutdown_send);
+ * @endcode
+ */
+ void shutdown(shutdown_type what)
+ {
+ this->service.shutdown(this->implementation, what, throw_error());
+ }
+
+ /// Disable sends or receives on the socket.
+ /**
+ * This function is used to disable send operations, receive operations, or
+ * both.
+ *
+ * @param what Determines what types of operation will no longer be allowed.
+ *
+ * @param error_handler A handler to be called when the operation completes,
+ * to indicate whether or not an error has occurred. Copies will be made of
+ * the handler as required. The function signature of the handler must be:
+ * @code void error_handler(
+ * const asio::error& error // Result of operation
+ * ); @endcode
+ *
+ * @par Example:
+ * Shutting down the send side of the socket:
+ * @code
+ * asio::ip::tcp::socket socket(io_service);
+ * ...
+ * asio::error error;
+ * socket.shutdown(asio::ip::tcp::socket::shutdown_send,
+ * asio::assign_error(error));
+ * if (error)
+ * {
+ * // An error occurred.
+ * }
+ * @endcode
+ */
+ template <typename Error_Handler>
+ void shutdown(shutdown_type what, Error_Handler error_handler)
+ {
+ this->service.shutdown(this->implementation, what, error_handler);
+ }
+
+protected:
+ /// Protected destructor to prevent deletion through this type.
+ ~basic_socket()
+ {
+ }
+};
+
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_BASIC_SOCKET_HPP
diff --git a/library/include/libtorrent/asio/basic_socket_acceptor.hpp b/library/include/libtorrent/asio/basic_socket_acceptor.hpp
new file mode 100644
index 000000000..c646dc9d2
--- /dev/null
+++ b/library/include/libtorrent/asio/basic_socket_acceptor.hpp
@@ -0,0 +1,854 @@
+//
+// basic_socket_acceptor.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_BASIC_SOCKET_ACCEPTOR_HPP
+#define ASIO_BASIC_SOCKET_ACCEPTOR_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/basic_io_object.hpp"
+#include "asio/basic_socket.hpp"
+#include "asio/error.hpp"
+#include "asio/error_handler.hpp"
+#include "asio/socket_acceptor_service.hpp"
+#include "asio/socket_base.hpp"
+
+namespace asio {
+
+/// Provides the ability to accept new connections.
+/**
+ * The basic_socket_acceptor class template is used for accepting new socket
+ * connections.
+ *
+ * @par Thread Safety:
+ * @e Distinct @e objects: Safe.@n
+ * @e Shared @e objects: Unsafe.
+ *
+ * @par Concepts:
+ * Async_Object, Error_Source.
+ *
+ * @par Example:
+ * Opening a socket acceptor with the SO_REUSEADDR option enabled:
+ * @code
+ * asio::ip::tcp::acceptor acceptor(io_service);
+ * asio::ip::tcp::endpoint endpoint(asio::ip::tcp::v4(), port);
+ * acceptor.open(endpoint.protocol());
+ * acceptor.set_option(asio::ip::tcp::acceptor::reuse_address(true));
+ * acceptor.bind(endpoint);
+ * acceptor.listen();
+ * @endcode
+ */
+template <typename Protocol,
+ typename Service = socket_acceptor_service<Protocol> >
+class basic_socket_acceptor
+ : public basic_io_object<Service>,
+ public socket_base
+{
+public:
+ /// The native representation of an acceptor.
+ typedef typename Service::native_type native_type;
+
+ /// The protocol type.
+ typedef Protocol protocol_type;
+
+ /// The endpoint type.
+ typedef typename Protocol::endpoint endpoint_type;
+
+ /// The type used for reporting errors.
+ typedef asio::error error_type;
+
+ /// Construct an acceptor without opening it.
+ /**
+ * This constructor creates an acceptor without opening it to listen for new
+ * connections. The open() function must be called before the acceptor can
+ * accept new socket connections.
+ *
+ * @param io_service The io_service object that the acceptor will use to
+ * dispatch handlers for any asynchronous operations performed on the
+ * acceptor.
+ */
+ explicit basic_socket_acceptor(asio::io_service& io_service)
+ : basic_io_object<Service>(io_service)
+ {
+ }
+
+ /// Construct an open acceptor.
+ /**
+ * This constructor creates an acceptor and automatically opens it.
+ *
+ * @param io_service The io_service object that the acceptor will use to
+ * dispatch handlers for any asynchronous operations performed on the
+ * acceptor.
+ *
+ * @param protocol An object specifying protocol parameters to be used.
+ *
+ * @throws asio::error Thrown on failure.
+ */
+ basic_socket_acceptor(asio::io_service& io_service,
+ const protocol_type& protocol)
+ : basic_io_object<Service>(io_service)
+ {
+ this->service.open(this->implementation, protocol, throw_error());
+ }
+
+ /// Construct an acceptor opened on the given endpoint.
+ /**
+ * This constructor creates an acceptor and automatically opens it to listen
+ * for new connections on the specified endpoint.
+ *
+ * @param io_service The io_service object that the acceptor will use to
+ * dispatch handlers for any asynchronous operations performed on the
+ * acceptor.
+ *
+ * @param endpoint An endpoint on the local machine on which the acceptor
+ * will listen for new connections.
+ *
+ * @param reuse_addr Whether the constructor should set the socket option
+ * socket_base::reuse_address.
+ *
+ * @throws asio::error Thrown on failure.
+ *
+ * @note This constructor is equivalent to the following code:
+ * @code
+ * basic_socket_acceptor<Protocol> acceptor(io_service);
+ * acceptor.open(endpoint.protocol());
+ * if (reuse_addr)
+ * acceptor.set_option(socket_base::reuse_address(true));
+ * acceptor.bind(endpoint);
+ * acceptor.listen(listen_backlog);
+ * @endcode
+ */
+ basic_socket_acceptor(asio::io_service& io_service,
+ const endpoint_type& endpoint, bool reuse_addr = true)
+ : basic_io_object<Service>(io_service)
+ {
+ this->service.open(this->implementation, endpoint.protocol(),
+ throw_error());
+ if (reuse_addr)
+ {
+ this->service.set_option(this->implementation,
+ socket_base::reuse_address(true), throw_error());
+ }
+ this->service.bind(this->implementation, endpoint, throw_error());
+ this->service.listen(this->implementation,
+ socket_base::max_connections, throw_error());
+ }
+
+ /// Construct a basic_socket_acceptor on an existing native acceptor.
+ /**
+ * This constructor creates an acceptor object to hold an existing native
+ * acceptor.
+ *
+ * @param io_service The io_service object that the acceptor will use to
+ * dispatch handlers for any asynchronous operations performed on the
+ * acceptor.
+ *
+ * @param protocol An object specifying protocol parameters to be used.
+ *
+ * @param native_acceptor A native acceptor.
+ *
+ * @throws asio::error Thrown on failure.
+ */
+ basic_socket_acceptor(asio::io_service& io_service,
+ const protocol_type& protocol, const native_type& native_acceptor)
+ : basic_io_object<Service>(io_service)
+ {
+ this->service.assign(this->implementation, protocol, native_acceptor,
+ throw_error());
+ }
+
+ /// Open the acceptor using the specified protocol.
+ /**
+ * This function opens the socket acceptor so that it will use the specified
+ * protocol.
+ *
+ * @param protocol An object specifying which protocol is to be used.
+ *
+ * @par Example:
+ * @code
+ * asio::ip::tcp::acceptor acceptor(io_service);
+ * acceptor.open(asio::ip::tcp::v4());
+ * @endcode
+ */
+ void open(const protocol_type& protocol = protocol_type())
+ {
+ this->service.open(this->implementation, protocol, throw_error());
+ }
+
+ /// Open the acceptor using the specified protocol.
+ /**
+ * This function opens the socket acceptor so that it will use the specified
+ * protocol.
+ *
+ * @param protocol An object specifying which protocol is to be used.
+ *
+ * @param error_handler A handler to be called when the operation completes,
+ * to indicate whether or not an error has occurred. Copies will be made of
+ * the handler as required. The function signature of the handler must be:
+ * @code void error_handler(
+ * const asio::error& error // Result of operation
+ * ); @endcode
+ *
+ * @par Example:
+ * @code
+ * asio::ip::tcp::acceptor acceptor(io_service);
+ * asio::error error;
+ * acceptor.open(asio::ip::tcp::v4(),
+ * asio::assign_error(error));
+ * if (error)
+ * {
+ * // An error occurred.
+ * }
+ * @endcode
+ */
+ template <typename Error_Handler>
+ void open(const protocol_type& protocol, Error_Handler error_handler)
+ {
+ this->service.open(this->implementation, protocol, error_handler);
+ }
+
+ /// Assigns an existing native acceptor to the acceptor.
+ /*
+ * This function opens the acceptor to hold an existing native acceptor.
+ *
+ * @param protocol An object specifying which protocol is to be used.
+ *
+ * @param native_acceptor A native acceptor.
+ *
+ * @throws asio::error Thrown on failure.
+ */
+ void assign(const protocol_type& protocol, const native_type& native_acceptor)
+ {
+ this->service.assign(this->implementation, protocol, native_acceptor,
+ throw_error());
+ }
+
+ /// Assigns an existing native acceptor to the acceptor.
+ /*
+ * This function opens the acceptor to hold an existing native acceptor.
+ *
+ * @param protocol An object specifying which protocol is to be used.
+ *
+ * @param native_acceptor A native acceptor.
+ *
+ * @param error_handler A handler to be called when the operation completes,
+ * to indicate whether or not an error has occurred. Copies will be made of
+ * the handler as required. The function signature of the handler must be:
+ * @code void error_handler(
+ * const asio::error& error // Result of operation
+ * ); @endcode
+ */
+ template <typename Error_Handler>
+ void assign(const protocol_type& protocol, const native_type& native_acceptor,
+ Error_Handler error_handler)
+ {
+ this->service.assign(this->implementation, protocol, native_acceptor,
+ error_handler);
+ }
+
+ /// Bind the acceptor to the given local endpoint.
+ /**
+ * This function binds the socket acceptor to the specified endpoint on the
+ * local machine.
+ *
+ * @param endpoint An endpoint on the local machine to which the socket
+ * acceptor will be bound.
+ *
+ * @throws asio::error Thrown on failure.
+ *
+ * @par Example:
+ * @code
+ * asio::ip::tcp::acceptor acceptor(io_service);
+ * acceptor.open(asio::ip::tcp::v4());
+ * acceptor.bind(asio::ip::tcp::endpoint(12345));
+ * @endcode
+ */
+ void bind(const endpoint_type& endpoint)
+ {
+ this->service.bind(this->implementation, endpoint, throw_error());
+ }
+
+ /// Bind the acceptor to the given local endpoint.
+ /**
+ * This function binds the socket acceptor to the specified endpoint on the
+ * local machine.
+ *
+ * @param endpoint An endpoint on the local machine to which the socket
+ * acceptor will be bound.
+ *
+ * @param error_handler A handler to be called when the operation completes,
+ * to indicate whether or not an error has occurred. Copies will be made of
+ * the handler as required. The function signature of the handler must be:
+ * @code void error_handler(
+ * const asio::error& error // Result of operation
+ * ); @endcode
+ *
+ * @par Example:
+ * @code
+ * asio::ip::tcp::acceptor acceptor(io_service);
+ * acceptor.open(asio::ip::tcp::v4());
+ * asio::error error;
+ * acceptor.bind(asio::ip::tcp::endpoint(12345),
+ * asio::assign_error(error));
+ * if (error)
+ * {
+ * // An error occurred.
+ * }
+ * @endcode
+ */
+ template <typename Error_Handler>
+ void bind(const endpoint_type& endpoint, Error_Handler error_handler)
+ {
+ this->service.bind(this->implementation, endpoint, error_handler);
+ }
+
+ /// Place the acceptor into the state where it will listen for new
+ /// connections.
+ /**
+ * This function puts the socket acceptor into the state where it may accept
+ * new connections.
+ *
+ * @param backlog The maximum length of the queue of pending connections.
+ */
+ void listen(int backlog = socket_base::max_connections)
+ {
+ this->service.listen(this->implementation, backlog, throw_error());
+ }
+
+ /// Place the acceptor into the state where it will listen for new
+ /// connections.
+ /**
+ * This function puts the socket acceptor into the state where it may accept
+ * new connections.
+ *
+ * @param backlog The maximum length of the queue of pending connections.
+ *
+ * @param error_handler A handler to be called when the operation completes,
+ * to indicate whether or not an error has occurred. Copies will be made of
+ * the handler as required. The function signature of the handler must be:
+ * @code void error_handler(
+ * const asio::error& error // Result of operation
+ * ); @endcode
+ *
+ * @par Example:
+ * @code
+ * asio::ip::tcp::acceptor acceptor(io_service);
+ * ...
+ * asio::error error;
+ * acceptor.listen(asio::socket_base::max_connections,
+ * asio::assign_error(error));
+ * if (error)
+ * {
+ * // An error occurred.
+ * }
+ * @endcode
+ */
+ template <typename Error_Handler>
+ void listen(int backlog, Error_Handler error_handler)
+ {
+ this->service.listen(this->implementation, backlog, error_handler);
+ }
+
+ /// Close the acceptor.
+ /**
+ * This function is used to close the acceptor. Any asynchronous accept
+ * operations will be cancelled immediately.
+ *
+ * A subsequent call to open() is required before the acceptor can again be
+ * used to again perform socket accept operations.
+ *
+ * @throws asio::error Thrown on failure.
+ */
+ void close()
+ {
+ this->service.close(this->implementation, throw_error());
+ }
+
+ /// Close the acceptor.
+ /**
+ * This function is used to close the acceptor. Any asynchronous accept
+ * operations will be cancelled immediately.
+ *
+ * A subsequent call to open() is required before the acceptor can again be
+ * used to again perform socket accept operations.
+ *
+ * @param error_handler A handler to be called when the operation completes,
+ * to indicate whether or not an error has occurred. Copies will be made of
+ * the handler as required. The function signature of the handler must be:
+ * @code void error_handler(
+ * const asio::error& error // Result of operation
+ * ); @endcode
+ *
+ * @par Example:
+ * @code
+ * asio::ip::tcp::acceptor acceptor(io_service);
+ * ...
+ * asio::error error;
+ * acceptor.close(asio::assign_error(error));
+ * if (error)
+ * {
+ * // An error occurred.
+ * }
+ * @endcode
+ */
+ template <typename Error_Handler>
+ void close(Error_Handler error_handler)
+ {
+ this->service.close(this->implementation, error_handler);
+ }
+
+ /// Get the native acceptor representation.
+ /**
+ * This function may be used to obtain the underlying representation of the
+ * acceptor. This is intended to allow access to native acceptor functionality
+ * that is not otherwise provided.
+ */
+ native_type native()
+ {
+ return this->service.native(this->implementation);
+ }
+
+ /// Cancel all asynchronous operations associated with the acceptor.
+ /**
+ * This function causes all outstanding asynchronous connect, send and receive
+ * operations to finish immediately, and the handlers for cancelled operations
+ * will be passed the asio::error::operation_aborted error.
+ *
+ * @throws asio::error Thrown on failure.
+ */
+ void cancel()
+ {
+ this->service.cancel(this->implementation, throw_error());
+ }
+
+ /// Cancel all asynchronous operations associated with the acceptor.
+ /**
+ * This function causes all outstanding asynchronous connect, send and receive
+ * operations to finish immediately, and the handlers for cancelled operations
+ * will be passed the asio::error::operation_aborted error.
+ *
+ * @param error_handler A handler to be called when the operation completes,
+ * to indicate whether or not an error has occurred. Copies will be made of
+ * the handler as required. The function signature of the handler must be:
+ * @code void error_handler(
+ * const asio::error& error // Result of operation
+ * ); @endcode
+ */
+ template <typename Error_Handler>
+ void cancel(Error_Handler error_handler)
+ {
+ this->service.cancel(this->implementation, error_handler);
+ }
+
+ /// Set an option on the acceptor.
+ /**
+ * This function is used to set an option on the acceptor.
+ *
+ * @param option The new option value to be set on the acceptor.
+ *
+ * @throws asio::error Thrown on failure.
+ *
+ * @sa Socket_Option @n
+ * asio::socket_base::reuse_address
+ * asio::socket_base::enable_connection_aborted
+ *
+ * @par Example:
+ * Setting the SOL_SOCKET/SO_REUSEADDR option:
+ * @code
+ * asio::ip::tcp::acceptor acceptor(io_service);
+ * ...
+ * asio::ip::tcp::acceptor::reuse_address option(true);
+ * acceptor.set_option(option);
+ * @endcode
+ */
+ template <typename Option>
+ void set_option(const Option& option)
+ {
+ this->service.set_option(this->implementation, option, throw_error());
+ }
+
+ /// Set an option on the acceptor.
+ /**
+ * This function is used to set an option on the acceptor.
+ *
+ * @param option The new option value to be set on the acceptor.
+ *
+ * @param error_handler A handler to be called when the operation completes,
+ * to indicate whether or not an error has occurred. Copies will be made of
+ * the handler as required. The function signature of the handler must be:
+ * @code void error_handler(
+ * const asio::error& error // Result of operation
+ * ); @endcode
+ *
+ * @sa Socket_Option @n
+ * asio::socket_base::reuse_address
+ * asio::socket_base::enable_connection_aborted
+ *
+ * @par Example:
+ * Setting the SOL_SOCKET/SO_REUSEADDR option:
+ * @code
+ * asio::ip::tcp::acceptor acceptor(io_service);
+ * ...
+ * asio::ip::tcp::acceptor::reuse_address option(true);
+ * asio::error error;
+ * acceptor.set_option(option, asio::assign_error(error));
+ * if (error)
+ * {
+ * // An error occurred.
+ * }
+ * @endcode
+ */
+ template <typename Option, typename Error_Handler>
+ void set_option(const Option& option, Error_Handler error_handler)
+ {
+ this->service.set_option(this->implementation, option, error_handler);
+ }
+
+ /// Get an option from the acceptor.
+ /**
+ * This function is used to get the current value of an option on the
+ * acceptor.
+ *
+ * @param option The option value to be obtained from the acceptor.
+ *
+ * @throws asio::error Thrown on failure.
+ *
+ * @sa Socket_Option @n
+ * asio::socket_base::reuse_address
+ *
+ * @par Example:
+ * Getting the value of the SOL_SOCKET/SO_REUSEADDR option:
+ * @code
+ * asio::ip::tcp::acceptor acceptor(io_service);
+ * ...
+ * asio::ip::tcp::acceptor::reuse_address option;
+ * acceptor.get_option(option);
+ * bool is_set = option.get();
+ * @endcode
+ */
+ template <typename Option>
+ void get_option(Option& option)
+ {
+ this->service.get_option(this->implementation, option, throw_error());
+ }
+
+ /// Get an option from the acceptor.
+ /**
+ * This function is used to get the current value of an option on the
+ * acceptor.
+ *
+ * @param option The option value to be obtained from the acceptor.
+ *
+ * @param error_handler A handler to be called when the operation completes,
+ * to indicate whether or not an error has occurred. Copies will be made of
+ * the handler as required. The function signature of the handler must be:
+ * @code void error_handler(
+ * const asio::error& error // Result of operation
+ * ); @endcode
+ *
+ * @sa Socket_Option @n
+ * asio::socket_base::reuse_address
+ *
+ * @par Example:
+ * Getting the value of the SOL_SOCKET/SO_REUSEADDR option:
+ * @code
+ * asio::ip::tcp::acceptor acceptor(io_service);
+ * ...
+ * asio::ip::tcp::acceptor::reuse_address option;
+ * asio::error error;
+ * acceptor.get_option(option, asio::assign_error(error));
+ * if (error)
+ * {
+ * // An error occurred.
+ * }
+ * bool is_set = option.get();
+ * @endcode
+ */
+ template <typename Option, typename Error_Handler>
+ void get_option(Option& option, Error_Handler error_handler)
+ {
+ this->service.get_option(this->implementation, option, error_handler);
+ }
+
+ /// Get the local endpoint of the acceptor.
+ /**
+ * This function is used to obtain the locally bound endpoint of the acceptor.
+ *
+ * @returns An object that represents the local endpoint of the acceptor.
+ *
+ * @throws asio::error Thrown on failure.
+ *
+ * @par Example:
+ * @code
+ * asio::ip::tcp::acceptor acceptor(io_service);
+ * ...
+ * asio::ip::tcp::endpoint endpoint = acceptor.local_endpoint();
+ * @endcode
+ */
+ endpoint_type local_endpoint() const
+ {
+ return this->service.local_endpoint(this->implementation, throw_error());
+ }
+
+ /// Get the local endpoint of the acceptor.
+ /**
+ * This function is used to obtain the locally bound endpoint of the acceptor.
+ *
+ * @param error_handler A handler to be called when the operation completes,
+ * to indicate whether or not an error has occurred. Copies will be made of
+ * the handler as required. The function signature of the handler must be:
+ * @code void error_handler(
+ * const asio::error& error // Result of operation
+ * ); @endcode
+ *
+ * @returns An object that represents the local endpoint of the acceptor.
+ * Returns a default-constructed endpoint object if an error occurred and the
+ * error handler did not throw an exception.
+ *
+ * @par Example:
+ * @code
+ * asio::ip::tcp::acceptor acceptor(io_service);
+ * ...
+ * asio::error error;
+ * asio::ip::tcp::endpoint endpoint
+ * = acceptor.local_endpoint(asio::assign_error(error));
+ * if (error)
+ * {
+ * // An error occurred.
+ * }
+ * @endcode
+ */
+ template <typename Error_Handler>
+ endpoint_type local_endpoint(Error_Handler error_handler) const
+ {
+ return this->service.local_endpoint(this->implementation, error_handler);
+ }
+
+ /// Accept a new connection.
+ /**
+ * This function is used to accept a new connection from a peer into the
+ * given socket. The function call will block until a new connection has been
+ * accepted successfully or an error occurs.
+ *
+ * @param peer The socket into which the new connection will be accepted.
+ *
+ * @throws asio::error Thrown on failure.
+ *
+ * @par Example:
+ * @code
+ * asio::ip::tcp::acceptor acceptor(io_service);
+ * ...
+ * asio::ip::tcp::socket socket(io_service);
+ * acceptor.accept(socket);
+ * @endcode
+ */
+ template <typename Socket_Service>
+ void accept(basic_socket<protocol_type, Socket_Service>& peer)
+ {
+ this->service.accept(this->implementation, peer, throw_error());
+ }
+
+ /// Accept a new connection.
+ /**
+ * This function is used to accept a new connection from a peer into the
+ * given socket. The function call will block until a new connection has been
+ * accepted successfully or an error occurs.
+ *
+ * @param peer The socket into which the new connection will be accepted.
+ *
+ * @param error_handler A handler to be called when the operation completes,
+ * to indicate whether or not an error has occurred. Copies will be made of
+ * the handler as required. The function signature of the handler must be:
+ * @code void error_handler(
+ * const asio::error& error // Result of operation
+ * ); @endcode
+ *
+ * @par Example:
+ * @code
+ * asio::ip::tcp::acceptor acceptor(io_service);
+ * ...
+ * asio::ip::tcp::soocket socket(io_service);
+ * asio::error error;
+ * acceptor.accept(socket, asio::assign_error(error));
+ * if (error)
+ * {
+ * // An error occurred.
+ * }
+ * @endcode
+ */
+ template <typename Socket_Service, typename Error_Handler>
+ void accept(basic_socket<protocol_type, Socket_Service>& peer,
+ Error_Handler error_handler)
+ {
+ this->service.accept(this->implementation, peer, error_handler);
+ }
+
+ /// Start an asynchronous accept.
+ /**
+ * This function is used to asynchronously accept a new connection into a
+ * socket. The function call always returns immediately.
+ *
+ * @param peer The socket into which the new connection will be accepted.
+ * Ownership of the peer object is retained by the caller, which must
+ * guarantee that it is valid until the handler is called.
+ *
+ * @param handler The handler to be called when the accept operation
+ * completes. Copies will be made of the handler as required. The function
+ * signature of the handler must be:
+ * @code void handler(
+ * const asio::error& error // Result of operation
+ * ); @endcode
+ * Regardless of whether the asynchronous operation completes immediately or
+ * not, the handler will not be invoked from within this function. Invocation
+ * of the handler will be performed in a manner equivalent to using
+ * asio::io_service::post().
+ *
+ * @par Example:
+ * @code
+ * void accept_handler(const asio::error& error)
+ * {
+ * if (!error)
+ * {
+ * // Accept succeeded.
+ * }
+ * }
+ *
+ * ...
+ *
+ * asio::ip::tcp::acceptor acceptor(io_service);
+ * ...
+ * asio::ip::tcp::socket socket(io_service);
+ * acceptor.async_accept(socket, accept_handler);
+ * @endcode
+ */
+ template <typename Socket_Service, typename Handler>
+ void async_accept(basic_socket<protocol_type, Socket_Service>& peer,
+ Handler handler)
+ {
+ this->service.async_accept(this->implementation, peer, handler);
+ }
+
+ /// Accept a new connection and obtain the endpoint of the peer
+ /**
+ * This function is used to accept a new connection from a peer into the
+ * given socket, and additionally provide the endpoint of the remote peer.
+ * The function call will block until a new connection has been accepted
+ * successfully or an error occurs.
+ *
+ * @param peer The socket into which the new connection will be accepted.
+ *
+ * @param peer_endpoint An endpoint object which will receive the endpoint of
+ * the remote peer.
+ *
+ * @throws asio::error Thrown on failure.
+ *
+ * @par Example:
+ * @code
+ * asio::ip::tcp::acceptor acceptor(io_service);
+ * ...
+ * asio::ip::tcp::socket socket(io_service);
+ * asio::ip::tcp::endpoint endpoint;
+ * acceptor.accept_endpoint(socket, endpoint);
+ * @endcode
+ */
+ template <typename Socket_Service>
+ void accept_endpoint(basic_socket<protocol_type, Socket_Service>& peer,
+ endpoint_type& peer_endpoint)
+ {
+ this->service.accept_endpoint(this->implementation, peer, peer_endpoint,
+ throw_error());
+ }
+
+ /// Accept a new connection and obtain the endpoint of the peer
+ /**
+ * This function is used to accept a new connection from a peer into the
+ * given socket, and additionally provide the endpoint of the remote peer.
+ * The function call will block until a new connection has been accepted
+ * successfully or an error occurs.
+ *
+ * @param peer The socket into which the new connection will be accepted.
+ *
+ * @param peer_endpoint An endpoint object which will receive the endpoint of
+ * the remote peer.
+ *
+ * @param error_handler A handler to be called when the operation completes,
+ * to indicate whether or not an error has occurred. Copies will be made of
+ * the handler as required. The function signature of the handler must be:
+ * @code void error_handler(
+ * const asio::error& error // Result of operation
+ * ); @endcode
+ *
+ * @par Example:
+ * @code
+ * asio::ip::tcp::acceptor acceptor(io_service);
+ * ...
+ * asio::ip::tcp::socket socket(io_service);
+ * asio::ip::tcp::endpoint endpoint;
+ * asio::error error;
+ * acceptor.accept_endpoint(socket, endpoint,
+ * asio::assign_error(error));
+ * if (error)
+ * {
+ * // An error occurred.
+ * }
+ * @endcode
+ */
+ template <typename Socket_Service, typename Error_Handler>
+ void accept_endpoint(basic_socket<protocol_type, Socket_Service>& peer,
+ endpoint_type& peer_endpoint, Error_Handler error_handler)
+ {
+ this->service.accept_endpoint(this->implementation, peer, peer_endpoint,
+ error_handler);
+ }
+
+ /// Start an asynchronous accept.
+ /**
+ * This function is used to asynchronously accept a new connection into a
+ * socket, and additionally obtain the endpoint of the remote peer. The
+ * function call always returns immediately.
+ *
+ * @param peer The socket into which the new connection will be accepted.
+ * Ownership of the peer object is retained by the caller, which must
+ * guarantee that it is valid until the handler is called.
+ *
+ * @param peer_endpoint An endpoint object into which the endpoint of the
+ * remote peer will be written. Ownership of the peer_endpoint object is
+ * retained by the caller, which must guarantee that it is valid until the
+ * handler is called.
+ *
+ * @param handler The handler to be called when the accept operation
+ * completes. Copies will be made of the handler as required. The function
+ * signature of the handler must be:
+ * @code void handler(
+ * const asio::error& error // Result of operation
+ * ); @endcode
+ * Regardless of whether the asynchronous operation completes immediately or
+ * not, the handler will not be invoked from within this function. Invocation
+ * of the handler will be performed in a manner equivalent to using
+ * asio::io_service::post().
+ */
+ template <typename Socket_Service, typename Handler>
+ void async_accept_endpoint(basic_socket<protocol_type, Socket_Service>& peer,
+ endpoint_type& peer_endpoint, Handler handler)
+ {
+ this->service.async_accept_endpoint(this->implementation, peer,
+ peer_endpoint, handler);
+ }
+};
+
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_BASIC_SOCKET_ACCEPTOR_HPP
diff --git a/library/include/libtorrent/asio/basic_socket_iostream.hpp b/library/include/libtorrent/asio/basic_socket_iostream.hpp
new file mode 100644
index 000000000..052b6e603
--- /dev/null
+++ b/library/include/libtorrent/asio/basic_socket_iostream.hpp
@@ -0,0 +1,176 @@
+//
+// basic_socket_iostream.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_BASIC_SOCKET_IOSTREAM_HPP
+#define ASIO_BASIC_SOCKET_IOSTREAM_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/detail/push_options.hpp"
+#include <boost/preprocessor/arithmetic/inc.hpp>
+#include <boost/preprocessor/repetition/enum_binary_params.hpp>
+#include <boost/preprocessor/repetition/enum_params.hpp>
+#include <boost/preprocessor/repetition/repeat_from_to.hpp>
+#include <boost/utility/base_from_member.hpp>
+#include "asio/detail/pop_options.hpp"
+
+#include "asio/basic_socket_streambuf.hpp"
+#include "asio/stream_socket_service.hpp"
+
+#if !defined(ASIO_SOCKET_IOSTREAM_MAX_ARITY)
+#define ASIO_SOCKET_IOSTREAM_MAX_ARITY 5
+#endif // !defined(ASIO_SOCKET_IOSTREAM_MAX_ARITY)
+
+// A macro that should expand to:
+// template < typename T1, ..., typename Tn >
+// explicit basic_socket_iostream( T1 x1, ..., Tn xn )
+// : basic_iostream<char>(&this->boost::base_from_member<
+// basic_socket_streambuf<Protocol, Service> >::member)
+// {
+// try
+// {
+// rdbuf()->connect ( x1, ..., xn );
+// }
+// catch (asio::error&)
+// {
+// this->setstate(std::ios_base::failbit);
+// if (this->exceptions() & std::ios_base::failbit)
+// throw;
+// }
+// }
+// This macro should only persist within this file.
+
+#define ASIO_PRIVATE_CTR_DEF( z, n, data ) \
+ template < BOOST_PP_ENUM_PARAMS(n, typename T) > \
+ explicit basic_socket_iostream( BOOST_PP_ENUM_BINARY_PARAMS(n, T, x) ) \
+ : std::basic_iostream<char>(&this->boost::base_from_member< \
+ basic_socket_streambuf<Protocol, Service> >::member) \
+ { \
+ try \
+ { \
+ rdbuf()->connect( BOOST_PP_ENUM_PARAMS(n, x) ); \
+ } \
+ catch (asio::error&) \
+ { \
+ this->setstate(std::ios_base::failbit); \
+ if (this->exceptions() & std::ios_base::failbit) \
+ throw; \
+ } \
+ } \
+ /**/
+
+// A macro that should expand to:
+// template < typename T1, ..., typename Tn >
+// void connect( T1 x1, ..., Tn xn )
+// {
+// try
+// {
+// rdbuf()->connect ( x1, ..., xn );
+// }
+// catch (asio::error&)
+// {
+// this->setstate(std::ios_base::failbit);
+// if (this->exceptions() & std::ios_base::failbit)
+// throw;
+// }
+// }
+// This macro should only persist within this file.
+
+#define ASIO_PRIVATE_CONNECT_DEF( z, n, data ) \
+ template < BOOST_PP_ENUM_PARAMS(n, typename T) > \
+ void connect( BOOST_PP_ENUM_BINARY_PARAMS(n, T, x) ) \
+ { \
+ try \
+ { \
+ rdbuf()->connect( BOOST_PP_ENUM_PARAMS(n, x) ); \
+ } \
+ catch (asio::error&) \
+ { \
+ this->setstate(std::ios_base::failbit); \
+ if (this->exceptions() & std::ios_base::failbit) \
+ throw; \
+ } \
+ } \
+ /**/
+
+namespace asio {
+
+/// Iostream interface for a socket.
+template <typename Protocol,
+ typename Service = stream_socket_service<Protocol> >
+class basic_socket_iostream
+ : public boost::base_from_member<basic_socket_streambuf<Protocol, Service> >,
+ public std::basic_iostream<char>
+{
+public:
+ /// Construct a basic_socket_iostream without establishing a connection.
+ basic_socket_iostream()
+ : std::basic_iostream<char>(&this->boost::base_from_member<
+ basic_socket_streambuf<Protocol, Service> >::member)
+ {
+ }
+
+#if defined(GENERATING_DOCUMENTATION)
+ /// Establish a connection to an endpoint corresponding to a resolver query.
+ /**
+ * This constructor automatically establishes a connection based on the
+ * supplied resolver query parameters. The arguments are used to construct
+ * a resolver query object.
+ */
+ template <typename T1, ..., typename TN>
+ explicit basic_socket_iostream(T1 t1, ..., TN tn);
+#else
+ BOOST_PP_REPEAT_FROM_TO(
+ 1, BOOST_PP_INC(ASIO_SOCKET_IOSTREAM_MAX_ARITY),
+ ASIO_PRIVATE_CTR_DEF, _ )
+#endif
+
+#if defined(GENERATING_DOCUMENTATION)
+ /// Establish a connection to an endpoint corresponding to a resolver query.
+ /**
+ * This function automatically establishes a connection based on the supplied
+ * resolver query parameters. The arguments are used to construct a resolver
+ * query object.
+ */
+ template <typename T1, ..., typename TN>
+ void connect(T1 t1, ..., TN tn);
+#else
+ BOOST_PP_REPEAT_FROM_TO(
+ 1, BOOST_PP_INC(ASIO_SOCKET_IOSTREAM_MAX_ARITY),
+ ASIO_PRIVATE_CONNECT_DEF, _ )
+#endif
+
+ /// Close the connection.
+ void close()
+ {
+ rdbuf()->close();
+ }
+
+ /// Return a pointer to the underlying streambuf.
+ basic_socket_streambuf<Protocol, Service>* rdbuf() const
+ {
+ return const_cast<basic_socket_streambuf<Protocol, Service>*>(
+ &this->boost::base_from_member<
+ basic_socket_streambuf<Protocol, Service> >::member);
+ }
+};
+
+} // namespace asio
+
+#undef ASIO_PRIVATE_CTR_DEF
+#undef ASIO_PRIVATE_CONNECT_DEF
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_BASIC_SOCKET_IOSTREAM_HPP
diff --git a/library/include/libtorrent/asio/basic_socket_streambuf.hpp b/library/include/libtorrent/asio/basic_socket_streambuf.hpp
new file mode 100644
index 000000000..c24e00140
--- /dev/null
+++ b/library/include/libtorrent/asio/basic_socket_streambuf.hpp
@@ -0,0 +1,280 @@
+//
+// basic_socket_streambuf.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_BASIC_SOCKET_STREAMBUF_HPP
+#define ASIO_BASIC_SOCKET_STREAMBUF_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/detail/push_options.hpp"
+#include <streambuf>
+#include <boost/array.hpp>
+#include <boost/preprocessor/arithmetic/inc.hpp>
+#include <boost/preprocessor/repetition/enum_binary_params.hpp>
+#include <boost/preprocessor/repetition/enum_params.hpp>
+#include <boost/preprocessor/repetition/repeat_from_to.hpp>
+#include <boost/utility/base_from_member.hpp>
+#include "asio/detail/pop_options.hpp"
+
+#include "asio/basic_socket.hpp"
+#include "asio/error_handler.hpp"
+#include "asio/io_service.hpp"
+#include "asio/stream_socket_service.hpp"
+
+#if !defined(ASIO_SOCKET_STREAMBUF_MAX_ARITY)
+#define ASIO_SOCKET_STREAMBUF_MAX_ARITY 5
+#endif // !defined(ASIO_SOCKET_STREAMBUF_MAX_ARITY)
+
+// A macro that should expand to:
+// template < typename T1, ..., typename Tn >
+// explicit basic_socket_streambuf( T1 x1, ..., Tn xn )
+// : basic_socket<Protocol, Service>(
+// boost::base_from_member<io_service>::member)
+// {
+// init_buffers();
+// typedef typename Protocol::resolver_query resolver_query;
+// resolver_query query( x1, ..., xn );
+// resolve_and_connect(query);
+// }
+// This macro should only persist within this file.
+
+#define ASIO_PRIVATE_CTR_DEF( z, n, data ) \
+ template < BOOST_PP_ENUM_PARAMS(n, typename T) > \
+ explicit basic_socket_streambuf( BOOST_PP_ENUM_BINARY_PARAMS(n, T, x) ) \
+ : basic_socket<Protocol, Service>( \
+ boost::base_from_member<io_service>::member) \
+ { \
+ init_buffers(); \
+ typedef typename Protocol::resolver_query resolver_query; \
+ resolver_query query( BOOST_PP_ENUM_PARAMS(n, x) ); \
+ resolve_and_connect(query); \
+ } \
+ /**/
+
+// A macro that should expand to:
+// template < typename T1, ..., typename Tn >
+// void connect( T1 x1, ..., Tn xn )
+// {
+// this->basic_socket<Protocol, Service>::close();
+// init_buffers();
+// typedef typename Protocol::resolver_query resolver_query;
+// resolver_query query( x1, ..., xn );
+// resolve_and_connect(query);
+// }
+// This macro should only persist within this file.
+
+#define ASIO_PRIVATE_CONNECT_DEF( z, n, data ) \
+ template < BOOST_PP_ENUM_PARAMS(n, typename T) > \
+ void connect( BOOST_PP_ENUM_BINARY_PARAMS(n, T, x) ) \
+ { \
+ this->basic_socket<Protocol, Service>::close(); \
+ init_buffers(); \
+ typedef typename Protocol::resolver_query resolver_query; \
+ resolver_query query( BOOST_PP_ENUM_PARAMS(n, x) ); \
+ resolve_and_connect(query); \
+ } \
+ /**/
+
+namespace asio {
+
+/// Iostream streambuf for a socket.
+template <typename Protocol,
+ typename Service = stream_socket_service<Protocol> >
+class basic_socket_streambuf
+ : public std::streambuf,
+ private boost::base_from_member<io_service>,
+ public basic_socket<Protocol, Service>
+{
+public:
+ /// The endpoint type.
+ typedef typename Protocol::endpoint endpoint_type;
+
+ /// Construct a basic_socket_streambuf without establishing a connection.
+ basic_socket_streambuf()
+ : basic_socket<Protocol, Service>(
+ boost::base_from_member<asio::io_service>::member)
+ {
+ init_buffers();
+ }
+
+ /// Establish a connection to the specified endpoint.
+ explicit basic_socket_streambuf(const endpoint_type& endpoint)
+ : basic_socket<Protocol, Service>(
+ boost::base_from_member<asio::io_service>::member)
+ {
+ init_buffers();
+ this->basic_socket<Protocol, Service>::connect(endpoint);
+ }
+
+#if defined(GENERATING_DOCUMENTATION)
+ /// Establish a connection to an endpoint corresponding to a resolver query.
+ /**
+ * This constructor automatically establishes a connection based on the
+ * supplied resolver query parameters. The arguments are used to construct
+ * a resolver query object.
+ */
+ template <typename T1, ..., typename TN>
+ explicit basic_socket_streambuf(T1 t1, ..., TN tn);
+#else
+ BOOST_PP_REPEAT_FROM_TO(
+ 1, BOOST_PP_INC(ASIO_SOCKET_STREAMBUF_MAX_ARITY),
+ ASIO_PRIVATE_CTR_DEF, _ )
+#endif
+
+ /// Destructor flushes buffered data.
+ ~basic_socket_streambuf()
+ {
+ sync();
+ }
+
+ /// Establish a connection to the specified endpoint.
+ void connect(const endpoint_type& endpoint)
+ {
+ this->basic_socket<Protocol, Service>::close();
+ init_buffers();
+ this->basic_socket<Protocol, Service>::connect(endpoint);
+ }
+
+#if defined(GENERATING_DOCUMENTATION)
+ /// Establish a connection to an endpoint corresponding to a resolver query.
+ /**
+ * This function automatically establishes a connection based on the supplied
+ * resolver query parameters. The arguments are used to construct a resolver
+ * query object.
+ */
+ template <typename T1, ..., typename TN>
+ void connect(T1 t1, ..., TN tn);
+#else
+ BOOST_PP_REPEAT_FROM_TO(
+ 1, BOOST_PP_INC(ASIO_SOCKET_STREAMBUF_MAX_ARITY),
+ ASIO_PRIVATE_CONNECT_DEF, _ )
+#endif
+
+ /// Close the connection.
+ void close()
+ {
+ sync();
+ this->basic_socket<Protocol, Service>::close();
+ init_buffers();
+ }
+
+protected:
+ int_type underflow()
+ {
+ if (gptr() == egptr())
+ {
+ asio::error error;
+ std::size_t bytes_transferred = this->service.receive(
+ this->implementation,
+ asio::buffer(asio::buffer(get_buffer_) + putback_max),
+ 0, asio::assign_error(error));
+ if (error)
+ {
+ if (error != asio::error::eof)
+ throw error;
+ return traits_type::eof();
+ }
+ setg(get_buffer_.begin(), get_buffer_.begin() + putback_max,
+ get_buffer_.begin() + putback_max + bytes_transferred);
+ return traits_type::to_int_type(*gptr());
+ }
+ else
+ {
+ return traits_type::eof();
+ }
+ }
+
+ int_type overflow(int_type c)
+ {
+ if (!traits_type::eq_int_type(c, traits_type::eof()))
+ {
+ if (pptr() == epptr())
+ {
+ asio::const_buffer buffer =
+ asio::buffer(pbase(), pptr() - pbase());
+ while (asio::buffer_size(buffer) > 0)
+ {
+ std::size_t bytes_transferred = this->service.send(
+ this->implementation, asio::buffer(buffer),
+ 0, asio::throw_error());
+ buffer = buffer + bytes_transferred;
+ }
+ setp(put_buffer_.begin(), put_buffer_.end());
+ }
+
+ *pptr() = traits_type::to_char_type(c);
+ pbump(1);
+ return c;
+ }
+
+ return traits_type::not_eof(c);
+ }
+
+ int sync()
+ {
+ asio::const_buffer buffer =
+ asio::buffer(pbase(), pptr() - pbase());
+ while (asio::buffer_size(buffer) > 0)
+ {
+ std::size_t bytes_transferred = this->service.send(
+ this->implementation, asio::buffer(buffer),
+ 0, asio::throw_error());
+ buffer = buffer + bytes_transferred;
+ }
+ setp(put_buffer_.begin(), put_buffer_.end());
+ return 0;
+ }
+
+private:
+ void init_buffers()
+ {
+ setg(get_buffer_.begin(),
+ get_buffer_.begin() + putback_max,
+ get_buffer_.begin() + putback_max);
+ setp(put_buffer_.begin(), put_buffer_.end());
+ }
+
+ void resolve_and_connect(const typename Protocol::resolver_query& query)
+ {
+ typedef typename Protocol::resolver resolver_type;
+ typedef typename Protocol::resolver_iterator iterator_type;
+ resolver_type resolver(
+ boost::base_from_member<asio::io_service>::member);
+ iterator_type iterator = resolver.resolve(query);
+ asio::error error(asio::error::host_not_found);
+ while (error && iterator != iterator_type())
+ {
+ this->basic_socket<Protocol, Service>::close();
+ this->basic_socket<Protocol, Service>::connect(
+ *iterator, asio::assign_error(error));
+ ++iterator;
+ }
+ if (error)
+ throw error;
+ }
+
+ enum { putback_max = 8 };
+ enum { buffer_size = 512 };
+ boost::array<char, buffer_size> get_buffer_;
+ boost::array<char, buffer_size> put_buffer_;
+};
+
+} // namespace asio
+
+#undef ASIO_PRIVATE_CTR_DEF
+#undef ASIO_PRIVATE_CONNECT_DEF
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_BASIC_SOCKET_STREAMBUF_HPP
diff --git a/library/include/libtorrent/asio/basic_stream_socket.hpp b/library/include/libtorrent/asio/basic_stream_socket.hpp
new file mode 100644
index 000000000..c2cf3c3c7
--- /dev/null
+++ b/library/include/libtorrent/asio/basic_stream_socket.hpp
@@ -0,0 +1,816 @@
+//
+// basic_stream_socket.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_BASIC_STREAM_SOCKET_HPP
+#define ASIO_BASIC_STREAM_SOCKET_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/detail/push_options.hpp"
+#include <cstddef>
+#include <boost/config.hpp>
+#include "asio/detail/pop_options.hpp"
+
+#include "asio/basic_socket.hpp"
+#include "asio/error_handler.hpp"
+#include "asio/stream_socket_service.hpp"
+
+namespace asio {
+
+/// Provides stream-oriented socket functionality.
+/**
+ * The basic_stream_socket class template provides asynchronous and blocking
+ * stream-oriented socket functionality.
+ *
+ * @par Thread Safety:
+ * @e Distinct @e objects: Safe.@n
+ * @e Shared @e objects: Unsafe.
+ *
+ * @par Concepts:
+ * Async_Read_Stream, Async_Write_Stream, Error_Source, IO_Object, Stream,
+ * Sync_Read_Stream, Sync_Write_Stream.
+ */
+template <typename Protocol,
+ typename Service = stream_socket_service<Protocol> >
+class basic_stream_socket
+ : public basic_socket<Protocol, Service>
+{
+public:
+ /// The native representation of a socket.
+ typedef typename Service::native_type native_type;
+
+ /// The protocol type.
+ typedef Protocol protocol_type;
+
+ /// The endpoint type.
+ typedef typename Protocol::endpoint endpoint_type;
+
+ /// Construct a basic_stream_socket without opening it.
+ /**
+ * This constructor creates a stream socket without opening it. The socket
+ * needs to be opened and then connected or accepted before data can be sent
+ * or received on it.
+ *
+ * @param io_service The io_service object that the stream socket will use to
+ * dispatch handlers for any asynchronous operations performed on the socket.
+ */
+ explicit basic_stream_socket(asio::io_service& io_service)
+ : basic_socket<Protocol, Service>(io_service)
+ {
+ }
+
+ /// Construct and open a basic_stream_socket.
+ /**
+ * This constructor creates and opens a stream socket. The socket needs to be
+ * connected or accepted before data can be sent or received on it.
+ *
+ * @param io_service The io_service object that the stream socket will use to
+ * dispatch handlers for any asynchronous operations performed on the socket.
+ *
+ * @param protocol An object specifying protocol parameters to be used.
+ *
+ * @throws asio::error Thrown on failure.
+ */
+ basic_stream_socket(asio::io_service& io_service,
+ const protocol_type& protocol)
+ : basic_socket<Protocol, Service>(io_service, protocol)
+ {
+ }
+
+ /// Construct a basic_stream_socket, opening it and binding it to the given
+ /// local endpoint.
+ /**
+ * This constructor creates a stream socket and automatically opens it bound
+ * to the specified endpoint on the local machine. The protocol used is the
+ * protocol associated with the given endpoint.
+ *
+ * @param io_service The io_service object that the stream socket will use to
+ * dispatch handlers for any asynchronous operations performed on the socket.
+ *
+ * @param endpoint An endpoint on the local machine to which the stream
+ * socket will be bound.
+ *
+ * @throws asio::error Thrown on failure.
+ */
+ basic_stream_socket(asio::io_service& io_service,
+ const endpoint_type& endpoint)
+ : basic_socket<Protocol, Service>(io_service, endpoint)
+ {
+ }
+
+ /// Construct a basic_stream_socket on an existing native socket.
+ /**
+ * This constructor creates a stream socket object to hold an existing native
+ * socket.
+ *
+ * @param io_service The io_service object that the stream socket will use to
+ * dispatch handlers for any asynchronous operations performed on the socket.
+ *
+ * @param protocol An object specifying protocol parameters to be used.
+ *
+ * @param native_socket The new underlying socket implementation.
+ *
+ * @throws asio::error Thrown on failure.
+ */
+ basic_stream_socket(asio::io_service& io_service,
+ const protocol_type& protocol, const native_type& native_socket)
+ : basic_socket<Protocol, Service>(io_service, protocol, native_socket)
+ {
+ }
+
+ /// Send some data on the socket.
+ /**
+ * This function is used to send data on the stream socket. The function
+ * call will block until one or more bytes of the data has been sent
+ * successfully, or an until error occurs.
+ *
+ * @param buffers One or more data buffers to be sent on the socket.
+ *
+ * @returns The number of bytes sent.
+ *
+ * @throws asio::error Thrown on failure.
+ *
+ * @note The send operation may not transmit all of the data to the peer.
+ * Consider using the @ref write function if you need to ensure that all data
+ * is written before the blocking operation completes.
+ *
+ * @par Example:
+ * To send a single data buffer use the @ref buffer function as follows:
+ * @code
+ * socket.send(asio::buffer(data, size));
+ * @endcode
+ * See the @ref buffer documentation for information on sending multiple
+ * buffers in one go, and how to use it with arrays, boost::array or
+ * std::vector.
+ */
+ template <typename Const_Buffers>
+ std::size_t send(const Const_Buffers& buffers)
+ {
+ return this->service.send(this->implementation, buffers, 0, throw_error());
+ }
+
+ /// Send some data on the socket.
+ /**
+ * This function is used to send data on the stream socket. The function
+ * call will block until one or more bytes of the data has been sent
+ * successfully, or an until error occurs.
+ *
+ * @param buffers One or more data buffers to be sent on the socket.
+ *
+ * @param flags Flags specifying how the send call is to be made.
+ *
+ * @returns The number of bytes sent.
+ *
+ * @throws asio::error Thrown on failure.
+ *
+ * @note The send operation may not transmit all of the data to the peer.
+ * Consider using the @ref write function if you need to ensure that all data
+ * is written before the blocking operation completes.
+ *
+ * @par Example:
+ * To send a single data buffer use the @ref buffer function as follows:
+ * @code
+ * socket.send(asio::buffer(data, size), 0);
+ * @endcode
+ * See the @ref buffer documentation for information on sending multiple
+ * buffers in one go, and how to use it with arrays, boost::array or
+ * std::vector.
+ */
+ template <typename Const_Buffers>
+ std::size_t send(const Const_Buffers& buffers,
+ socket_base::message_flags flags)
+ {
+ return this->service.send(this->implementation, buffers, flags,
+ throw_error());
+ }
+
+ /// Send some data on the socket.
+ /**
+ * This function is used to send data on the stream socket. The function
+ * call will block until one or more bytes of the data has been sent
+ * successfully, or an until error occurs.
+ *
+ * @param buffers One or more data buffers to be sent on the socket.
+ *
+ * @param flags Flags specifying how the send call is to be made.
+ *
+ * @param error_handler A handler to be called when the operation completes,
+ * to indicate whether or not an error has occurred. Copies will be made of
+ * the handler as required. The function signature of the handler must be:
+ * @code void error_handler(
+ * const asio::error& error // Result of operation.
+ * ); @endcode
+ *
+ * @returns The number of bytes sent. Returns 0 if an error occurred and the
+ * error handler did not throw an exception.
+ *
+ * @note The send operation may not transmit all of the data to the peer.
+ * Consider using the @ref write function if you need to ensure that all data
+ * is written before the blocking operation completes.
+ */
+ template <typename Const_Buffers, typename Error_Handler>
+ std::size_t send(const Const_Buffers& buffers,
+ socket_base::message_flags flags,
+ Error_Handler error_handler)
+ {
+ return this->service.send(this->implementation, buffers, flags,
+ error_handler);
+ }
+
+ /// Start an asynchronous send.
+ /**
+ * This function is used to asynchronously send data on the stream socket.
+ * The function call always returns immediately.
+ *
+ * @param buffers One or more data buffers to be sent on the socket. Although
+ * the buffers object may be copied as necessary, ownership of the underlying
+ * memory blocks is retained by the caller, which must guarantee that they
+ * remain valid until the handler is called.
+ *
+ * @param handler The handler to be called when the send operation completes.
+ * Copies will be made of the handler as required. The function signature of
+ * the handler must be:
+ * @code void handler(
+ * const asio::error& error, // Result of operation.
+ * std::size_t bytes_transferred // Number of bytes sent.
+ * ); @endcode
+ * Regardless of whether the asynchronous operation completes immediately or
+ * not, the handler will not be invoked from within this function. Invocation
+ * of the handler will be performed in a manner equivalent to using
+ * asio::io_service::post().
+ *
+ * @note The send operation may not transmit all of the data to the peer.
+ * Consider using the @ref async_write function if you need to ensure that all
+ * data is written before the asynchronous operation completes.
+ *
+ * @par Example:
+ * To send a single data buffer use the @ref buffer function as follows:
+ * @code
+ * socket.async_send(asio::buffer(data, size), handler);
+ * @endcode
+ * See the @ref buffer documentation for information on sending multiple
+ * buffers in one go, and how to use it with arrays, boost::array or
+ * std::vector.
+ */
+ template <typename Const_Buffers, typename Handler>
+ void async_send(const Const_Buffers& buffers, Handler handler)
+ {
+ this->service.async_send(this->implementation, buffers, 0, handler);
+ }
+
+ /// Start an asynchronous send.
+ /**
+ * This function is used to asynchronously send data on the stream socket.
+ * The function call always returns immediately.
+ *
+ * @param buffers One or more data buffers to be sent on the socket. Although
+ * the buffers object may be copied as necessary, ownership of the underlying
+ * memory blocks is retained by the caller, which must guarantee that they
+ * remain valid until the handler is called.
+ *
+ * @param flags Flags specifying how the send call is to be made.
+ *
+ * @param handler The handler to be called when the send operation completes.
+ * Copies will be made of the handler as required. The function signature of
+ * the handler must be:
+ * @code void handler(
+ * const asio::error& error, // Result of operation.
+ * std::size_t bytes_transferred // Number of bytes sent.
+ * ); @endcode
+ * Regardless of whether the asynchronous operation completes immediately or
+ * not, the handler will not be invoked from within this function. Invocation
+ * of the handler will be performed in a manner equivalent to using
+ * asio::io_service::post().
+ *
+ * @note The send operation may not transmit all of the data to the peer.
+ * Consider using the @ref async_write function if you need to ensure that all
+ * data is written before the asynchronous operation completes.
+ *
+ * @par Example:
+ * To send a single data buffer use the @ref buffer function as follows:
+ * @code
+ * socket.async_send(asio::buffer(data, size), 0, handler);
+ * @endcode
+ * See the @ref buffer documentation for information on sending multiple
+ * buffers in one go, and how to use it with arrays, boost::array or
+ * std::vector.
+ */
+ template <typename Const_Buffers, typename Handler>
+ void async_send(const Const_Buffers& buffers,
+ socket_base::message_flags flags, Handler handler)
+ {
+ this->service.async_send(this->implementation, buffers, flags, handler);
+ }
+
+ /// Receive some data on the socket.
+ /**
+ * This function is used to receive data on the stream socket. The function
+ * call will block until one or more bytes of data has been received
+ * successfully, or until an error occurs.
+ *
+ * @param buffers One or more buffers into which the data will be received.
+ *
+ * @returns The number of bytes received.
+ *
+ * @throws asio::error Thrown on failure. An error code of
+ * asio::error::eof indicates that the connection was closed by the
+ * peer.
+ *
+ * @note The receive operation may not receive all of the requested number of
+ * bytes. Consider using the @ref read function if you need to ensure that the
+ * requested amount of data is read before the blocking operation completes.
+ *
+ * @par Example:
+ * To receive into a single data buffer use the @ref buffer function as
+ * follows:
+ * @code
+ * socket.receive(asio::buffer(data, size));
+ * @endcode
+ * See the @ref buffer documentation for information on receiving into
+ * multiple buffers in one go, and how to use it with arrays, boost::array or
+ * std::vector.
+ */
+ template <typename Mutable_Buffers>
+ std::size_t receive(const Mutable_Buffers& buffers)
+ {
+ return this->service.receive(this->implementation, buffers, 0,
+ throw_error());
+ }
+
+ /// Receive some data on the socket.
+ /**
+ * This function is used to receive data on the stream socket. The function
+ * call will block until one or more bytes of data has been received
+ * successfully, or until an error occurs.
+ *
+ * @param buffers One or more buffers into which the data will be received.
+ *
+ * @param flags Flags specifying how the receive call is to be made.
+ *
+ * @returns The number of bytes received.
+ *
+ * @throws asio::error Thrown on failure. An error code of
+ * asio::error::eof indicates that the connection was closed by the
+ * peer.
+ *
+ * @note The receive operation may not receive all of the requested number of
+ * bytes. Consider using the @ref read function if you need to ensure that the
+ * requested amount of data is read before the blocking operation completes.
+ *
+ * @par Example:
+ * To receive into a single data buffer use the @ref buffer function as
+ * follows:
+ * @code
+ * socket.receive(asio::buffer(data, size), 0);
+ * @endcode
+ * See the @ref buffer documentation for information on receiving into
+ * multiple buffers in one go, and how to use it with arrays, boost::array or
+ * std::vector.
+ */
+ template <typename Mutable_Buffers>
+ std::size_t receive(const Mutable_Buffers& buffers,
+ socket_base::message_flags flags)
+ {
+ return this->service.receive(this->implementation, buffers, flags,
+ throw_error());
+ }
+
+ /// Receive some data on a connected socket.
+ /**
+ * This function is used to receive data on the stream socket. The function
+ * call will block until one or more bytes of data has been received
+ * successfully, or until an error occurs.
+ *
+ * @param buffers One or more buffers into which the data will be received.
+ *
+ * @param flags Flags specifying how the receive call is to be made.
+ *
+ * @param error_handler A handler to be called when the operation completes,
+ * to indicate whether or not an error has occurred. Copies will be made of
+ * the handler as required. The function signature of the handler must be:
+ * @code void error_handler(
+ * const asio::error& error // Result of operation
+ * ); @endcode
+ *
+ * @returns The number of bytes received. Returns 0 if an error occurred and
+ * the error handler did not throw an exception.
+ *
+ * @note The receive operation may not receive all of the requested number of
+ * bytes. Consider using the @ref read function if you need to ensure that the
+ * requested amount of data is read before the blocking operation completes.
+ */
+ template <typename Mutable_Buffers, typename Error_Handler>
+ std::size_t receive(const Mutable_Buffers& buffers,
+ socket_base::message_flags flags, Error_Handler error_handler)
+ {
+ return this->service.receive(this->implementation, buffers, flags,
+ error_handler);
+ }
+
+ /// Start an asynchronous receive.
+ /**
+ * This function is used to asynchronously receive data from the stream
+ * socket. The function call always returns immediately.
+ *
+ * @param buffers One or more buffers into which the data will be received.
+ * Although the buffers object may be copied as necessary, ownership of the
+ * underlying memory blocks is retained by the caller, which must guarantee
+ * that they remain valid until the handler is called.
+ *
+ * @param handler The handler to be called when the receive operation
+ * completes. Copies will be made of the handler as required. The function
+ * signature of the handler must be:
+ * @code void handler(
+ * const asio::error& error, // Result of operation.
+ * std::size_t bytes_transferred // Number of bytes received.
+ * ); @endcode
+ * Regardless of whether the asynchronous operation completes immediately or
+ * not, the handler will not be invoked from within this function. Invocation
+ * of the handler will be performed in a manner equivalent to using
+ * asio::io_service::post().
+ *
+ * @note The receive operation may not receive all of the requested number of
+ * bytes. Consider using the @ref async_read function if you need to ensure
+ * that the requested amount of data is received before the asynchronous
+ * operation completes.
+ *
+ * @par Example:
+ * To receive into a single data buffer use the @ref buffer function as
+ * follows:
+ * @code
+ * socket.async_receive(asio::buffer(data, size), handler);
+ * @endcode
+ * See the @ref buffer documentation for information on receiving into
+ * multiple buffers in one go, and how to use it with arrays, boost::array or
+ * std::vector.
+ */
+ template <typename Mutable_Buffers, typename Handler>
+ void async_receive(const Mutable_Buffers& buffers, Handler handler)
+ {
+ this->service.async_receive(this->implementation, buffers, 0, handler);
+ }
+
+ /// Start an asynchronous receive.
+ /**
+ * This function is used to asynchronously receive data from the stream
+ * socket. The function call always returns immediately.
+ *
+ * @param buffers One or more buffers into which the data will be received.
+ * Although the buffers object may be copied as necessary, ownership of the
+ * underlying memory blocks is retained by the caller, which must guarantee
+ * that they remain valid until the handler is called.
+ *
+ * @param flags Flags specifying how the receive call is to be made.
+ *
+ * @param handler The handler to be called when the receive operation
+ * completes. Copies will be made of the handler as required. The function
+ * signature of the handler must be:
+ * @code void handler(
+ * const asio::error& error, // Result of operation.
+ * std::size_t bytes_transferred // Number of bytes received.
+ * ); @endcode
+ * Regardless of whether the asynchronous operation completes immediately or
+ * not, the handler will not be invoked from within this function. Invocation
+ * of the handler will be performed in a manner equivalent to using
+ * asio::io_service::post().
+ *
+ * @note The receive operation may not receive all of the requested number of
+ * bytes. Consider using the @ref async_read function if you need to ensure
+ * that the requested amount of data is received before the asynchronous
+ * operation completes.
+ *
+ * @par Example:
+ * To receive into a single data buffer use the @ref buffer function as
+ * follows:
+ * @code
+ * socket.async_receive(asio::buffer(data, size), 0, handler);
+ * @endcode
+ * See the @ref buffer documentation for information on receiving into
+ * multiple buffers in one go, and how to use it with arrays, boost::array or
+ * std::vector.
+ */
+ template <typename Mutable_Buffers, typename Handler>
+ void async_receive(const Mutable_Buffers& buffers,
+ socket_base::message_flags flags, Handler handler)
+ {
+ this->service.async_receive(this->implementation, buffers, flags, handler);
+ }
+
+ /// Write some data to the socket.
+ /**
+ * This function is used to write data to the stream socket. The function call
+ * will block until one or more bytes of the data has been written
+ * successfully, or until an error occurs.
+ *
+ * @param buffers One or more data buffers to be written to the socket.
+ *
+ * @returns The number of bytes written.
+ *
+ * @throws asio::error Thrown on failure. An error code of
+ * asio::error::eof indicates that the connection was closed by the
+ * peer.
+ *
+ * @note The write_some operation may not transmit all of the data to the
+ * peer. Consider using the @ref write function if you need to ensure that
+ * all data is written before the blocking operation completes.
+ *
+ * @par Example:
+ * To write a single data buffer use the @ref buffer function as follows:
+ * @code
+ * socket.write_some(asio::buffer(data, size));
+ * @endcode
+ * See the @ref buffer documentation for information on writing multiple
+ * buffers in one go, and how to use it with arrays, boost::array or
+ * std::vector.
+ */
+ template <typename Const_Buffers>
+ std::size_t write_some(const Const_Buffers& buffers)
+ {
+ return this->service.send(this->implementation, buffers, 0, throw_error());
+ }
+
+ /// Write some data to the socket.
+ /**
+ * This function is used to write data to the stream socket. The function call
+ * will block until one or more bytes of the data has been written
+ * successfully, or until an error occurs.
+ *
+ * @param buffers One or more data buffers to be written to the socket.
+ *
+ * @param error_handler A handler to be called when the operation completes,
+ * to indicate whether or not an error has occurred. Copies will be made of
+ * the handler as required. The function signature of the handler must be:
+ * @code void error_handler(
+ * const asio::error& error // Result of operation.
+ * ); @endcode
+ *
+ * @returns The number of bytes written. Returns 0 if an error occurred and
+ * the error handler did not throw an exception.
+ *
+ * @note The write_some operation may not transmit all of the data to the
+ * peer. Consider using the @ref write function if you need to ensure that
+ * all data is written before the blocking operation completes.
+ */
+ template <typename Const_Buffers, typename Error_Handler>
+ std::size_t write_some(const Const_Buffers& buffers,
+ Error_Handler error_handler)
+ {
+ return this->service.send(this->implementation, buffers, 0, error_handler);
+ }
+
+ /// Start an asynchronous write.
+ /**
+ * This function is used to asynchronously write data to the stream socket.
+ * The function call always returns immediately.
+ *
+ * @param buffers One or more data buffers to be written to the socket.
+ * Although the buffers object may be copied as necessary, ownership of the
+ * underlying memory blocks is retained by the caller, which must guarantee
+ * that they remain valid until the handler is called.
+ *
+ * @param handler The handler to be called when the write operation completes.
+ * Copies will be made of the handler as required. The function signature of
+ * the handler must be:
+ * @code void handler(
+ * const asio::error& error, // Result of operation.
+ * std::size_t bytes_transferred // Number of bytes written.
+ * ); @endcode
+ * Regardless of whether the asynchronous operation completes immediately or
+ * not, the handler will not be invoked from within this function. Invocation
+ * of the handler will be performed in a manner equivalent to using
+ * asio::io_service::post().
+ *
+ * @note The write operation may not transmit all of the data to the peer.
+ * Consider using the @ref async_write function if you need to ensure that all
+ * data is written before the asynchronous operation completes.
+ *
+ * @par Example:
+ * To write a single data buffer use the @ref buffer function as follows:
+ * @code
+ * socket.async_write_some(asio::buffer(data, size), handler);
+ * @endcode
+ * See the @ref buffer documentation for information on writing multiple
+ * buffers in one go, and how to use it with arrays, boost::array or
+ * std::vector.
+ */
+ template <typename Const_Buffers, typename Handler>
+ void async_write_some(const Const_Buffers& buffers, Handler handler)
+ {
+ this->service.async_send(this->implementation, buffers, 0, handler);
+ }
+
+ /// Read some data from the socket.
+ /**
+ * This function is used to read data from the stream socket. The function
+ * call will block until one or more bytes of data has been read successfully,
+ * or until an error occurs.
+ *
+ * @param buffers One or more buffers into which the data will be read.
+ *
+ * @returns The number of bytes read.
+ *
+ * @throws asio::error Thrown on failure. An error code of
+ * asio::error::eof indicates that the connection was closed by the
+ * peer.
+ *
+ * @note The read_some operation may not read all of the requested number of
+ * bytes. Consider using the @ref read function if you need to ensure that
+ * the requested amount of data is read before the blocking operation
+ * completes.
+ *
+ * @par Example:
+ * To read into a single data buffer use the @ref buffer function as follows:
+ * @code
+ * socket.read_some(asio::buffer(data, size));
+ * @endcode
+ * See the @ref buffer documentation for information on reading into multiple
+ * buffers in one go, and how to use it with arrays, boost::array or
+ * std::vector.
+ */
+ template <typename Mutable_Buffers>
+ std::size_t read_some(const Mutable_Buffers& buffers)
+ {
+ return this->service.receive(this->implementation, buffers, 0,
+ throw_error());
+ }
+
+ /// Read some data from the socket.
+ /**
+ * This function is used to read data from the stream socket. The function
+ * call will block until one or more bytes of data has been read successfully,
+ * or until an error occurs.
+ *
+ * @param buffers One or more buffers into which the data will be read.
+ *
+ * @param error_handler A handler to be called when the operation completes,
+ * to indicate whether or not an error has occurred. Copies will be made of
+ * the handler as required. The function signature of the handler must be:
+ * @code void error_handler(
+ * const asio::error& error // Result of operation.
+ * ); @endcode
+ *
+ * @returns The number of bytes read. Returns 0 if an error occurred and the
+ * error handler did not throw an exception.
+ *
+ * @note The read_some operation may not read all of the requested number of
+ * bytes. Consider using the @ref read function if you need to ensure that
+ * the requested amount of data is read before the blocking operation
+ * completes.
+ */
+ template <typename Mutable_Buffers, typename Error_Handler>
+ std::size_t read_some(const Mutable_Buffers& buffers,
+ Error_Handler error_handler)
+ {
+ return this->service.receive(this->implementation, buffers, 0,
+ error_handler);
+ }
+
+ /// Start an asynchronous read.
+ /**
+ * This function is used to asynchronously read data from the stream socket.
+ * The function call always returns immediately.
+ *
+ * @param buffers One or more buffers into which the data will be read.
+ * Although the buffers object may be copied as necessary, ownership of the
+ * underlying memory blocks is retained by the caller, which must guarantee
+ * that they remain valid until the handler is called.
+ *
+ * @param handler The handler to be called when the read operation completes.
+ * Copies will be made of the handler as required. The function signature of
+ * the handler must be:
+ * @code void handler(
+ * const asio::error& error, // Result of operation.
+ * std::size_t bytes_transferred // Number of bytes read.
+ * ); @endcode
+ * Regardless of whether the asynchronous operation completes immediately or
+ * not, the handler will not be invoked from within this function. Invocation
+ * of the handler will be performed in a manner equivalent to using
+ * asio::io_service::post().
+ *
+ * @note The read operation may not read all of the requested number of bytes.
+ * Consider using the @ref async_read function if you need to ensure that the
+ * requested amount of data is read before the asynchronous operation
+ * completes.
+ *
+ * @par Example:
+ * To read into a single data buffer use the @ref buffer function as follows:
+ * @code
+ * socket.async_read_some(asio::buffer(data, size), handler);
+ * @endcode
+ * See the @ref buffer documentation for information on reading into multiple
+ * buffers in one go, and how to use it with arrays, boost::array or
+ * std::vector.
+ */
+ template <typename Mutable_Buffers, typename Handler>
+ void async_read_some(const Mutable_Buffers& buffers, Handler handler)
+ {
+ this->service.async_receive(this->implementation, buffers, 0, handler);
+ }
+
+ /// Peek at the incoming data on the stream socket.
+ /**
+ * This function is used to peek at the incoming data on the stream socket,
+ * without removing it from the input queue. The function call will block
+ * until data has been read successfully or an error occurs.
+ *
+ * @param buffers One or more buffers into which the data will be read.
+ *
+ * @returns The number of bytes read.
+ *
+ * @throws asio::error Thrown on failure.
+ *
+ * @par Example:
+ * To peek using a single data buffer use the @ref buffer function as
+ * follows:
+ * @code socket.peek(asio::buffer(data, size)); @endcode
+ * See the @ref buffer documentation for information on using multiple
+ * buffers in one go, and how to use it with arrays, boost::array or
+ * std::vector.
+ */
+ template <typename Mutable_Buffers>
+ std::size_t peek(const Mutable_Buffers& buffers)
+ {
+ return this->service.receive(this->implementation, buffers,
+ socket_base::message_peek, throw_error());
+ }
+
+ /// Peek at the incoming data on the stream socket.
+ /**
+ * This function is used to peek at the incoming data on the stream socket,
+ * without removing it from the input queue. The function call will block
+ * until data has been read successfully or an error occurs.
+ *
+ * @param buffers One or more buffers into which the data will be read.
+ *
+ * @param error_handler A handler to be called when the operation completes,
+ * to indicate whether or not an error has occurred. Copies will be made of
+ * the handler as required. The function signature of the handler must be:
+ * @code void error_handler(
+ * const asio::error& error // Result of operation.
+ * ); @endcode
+ *
+ * @returns The number of bytes read. Returns 0 if an error occurred and the
+ * error handler did not throw an exception.
+ */
+ template <typename Mutable_Buffers, typename Error_Handler>
+ std::size_t peek(const Mutable_Buffers& buffers, Error_Handler error_handler)
+ {
+ return this->service.receive(this->implementation, buffers,
+ socket_base::message_peek, error_handler);
+ }
+
+ /// Determine the amount of data that may be read without blocking.
+ /**
+ * This function is used to determine the amount of data, in bytes, that may
+ * be read from the stream socket without blocking.
+ *
+ * @returns The number of bytes of data that can be read without blocking.
+ *
+ * @throws asio::error Thrown on failure.
+ */
+ std::size_t in_avail()
+ {
+ socket_base::bytes_readable command;
+ this->service.io_control(this->implementation, command, throw_error());
+ return command.get();
+ }
+
+ /// Determine the amount of data that may be read without blocking.
+ /**
+ * This function is used to determine the amount of data, in bytes, that may
+ * be read from the stream socket without blocking.
+ *
+ * @param error_handler A handler to be called when the operation completes,
+ * to indicate whether or not an error has occurred. Copies will be made of
+ * the handler as required. The function signature of the handler must be:
+ * @code void error_handler(
+ * const asio::error& error // Result of operation
+ * ); @endcode
+ *
+ * @returns The number of bytes of data that can be read without blocking.
+ */
+ template <typename Error_Handler>
+ std::size_t in_avail(Error_Handler error_handler)
+ {
+ socket_base::bytes_readable command;
+ this->service.io_control(this->implementation, command, error_handler);
+ return command.get();
+ }
+};
+
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_BASIC_STREAM_SOCKET_HPP
diff --git a/library/include/libtorrent/asio/basic_streambuf.hpp b/library/include/libtorrent/asio/basic_streambuf.hpp
new file mode 100644
index 000000000..49ce11a89
--- /dev/null
+++ b/library/include/libtorrent/asio/basic_streambuf.hpp
@@ -0,0 +1,200 @@
+//
+// basic_streambuf.hpp
+// ~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_BASIC_STREAMBUF_HPP
+#define ASIO_BASIC_STREAMBUF_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/detail/push_options.hpp"
+#include <algorithm>
+#include <limits>
+#include <memory>
+#include <stdexcept>
+#include <streambuf>
+#include <vector>
+#include "asio/detail/pop_options.hpp"
+
+#include "asio/buffer.hpp"
+#include "asio/detail/noncopyable.hpp"
+
+namespace asio {
+
+/// Automatically resizable buffer class based on std::streambuf.
+template <typename Allocator = std::allocator<char> >
+class basic_streambuf
+ : public std::streambuf,
+ private noncopyable
+{
+public:
+#if defined(GENERATING_DOCUMENTATION)
+ /// The type used to represent the get area as a list of buffers.
+ typedef implementation_defined const_buffers_type;
+
+ /// The type used to represent the put area as a list of buffers.
+ typedef implementation_defined mutable_buffers_type;
+#else
+ typedef asio::const_buffer_container_1 const_buffers_type;
+ typedef asio::mutable_buffer_container_1 mutable_buffers_type;
+#endif
+
+ /// Construct a buffer with a specified maximum size.
+ explicit basic_streambuf(
+ std::size_t max_size = (std::numeric_limits<std::size_t>::max)(),
+ const Allocator& allocator = Allocator())
+ : max_size_(max_size),
+ buffer_(allocator)
+ {
+ std::size_t pend = (std::min<std::size_t>)(max_size_, buffer_delta);
+ buffer_.resize((std::max<std::size_t>)(pend, 1));
+ setg(&buffer_[0], &buffer_[0], &buffer_[0]);
+ setp(&buffer_[0], &buffer_[0] + pend);
+ }
+
+ /// Return the size of the get area in characters.
+ std::size_t size() const
+ {
+ return pptr() - gptr();
+ }
+
+ /// Return the maximum size of the buffer.
+ std::size_t max_size() const
+ {
+ return max_size_;
+ }
+
+ /// Get a list of buffers that represents the get area.
+ const_buffers_type data() const
+ {
+ return asio::buffer(asio::const_buffer(gptr(),
+ (pptr() - gptr()) * sizeof(char_type)));
+ }
+
+ /// Get a list of buffers that represents the put area, with the given size.
+ mutable_buffers_type prepare(std::size_t size)
+ {
+ reserve(size);
+ return asio::buffer(asio::mutable_buffer(
+ pptr(), size * sizeof(char_type)));
+ }
+
+ /// Move the start of the put area by the specified number of characters.
+ void commit(std::size_t n)
+ {
+ if (pptr() + n > epptr())
+ n = epptr() - pptr();
+ pbump(static_cast<int>(n));
+ }
+
+ /// Move the start of the get area by the specified number of characters.
+ void consume(std::size_t n)
+ {
+ while (n > 0)
+ {
+ sbumpc();
+ --n;
+ }
+ }
+
+protected:
+ enum { buffer_delta = 128 };
+
+ int_type underflow()
+ {
+ if (gptr() < pptr())
+ {
+ setg(&buffer_[0], gptr(), pptr());
+ return traits_type::to_int_type(*gptr());
+ }
+ else
+ {
+ return traits_type::eof();
+ }
+ }
+
+ int_type overflow(int_type c)
+ {
+ if (!traits_type::eq_int_type(c, traits_type::eof()))
+ {
+ if (pptr() == epptr())
+ {
+ std::size_t buffer_size = pptr() - gptr();
+ if (buffer_size < max_size_ && max_size_ - buffer_size < buffer_delta)
+ {
+ reserve(max_size_ - buffer_size);
+ }
+ else
+ {
+ reserve(buffer_delta);
+ }
+ }
+
+ *pptr() = traits_type::to_char_type(c);
+ pbump(1);
+ return c;
+ }
+
+ return traits_type::not_eof(c);
+ }
+
+ void reserve(std::size_t n)
+ {
+ // Get current stream positions as offsets.
+ std::size_t gnext = gptr() - &buffer_[0];
+ std::size_t gend = egptr() - &buffer_[0];
+ std::size_t pnext = pptr() - &buffer_[0];
+ std::size_t pend = epptr() - &buffer_[0];
+
+ // Check if there is already enough space in the put area.
+ if (n <= pend - pnext)
+ {
+ return;
+ }
+
+ // Shift existing contents of get area to start of buffer.
+ if (gnext > 0)
+ {
+ std::rotate(&buffer_[0], &buffer_[0] + gnext, &buffer_[0] + pend);
+ gend -= gnext;
+ pnext -= gnext;
+ }
+
+ // Ensure buffer is large enough to hold at least the specified size.
+ if (n > pend - pnext)
+ {
+ if (n <= max_size_ && pnext <= max_size_ - n)
+ {
+ buffer_.resize((std::max<std::size_t>)(pnext + n, 1));
+ }
+ else
+ {
+ throw std::length_error("asio::streambuf too long");
+ }
+ }
+
+ // Update stream positions.
+ setg(&buffer_[0], &buffer_[0], &buffer_[0] + gend);
+ setp(&buffer_[0] + pnext, &buffer_[0] + pnext + n);
+ }
+
+private:
+ std::size_t max_size_;
+ std::vector<char_type, Allocator> buffer_;
+};
+
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_BASIC_STREAMBUF_HPP
diff --git a/library/include/libtorrent/asio/buffer.hpp b/library/include/libtorrent/asio/buffer.hpp
new file mode 100644
index 000000000..bd6442432
--- /dev/null
+++ b/library/include/libtorrent/asio/buffer.hpp
@@ -0,0 +1,639 @@
+//
+// buffer.hpp
+// ~~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_BUFFER_HPP
+#define ASIO_BUFFER_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/detail/push_options.hpp"
+#include <cstddef>
+#include <boost/config.hpp>
+#include <boost/array.hpp>
+#include <boost/type_traits/is_const.hpp>
+#include <string>
+#include <vector>
+#include "asio/detail/pop_options.hpp"
+
+namespace asio {
+
+class mutable_buffer;
+class const_buffer;
+
+namespace detail {
+void* buffer_cast_helper(const mutable_buffer&);
+const void* buffer_cast_helper(const const_buffer&);
+std::size_t buffer_size_helper(const mutable_buffer&);
+std::size_t buffer_size_helper(const const_buffer&);
+} // namespace detail
+
+/// Holds a buffer that can be modified.
+/**
+ * The mutable_buffer class provides a safe representation of a buffer that can
+ * be modified. It does not own the underlying data, and so is cheap to copy or
+ * assign.
+ */
+class mutable_buffer
+{
+public:
+ /// Construct an empty buffer.
+ mutable_buffer()
+ : data_(0),
+ size_(0)
+ {
+ }
+
+ /// Construct a buffer to represent a given memory range.
+ mutable_buffer(void* data, std::size_t size)
+ : data_(data),
+ size_(size)
+ {
+ }
+
+private:
+ friend void* asio::detail::buffer_cast_helper(
+ const mutable_buffer& b);
+ friend std::size_t asio::detail::buffer_size_helper(
+ const mutable_buffer& b);
+
+ void* data_;
+ std::size_t size_;
+};
+
+namespace detail {
+
+inline void* buffer_cast_helper(const mutable_buffer& b)
+{
+ return b.data_;
+}
+
+inline std::size_t buffer_size_helper(const mutable_buffer& b)
+{
+ return b.size_;
+}
+
+} // namespace detail
+
+/// Cast a non-modifiable buffer to a specified pointer to POD type.
+/**
+ * @relates mutable_buffer
+ */
+template <typename Pointer_To_Pod_Type>
+inline Pointer_To_Pod_Type buffer_cast(const mutable_buffer& b)
+{
+ return static_cast<Pointer_To_Pod_Type>(detail::buffer_cast_helper(b));
+}
+
+/// Get the number of bytes in a non-modifiable buffer.
+/**
+ * @relates mutable_buffer
+ */
+inline std::size_t buffer_size(const mutable_buffer& b)
+{
+ return detail::buffer_size_helper(b);
+}
+
+/// Create a new modifiable buffer that is offset from the start of another.
+/**
+ * @relates mutable_buffer
+ */
+inline mutable_buffer operator+(const mutable_buffer& b, std::size_t start)
+{
+ if (start > buffer_size(b))
+ return mutable_buffer();
+ char* new_data = buffer_cast<char*>(b) + start;
+ std::size_t new_size = buffer_size(b) - start;
+ return mutable_buffer(new_data, new_size);
+}
+
+/// Create a new modifiable buffer that is offset from the start of another.
+/**
+ * @relates mutable_buffer
+ */
+inline mutable_buffer operator+(std::size_t start, const mutable_buffer& b)
+{
+ if (start > buffer_size(b))
+ return mutable_buffer();
+ char* new_data = buffer_cast<char*>(b) + start;
+ std::size_t new_size = buffer_size(b) - start;
+ return mutable_buffer(new_data, new_size);
+}
+
+/// Adapts a single modifiable buffer so that it meets the requirements of the
+/// Mutable_Buffers concept.
+class mutable_buffer_container_1
+ : public mutable_buffer
+{
+public:
+ /// The type for each element in the list of buffers.
+ typedef mutable_buffer value_type;
+
+ /// A random-access iterator type that may be used to read elements.
+ typedef const mutable_buffer* const_iterator;
+
+ /// Construct to represent a single modifiable buffer.
+ explicit mutable_buffer_container_1(const mutable_buffer& b)
+ : mutable_buffer(b)
+ {
+ }
+
+ /// Get a random-access iterator to the first element.
+ const_iterator begin() const
+ {
+ return this;
+ }
+
+ /// Get a random-access iterator for one past the last element.
+ const_iterator end() const
+ {
+ return begin() + 1;
+ }
+};
+
+/// Holds a buffer that cannot be modified.
+/**
+ * The const_buffer class provides a safe representation of a buffer that cannot
+ * be modified. It does not own the underlying data, and so is cheap to copy or
+ * assign.
+ */
+class const_buffer
+{
+public:
+ /// Construct an empty buffer.
+ const_buffer()
+ : data_(0),
+ size_(0)
+ {
+ }
+
+ /// Construct a buffer to represent a given memory range.
+ const_buffer(const void* data, std::size_t size)
+ : data_(data),
+ size_(size)
+ {
+ }
+
+ /// Construct a non-modifiable buffer from a modifiable one.
+ const_buffer(const mutable_buffer& b)
+ : data_(asio::detail::buffer_cast_helper(b)),
+ size_(asio::detail::buffer_size_helper(b))
+ {
+ }
+
+private:
+ friend const void* asio::detail::buffer_cast_helper(
+ const const_buffer& b);
+ friend std::size_t asio::detail::buffer_size_helper(
+ const const_buffer& b);
+
+ const void* data_;
+ std::size_t size_;
+};
+
+namespace detail {
+
+inline const void* buffer_cast_helper(const const_buffer& b)
+{
+ return b.data_;
+}
+
+inline std::size_t buffer_size_helper(const const_buffer& b)
+{
+ return b.size_;
+}
+
+} // namespace detail
+
+/// Cast a non-modifiable buffer to a specified pointer to POD type.
+/**
+ * @relates const_buffer
+ */
+template <typename Pointer_To_Pod_Type>
+inline Pointer_To_Pod_Type buffer_cast(const const_buffer& b)
+{
+ return static_cast<Pointer_To_Pod_Type>(detail::buffer_cast_helper(b));
+}
+
+/// Get the number of bytes in a non-modifiable buffer.
+/**
+ * @relates const_buffer
+ */
+inline std::size_t buffer_size(const const_buffer& b)
+{
+ return detail::buffer_size_helper(b);
+}
+
+/// Create a new non-modifiable buffer that is offset from the start of another.
+/**
+ * @relates const_buffer
+ */
+inline const_buffer operator+(const const_buffer& b, std::size_t start)
+{
+ if (start > buffer_size(b))
+ return const_buffer();
+ const char* new_data = buffer_cast<const char*>(b) + start;
+ std::size_t new_size = buffer_size(b) - start;
+ return const_buffer(new_data, new_size);
+}
+
+/// Create a new non-modifiable buffer that is offset from the start of another.
+/**
+ * @relates const_buffer
+ */
+inline const_buffer operator+(std::size_t start, const const_buffer& b)
+{
+ if (start > buffer_size(b))
+ return const_buffer();
+ const char* new_data = buffer_cast<const char*>(b) + start;
+ std::size_t new_size = buffer_size(b) - start;
+ return const_buffer(new_data, new_size);
+}
+
+/// Adapts a single non-modifiable buffer so that it meets the requirements of
+/// the Const_Buffers concept.
+class const_buffer_container_1
+ : public const_buffer
+{
+public:
+ /// The type for each element in the list of buffers.
+ typedef const_buffer value_type;
+
+ /// A random-access iterator type that may be used to read elements.
+ typedef const const_buffer* const_iterator;
+
+ /// Construct to represent a single non-modifiable buffer.
+ explicit const_buffer_container_1(const const_buffer& b)
+ : const_buffer(b)
+ {
+ }
+
+ /// Get a random-access iterator to the first element.
+ const_iterator begin() const
+ {
+ return this;
+ }
+
+ /// Get a random-access iterator for one past the last element.
+ const_iterator end() const
+ {
+ return begin() + 1;
+ }
+};
+
+/** @defgroup buffer asio::buffer
+ *
+ * @brief The asio::buffer function is used to create a buffer object to
+ * represent raw memory, an array of POD elements, or a vector of POD elements.
+ *
+ * The simplest use case involves reading or writing a single buffer of a
+ * specified size:
+ *
+ * @code sock.write(asio::buffer(data, size)); @endcode
+ *
+ * In the above example, the return value of asio::buffer meets the
+ * requirements of the Const_Buffers concept so that it may be directly passed
+ * to the socket's write function. A buffer created for modifiable memory also
+ * meets the requirements of the Mutable_Buffers concept.
+ *
+ * An individual buffer may be created from a builtin array, std::vector or
+ * boost::array of POD elements. This helps prevent buffer overruns by
+ * automatically determining the size of the buffer:
+ *
+ * @code char d1[128];
+ * size_t bytes_transferred = sock.read(asio::buffer(d1));
+ *
+ * std::vector<char> d2(128);
+ * bytes_transferred = sock.read(asio::buffer(d2));
+ *
+ * boost::array<char, 128> d3;
+ * bytes_transferred = sock.read(asio::buffer(d3)); @endcode
+ *
+ * To read or write using multiple buffers (i.e. scatter-gather I/O), multiple
+ * buffer objects may be assigned into a container that supports the
+ * Mutable_Buffers (for read) or Const_Buffers (for write) concepts:
+ *
+ * @code
+ * char d1[128];
+ * std::vector<char> d2(128);
+ * boost::array<char, 128> d3;
+ *
+ * boost::array<mutable_buffer, 3> bufs1 = {
+ * asio::buffer(d1),
+ * asio::buffer(d2),
+ * asio::buffer(d3) };
+ * bytes_transferred = sock.read(bufs1);
+ *
+ * std::vector<const_buffer> bufs2;
+ * bufs2.push_back(asio::buffer(d1));
+ * bufs2.push_back(asio::buffer(d2));
+ * bufs2.push_back(asio::buffer(d3));
+ * bytes_transferred = sock.write(bufs2); @endcode
+ */
+/*@{*/
+
+/// Create a new modifiable buffer from an existing buffer.
+inline mutable_buffer_container_1 buffer(const mutable_buffer& b)
+{
+ return mutable_buffer_container_1(b);
+}
+
+/// Create a new modifiable buffer from an existing buffer.
+inline mutable_buffer_container_1 buffer(const mutable_buffer& b,
+ std::size_t max_size_in_bytes)
+{
+ return mutable_buffer_container_1(
+ mutable_buffer(buffer_cast<void*>(b),
+ buffer_size(b) < max_size_in_bytes
+ ? buffer_size(b) : max_size_in_bytes));
+}
+
+/// Create a new non-modifiable buffer from an existing buffer.
+inline const_buffer_container_1 buffer(const const_buffer& b)
+{
+ return const_buffer_container_1(b);
+}
+
+/// Create a new non-modifiable buffer from an existing buffer.
+inline const_buffer_container_1 buffer(const const_buffer& b,
+ std::size_t max_size_in_bytes)
+{
+ return const_buffer_container_1(
+ const_buffer(buffer_cast<const void*>(b),
+ buffer_size(b) < max_size_in_bytes
+ ? buffer_size(b) : max_size_in_bytes));
+}
+
+/// Create a new modifiable buffer that represents the given memory range.
+inline mutable_buffer_container_1 buffer(void* data, std::size_t size_in_bytes)
+{
+ return mutable_buffer_container_1(mutable_buffer(data, size_in_bytes));
+}
+
+/// Create a new non-modifiable buffer that represents the given memory range.
+inline const_buffer_container_1 buffer(const void* data,
+ std::size_t size_in_bytes)
+{
+ return const_buffer_container_1(const_buffer(data, size_in_bytes));
+}
+
+/// Create a new modifiable buffer that represents the given POD array.
+template <typename Pod_Type, std::size_t N>
+inline mutable_buffer_container_1 buffer(Pod_Type (&data)[N])
+{
+ return mutable_buffer_container_1(mutable_buffer(data, N * sizeof(Pod_Type)));
+}
+
+/// Create a new modifiable buffer that represents the given POD array.
+template <typename Pod_Type, std::size_t N>
+inline mutable_buffer_container_1 buffer(Pod_Type (&data)[N],
+ std::size_t max_size_in_bytes)
+{
+ return mutable_buffer_container_1(
+ mutable_buffer(data,
+ N * sizeof(Pod_Type) < max_size_in_bytes
+ ? N * sizeof(Pod_Type) : max_size_in_bytes));
+}
+
+/// Create a new non-modifiable buffer that represents the given POD array.
+template <typename Pod_Type, std::size_t N>
+inline const_buffer_container_1 buffer(const Pod_Type (&data)[N])
+{
+ return const_buffer_container_1(const_buffer(data, N * sizeof(Pod_Type)));
+}
+
+/// Create a new non-modifiable buffer that represents the given POD array.
+template <typename Pod_Type, std::size_t N>
+inline const_buffer_container_1 buffer(const Pod_Type (&data)[N],
+ std::size_t max_size_in_bytes)
+{
+ return const_buffer_container_1(
+ const_buffer(data,
+ N * sizeof(Pod_Type) < max_size_in_bytes
+ ? N * sizeof(Pod_Type) : max_size_in_bytes));
+}
+
+#if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564))
+
+// Borland C++ thinks the overloads:
+//
+// unspecified buffer(boost::array<Pod_Type, N>& array ...);
+//
+// and
+//
+// unspecified buffer(boost::array<const Pod_Type, N>& array ...);
+//
+// are ambiguous. This will be worked around by using a buffer_types traits
+// class that contains typedefs for the appropriate buffer and container
+// classes, based on whether Pod_Type is const or non-const.
+
+namespace detail {
+
+template <bool IsConst>
+struct buffer_types_base;
+
+template <>
+struct buffer_types_base<false>
+{
+ typedef mutable_buffer buffer_type;
+ typedef mutable_buffer_container_1 container_type;
+};
+
+template <>
+struct buffer_types_base<true>
+{
+ typedef const_buffer buffer_type;
+ typedef const_buffer_container_1 container_type;
+};
+
+template <typename Pod_Type>
+struct buffer_types
+ : public buffer_types_base<boost::is_const<Pod_Type>::value>
+{
+};
+
+} // namespace detail
+
+template <typename Pod_Type, std::size_t N>
+inline typename detail::buffer_types<Pod_Type>::container_type
+buffer(boost::array<Pod_Type, N>& data)
+{
+ typedef typename asio::detail::buffer_types<Pod_Type>::buffer_type
+ buffer_type;
+ typedef typename asio::detail::buffer_types<Pod_Type>::container_type
+ container_type;
+ return container_type(
+ buffer_type(data.c_array(), data.size() * sizeof(Pod_Type)));
+}
+
+template <typename Pod_Type, std::size_t N>
+inline typename detail::buffer_types<Pod_Type>::container_type
+buffer(boost::array<Pod_Type, N>& data, std::size_t max_size_in_bytes)
+{
+ typedef typename asio::detail::buffer_types<Pod_Type>::buffer_type
+ buffer_type;
+ typedef typename asio::detail::buffer_types<Pod_Type>::container_type
+ container_type;
+ return container_type(
+ buffer_type(data.c_array(),
+ data.size() * sizeof(Pod_Type) < max_size_in_bytes
+ ? data.size() * sizeof(Pod_Type) : max_size_in_bytes));
+}
+
+#else // BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564))
+
+/// Create a new modifiable buffer that represents the given POD array.
+template <typename Pod_Type, std::size_t N>
+inline mutable_buffer_container_1 buffer(boost::array<Pod_Type, N>& data)
+{
+ return mutable_buffer_container_1(
+ mutable_buffer(data.c_array(), data.size() * sizeof(Pod_Type)));
+}
+
+/// Create a new modifiable buffer that represents the given POD array.
+template <typename Pod_Type, std::size_t N>
+inline mutable_buffer_container_1 buffer(boost::array<Pod_Type, N>& data,
+ std::size_t max_size_in_bytes)
+{
+ return mutable_buffer_container_1(
+ mutable_buffer(data.c_array(),
+ data.size() * sizeof(Pod_Type) < max_size_in_bytes
+ ? data.size() * sizeof(Pod_Type) : max_size_in_bytes));
+}
+
+/// Create a new non-modifiable buffer that represents the given POD array.
+template <typename Pod_Type, std::size_t N>
+inline const_buffer_container_1 buffer(boost::array<const Pod_Type, N>& data)
+{
+ return const_buffer_container_1(
+ const_buffer(data.data(), data.size() * sizeof(Pod_Type)));
+}
+
+/// Create a new non-modifiable buffer that represents the given POD array.
+template <typename Pod_Type, std::size_t N>
+inline const_buffer_container_1 buffer(boost::array<const Pod_Type, N>& data,
+ std::size_t max_size_in_bytes)
+{
+ return const_buffer_container_1(
+ const_buffer(data.data(),
+ data.size() * sizeof(Pod_Type) < max_size_in_bytes
+ ? data.size() * sizeof(Pod_Type) : max_size_in_bytes));
+}
+
+#endif // BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564))
+
+/// Create a new non-modifiable buffer that represents the given POD array.
+template <typename Pod_Type, std::size_t N>
+inline const_buffer_container_1 buffer(const boost::array<Pod_Type, N>& data)
+{
+ return const_buffer_container_1(
+ const_buffer(data.data(), data.size() * sizeof(Pod_Type)));
+}
+
+/// Create a new non-modifiable buffer that represents the given POD array.
+template <typename Pod_Type, std::size_t N>
+inline const_buffer_container_1 buffer(const boost::array<Pod_Type, N>& data,
+ std::size_t max_size_in_bytes)
+{
+ return const_buffer_container_1(
+ const_buffer(data.data(),
+ data.size() * sizeof(Pod_Type) < max_size_in_bytes
+ ? data.size() * sizeof(Pod_Type) : max_size_in_bytes));
+}
+
+/// Create a new modifiable buffer that represents the given POD vector.
+/**
+ * @note The buffer is invalidated by any vector operation that would also
+ * invalidate iterators.
+ */
+template <typename Pod_Type, typename Allocator>
+inline mutable_buffer_container_1 buffer(std::vector<Pod_Type, Allocator>& data)
+{
+ return mutable_buffer_container_1(
+ mutable_buffer(&data[0], data.size() * sizeof(Pod_Type)));
+}
+
+/// Create a new modifiable buffer that represents the given POD vector.
+/**
+ * @note The buffer is invalidated by any vector operation that would also
+ * invalidate iterators.
+ */
+template <typename Pod_Type, typename Allocator>
+inline mutable_buffer_container_1 buffer(std::vector<Pod_Type, Allocator>& data,
+ std::size_t max_size_in_bytes)
+{
+ return mutable_buffer_container_1(
+ mutable_buffer(&data[0],
+ data.size() * sizeof(Pod_Type) < max_size_in_bytes
+ ? data.size() * sizeof(Pod_Type) : max_size_in_bytes));
+}
+
+/// Create a new non-modifiable buffer that represents the given POD vector.
+/**
+ * @note The buffer is invalidated by any vector operation that would also
+ * invalidate iterators.
+ */
+template <typename Pod_Type, typename Allocator>
+inline const_buffer_container_1 buffer(
+ const std::vector<Pod_Type, Allocator>& data)
+{
+ return const_buffer_container_1(
+ const_buffer(&data[0], data.size() * sizeof(Pod_Type)));
+}
+
+/// Create a new non-modifiable buffer that represents the given POD vector.
+/**
+ * @note The buffer is invalidated by any vector operation that would also
+ * invalidate iterators.
+ */
+template <typename Pod_Type, typename Allocator>
+inline const_buffer_container_1 buffer(
+ const std::vector<Pod_Type, Allocator>& data, std::size_t max_size_in_bytes)
+{
+ return const_buffer_container_1(
+ const_buffer(&data[0],
+ data.size() * sizeof(Pod_Type) < max_size_in_bytes
+ ? data.size() * sizeof(Pod_Type) : max_size_in_bytes));
+}
+
+/// Create a new non-modifiable buffer that represents the given string.
+/**
+ * @note The buffer is invalidated by any non-const operation called on the
+ * given string object.
+ */
+inline const_buffer_container_1 buffer(const std::string& data)
+{
+ return const_buffer_container_1(const_buffer(data.data(), data.size()));
+}
+
+/// Create a new non-modifiable buffer that represents the given string.
+/**
+ * @note The buffer is invalidated by any non-const operation called on the
+ * given string object.
+ */
+inline const_buffer_container_1 buffer(const std::string& data,
+ std::size_t max_size_in_bytes)
+{
+ return const_buffer_container_1(
+ const_buffer(data.data(),
+ data.size() < max_size_in_bytes
+ ? data.size() : max_size_in_bytes));
+}
+
+/*@}*/
+
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_BUFFER_HPP
diff --git a/library/include/libtorrent/asio/buffered_read_stream.hpp b/library/include/libtorrent/asio/buffered_read_stream.hpp
new file mode 100644
index 000000000..6260d22a0
--- /dev/null
+++ b/library/include/libtorrent/asio/buffered_read_stream.hpp
@@ -0,0 +1,407 @@
+//
+// buffered_read_stream.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_BUFFERED_READ_STREAM_HPP
+#define ASIO_BUFFERED_READ_STREAM_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/detail/push_options.hpp"
+#include <cstddef>
+#include <cstring>
+#include <boost/config.hpp>
+#include <boost/type_traits.hpp>
+#include "asio/detail/pop_options.hpp"
+
+#include "asio/buffered_read_stream_fwd.hpp"
+#include "asio/buffer.hpp"
+#include "asio/error.hpp"
+#include "asio/io_service.hpp"
+#include "asio/detail/bind_handler.hpp"
+#include "asio/detail/buffer_resize_guard.hpp"
+#include "asio/detail/buffered_stream_storage.hpp"
+#include "asio/detail/noncopyable.hpp"
+
+namespace asio {
+
+/// Adds buffering to the read-related operations of a stream.
+/**
+ * The buffered_read_stream class template can be used to add buffering to the
+ * synchronous and asynchronous read operations of a stream.
+ *
+ * @par Thread Safety:
+ * @e Distinct @e objects: Safe.@n
+ * @e Shared @e objects: Unsafe.
+ *
+ * @par Concepts:
+ * Async_Object, Async_Read_Stream, Async_Write_Stream, Error_Source, Stream,
+ * Sync_Read_Stream, Sync_Write_Stream.
+ */
+template <typename Stream>
+class buffered_read_stream
+ : private noncopyable
+{
+public:
+ /// The type of the next layer.
+ typedef typename boost::remove_reference<Stream>::type next_layer_type;
+
+ /// The type of the lowest layer.
+ typedef typename next_layer_type::lowest_layer_type lowest_layer_type;
+
+ /// The type used for reporting errors.
+ typedef typename next_layer_type::error_type error_type;
+
+#if defined(GENERATING_DOCUMENTATION)
+ /// The default buffer size.
+ static const std::size_t default_buffer_size = implementation_defined;
+#else
+ BOOST_STATIC_CONSTANT(std::size_t, default_buffer_size = 1024);
+#endif
+
+ /// Construct, passing the specified argument to initialise the next layer.
+ template <typename Arg>
+ explicit buffered_read_stream(Arg& a)
+ : next_layer_(a),
+ storage_(default_buffer_size)
+ {
+ }
+
+ /// Construct, passing the specified argument to initialise the next layer.
+ template <typename Arg>
+ buffered_read_stream(Arg& a, std::size_t buffer_size)
+ : next_layer_(a),
+ storage_(buffer_size)
+ {
+ }
+
+ /// Get a reference to the next layer.
+ next_layer_type& next_layer()
+ {
+ return next_layer_;
+ }
+
+ /// Get a reference to the lowest layer.
+ lowest_layer_type& lowest_layer()
+ {
+ return next_layer_.lowest_layer();
+ }
+
+ /// Get the io_service associated with the object.
+ asio::io_service& io_service()
+ {
+ return next_layer_.io_service();
+ }
+
+ /// Close the stream.
+ void close()
+ {
+ next_layer_.close();
+ }
+
+ /// Close the stream.
+ template <typename Error_Handler>
+ void close(Error_Handler error_handler)
+ {
+ next_layer_.close(error_handler);
+ }
+
+ /// Write the given data to the stream. Returns the number of bytes written.
+ /// Throws an exception on failure.
+ template <typename Const_Buffers>
+ std::size_t write_some(const Const_Buffers& buffers)
+ {
+ return next_layer_.write_some(buffers);
+ }
+
+ /// Write the given data to the stream. Returns the number of bytes written,
+ /// or 0 if an error occurred and the error handler did not throw.
+ template <typename Const_Buffers, typename Error_Handler>
+ std::size_t write_some(const Const_Buffers& buffers,
+ Error_Handler error_handler)
+ {
+ return next_layer_.write_some(buffers, error_handler);
+ }
+
+ /// Start an asynchronous write. The data being written must be valid for the
+ /// lifetime of the asynchronous operation.
+ template <typename Const_Buffers, typename Handler>
+ void async_write_some(const Const_Buffers& buffers, Handler handler)
+ {
+ next_layer_.async_write_some(buffers, handler);
+ }
+
+ /// Fill the buffer with some data. Returns the number of bytes placed in the
+ /// buffer as a result of the operation. Throws an exception on failure.
+ std::size_t fill()
+ {
+ detail::buffer_resize_guard<detail::buffered_stream_storage>
+ resize_guard(storage_);
+ std::size_t previous_size = storage_.size();
+ storage_.resize(storage_.capacity());
+ storage_.resize(previous_size + next_layer_.read_some(buffer(
+ storage_.data() + previous_size,
+ storage_.size() - previous_size)));
+ resize_guard.commit();
+ return storage_.size() - previous_size;
+ }
+
+ /// Fill the buffer with some data. Returns the number of bytes placed in the
+ /// buffer as a result of the operation, or 0 if an error occurred and the
+ /// error handler did not throw.
+ template <typename Error_Handler>
+ std::size_t fill(Error_Handler error_handler)
+ {
+ detail::buffer_resize_guard<detail::buffered_stream_storage>
+ resize_guard(storage_);
+ std::size_t previous_size = storage_.size();
+ storage_.resize(storage_.capacity());
+ storage_.resize(previous_size + next_layer_.read_some(buffer(
+ storage_.data() + previous_size,
+ storage_.size() - previous_size),
+ error_handler));
+ resize_guard.commit();
+ return storage_.size() - previous_size;
+ }
+
+ template <typename Handler>
+ class fill_handler
+ {
+ public:
+ fill_handler(asio::io_service& io_service,
+ detail::buffered_stream_storage& storage,
+ std::size_t previous_size, Handler handler)
+ : io_service_(io_service),
+ storage_(storage),
+ previous_size_(previous_size),
+ handler_(handler)
+ {
+ }
+
+ template <typename Error>
+ void operator()(const Error& e, std::size_t bytes_transferred)
+ {
+ storage_.resize(previous_size_ + bytes_transferred);
+ io_service_.dispatch(detail::bind_handler(
+ handler_, e, bytes_transferred));
+ }
+
+ private:
+ asio::io_service& io_service_;
+ detail::buffered_stream_storage& storage_;
+ std::size_t previous_size_;
+ Handler handler_;
+ };
+
+ /// Start an asynchronous fill.
+ template <typename Handler>
+ void async_fill(Handler handler)
+ {
+ std::size_t previous_size = storage_.size();
+ storage_.resize(storage_.capacity());
+ next_layer_.async_read_some(
+ buffer(
+ storage_.data() + previous_size,
+ storage_.size() - previous_size),
+ fill_handler<Handler>(io_service(), storage_, previous_size, handler));
+ }
+
+ /// Read some data from the stream. Returns the number of bytes read. Throws
+ /// an exception on failure.
+ template <typename Mutable_Buffers>
+ std::size_t read_some(const Mutable_Buffers& buffers)
+ {
+ if (storage_.empty())
+ fill();
+ return copy(buffers);
+ }
+
+ /// Read some data from the stream. Returns the number of bytes read or 0 if
+ /// an error occurred and the error handler did not throw an exception.
+ template <typename Mutable_Buffers, typename Error_Handler>
+ std::size_t read_some(const Mutable_Buffers& buffers,
+ Error_Handler error_handler)
+ {
+ if (storage_.empty() && !fill(error_handler))
+ return 0;
+ return copy(buffers);
+ }
+
+ template <typename Mutable_Buffers, typename Handler>
+ class read_some_handler
+ {
+ public:
+ read_some_handler(asio::io_service& io_service,
+ detail::buffered_stream_storage& storage,
+ const Mutable_Buffers& buffers, Handler handler)
+ : io_service_(io_service),
+ storage_(storage),
+ buffers_(buffers),
+ handler_(handler)
+ {
+ }
+
+ void operator()(const error_type& e, std::size_t)
+ {
+ if (e || storage_.empty())
+ {
+ std::size_t length = 0;
+ io_service_.dispatch(detail::bind_handler(handler_, e, length));
+ }
+ else
+ {
+ using namespace std; // For memcpy.
+
+ std::size_t bytes_avail = storage_.size();
+ std::size_t bytes_copied = 0;
+
+ typename Mutable_Buffers::const_iterator iter = buffers_.begin();
+ typename Mutable_Buffers::const_iterator end = buffers_.end();
+ for (; iter != end && bytes_avail > 0; ++iter)
+ {
+ std::size_t max_length = buffer_size(*iter);
+ std::size_t length = (max_length < bytes_avail)
+ ? max_length : bytes_avail;
+ memcpy(buffer_cast<void*>(*iter),
+ storage_.data() + bytes_copied, length);
+ bytes_copied += length;
+ bytes_avail -= length;
+ }
+
+ storage_.consume(bytes_copied);
+ io_service_.dispatch(detail::bind_handler(handler_, e, bytes_copied));
+ }
+ }
+
+ private:
+ asio::io_service& io_service_;
+ detail::buffered_stream_storage& storage_;
+ Mutable_Buffers buffers_;
+ Handler handler_;
+ };
+
+ /// Start an asynchronous read. The buffer into which the data will be read
+ /// must be valid for the lifetime of the asynchronous operation.
+ template <typename Mutable_Buffers, typename Handler>
+ void async_read_some(const Mutable_Buffers& buffers, Handler handler)
+ {
+ if (storage_.empty())
+ {
+ async_fill(read_some_handler<Mutable_Buffers, Handler>(
+ io_service(), storage_, buffers, handler));
+ }
+ else
+ {
+ std::size_t length = copy(buffers);
+ io_service().post(detail::bind_handler(handler, 0, length));
+ }
+ }
+
+ /// Peek at the incoming data on the stream. Returns the number of bytes read.
+ /// Throws an exception on failure.
+ template <typename Mutable_Buffers>
+ std::size_t peek(const Mutable_Buffers& buffers)
+ {
+ if (storage_.empty())
+ fill();
+ return peek_copy(buffers);
+ }
+
+ /// Peek at the incoming data on the stream. Returns the number of bytes read,
+ /// or 0 if an error occurred and the error handler did not throw.
+ template <typename Mutable_Buffers, typename Error_Handler>
+ std::size_t peek(const Mutable_Buffers& buffers, Error_Handler error_handler)
+ {
+ if (storage_.empty() && !fill(error_handler))
+ return 0;
+ return peek_copy(buffers);
+ }
+
+ /// Determine the amount of data that may be read without blocking.
+ std::size_t in_avail()
+ {
+ return storage_.size();
+ }
+
+ /// Determine the amount of data that may be read without blocking.
+ template <typename Error_Handler>
+ std::size_t in_avail(Error_Handler error_handler)
+ {
+ return storage_.size();
+ }
+
+private:
+ /// Copy data out of the internal buffer to the specified target buffer.
+ /// Returns the number of bytes copied.
+ template <typename Mutable_Buffers>
+ std::size_t copy(const Mutable_Buffers& buffers)
+ {
+ using namespace std; // For memcpy.
+
+ std::size_t bytes_avail = storage_.size();
+ std::size_t bytes_copied = 0;
+
+ typename Mutable_Buffers::const_iterator iter = buffers.begin();
+ typename Mutable_Buffers::const_iterator end = buffers.end();
+ for (; iter != end && bytes_avail > 0; ++iter)
+ {
+ std::size_t max_length = buffer_size(*iter);
+ std::size_t length = (max_length < bytes_avail)
+ ? max_length : bytes_avail;
+ memcpy(buffer_cast<void*>(*iter), storage_.data() + bytes_copied, length);
+ bytes_copied += length;
+ bytes_avail -= length;
+ }
+
+ storage_.consume(bytes_copied);
+ return bytes_copied;
+ }
+
+ /// Copy data from the internal buffer to the specified target buffer, without
+ /// removing the data from the internal buffer. Returns the number of bytes
+ /// copied.
+ template <typename Mutable_Buffers>
+ std::size_t peek_copy(const Mutable_Buffers& buffers)
+ {
+ using namespace std; // For memcpy.
+
+ std::size_t bytes_avail = storage_.size();
+ std::size_t bytes_copied = 0;
+
+ typename Mutable_Buffers::const_iterator iter = buffers.begin();
+ typename Mutable_Buffers::const_iterator end = buffers.end();
+ for (; iter != end && bytes_avail > 0; ++iter)
+ {
+ std::size_t max_length = buffer_size(*iter);
+ std::size_t length = (max_length < bytes_avail)
+ ? max_length : bytes_avail;
+ memcpy(buffer_cast<void*>(*iter), storage_.data() + bytes_copied, length);
+ bytes_copied += length;
+ bytes_avail -= length;
+ }
+
+ return bytes_copied;
+ }
+
+ /// The next layer.
+ Stream next_layer_;
+
+ // The data in the buffer.
+ detail::buffered_stream_storage storage_;
+};
+
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_BUFFERED_READ_STREAM_HPP
diff --git a/library/include/libtorrent/asio/buffered_read_stream_fwd.hpp b/library/include/libtorrent/asio/buffered_read_stream_fwd.hpp
new file mode 100644
index 000000000..7cdf04bcf
--- /dev/null
+++ b/library/include/libtorrent/asio/buffered_read_stream_fwd.hpp
@@ -0,0 +1,29 @@
+//
+// buffered_read_stream_fwd.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_BUFFERED_READ_STREAM_FWD_HPP
+#define ASIO_BUFFERED_READ_STREAM_FWD_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+namespace asio {
+
+template <typename Stream>
+class buffered_read_stream;
+
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_BUFFERED_READ_STREAM_FWD_HPP
diff --git a/library/include/libtorrent/asio/buffered_stream.hpp b/library/include/libtorrent/asio/buffered_stream.hpp
new file mode 100644
index 000000000..1948fef09
--- /dev/null
+++ b/library/include/libtorrent/asio/buffered_stream.hpp
@@ -0,0 +1,249 @@
+//
+// buffered_stream.hpp
+// ~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_BUFFERED_STREAM_HPP
+#define ASIO_BUFFERED_STREAM_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/detail/push_options.hpp"
+#include <cstddef>
+#include <boost/config.hpp>
+#include "asio/detail/pop_options.hpp"
+
+#include "asio/buffered_read_stream.hpp"
+#include "asio/buffered_write_stream.hpp"
+#include "asio/buffered_stream_fwd.hpp"
+#include "asio/error.hpp"
+#include "asio/io_service.hpp"
+#include "asio/detail/noncopyable.hpp"
+
+namespace asio {
+
+/// Adds buffering to the read- and write-related operations of a stream.
+/**
+ * The buffered_stream class template can be used to add buffering to the
+ * synchronous and asynchronous read and write operations of a stream.
+ *
+ * @par Thread Safety:
+ * @e Distinct @e objects: Safe.@n
+ * @e Shared @e objects: Unsafe.
+ *
+ * @par Concepts:
+ * Async_Object, Async_Read_Stream, Async_Write_Stream, Error_Source, Stream,
+ * Sync_Read_Stream, Sync_Write_Stream.
+ */
+template <typename Stream>
+class buffered_stream
+ : private noncopyable
+{
+public:
+ /// The type of the next layer.
+ typedef typename boost::remove_reference<Stream>::type next_layer_type;
+
+ /// The type of the lowest layer.
+ typedef typename next_layer_type::lowest_layer_type lowest_layer_type;
+
+ /// The type used for reporting errors.
+ typedef typename next_layer_type::error_type error_type;
+
+ /// Construct, passing the specified argument to initialise the next layer.
+ template <typename Arg>
+ explicit buffered_stream(Arg& a)
+ : inner_stream_impl_(a),
+ stream_impl_(inner_stream_impl_)
+ {
+ }
+
+ /// Construct, passing the specified argument to initialise the next layer.
+ template <typename Arg>
+ explicit buffered_stream(Arg& a, std::size_t read_buffer_size,
+ std::size_t write_buffer_size)
+ : inner_stream_impl_(a, write_buffer_size),
+ stream_impl_(inner_stream_impl_, read_buffer_size)
+ {
+ }
+
+ /// Get a reference to the next layer.
+ next_layer_type& next_layer()
+ {
+ return stream_impl_.next_layer().next_layer();
+ }
+
+ /// Get a reference to the lowest layer.
+ lowest_layer_type& lowest_layer()
+ {
+ return stream_impl_.lowest_layer();
+ }
+
+ /// Get the io_service associated with the object.
+ asio::io_service& io_service()
+ {
+ return stream_impl_.io_service();
+ }
+
+ /// Close the stream.
+ void close()
+ {
+ stream_impl_.close();
+ }
+
+ /// Close the stream.
+ template <typename Error_Handler>
+ void close(Error_Handler error_handler)
+ {
+ stream_impl_.close(error_handler);
+ }
+
+ /// Flush all data from the buffer to the next layer. Returns the number of
+ /// bytes written to the next layer on the last write operation. Throws an
+ /// exception on failure.
+ std::size_t flush()
+ {
+ return stream_impl_.next_layer().flush();
+ }
+
+ /// Flush all data from the buffer to the next layer. Returns the number of
+ /// bytes written to the next layer on the last write operation, or 0 if an
+ /// error occurred and the error handler did not throw.
+ template <typename Error_Handler>
+ std::size_t flush(Error_Handler error_handler)
+ {
+ return stream_impl_.next_layer().flush(error_handler);
+ }
+
+ /// Start an asynchronous flush.
+ template <typename Handler>
+ void async_flush(Handler handler)
+ {
+ return stream_impl_.next_layer().async_flush(handler);
+ }
+
+ /// Write the given data to the stream. Returns the number of bytes written.
+ /// Throws an exception on failure.
+ template <typename Const_Buffers>
+ std::size_t write_some(const Const_Buffers& buffers)
+ {
+ return stream_impl_.write_some(buffers);
+ }
+
+ /// Write the given data to the stream. Returns the number of bytes written,
+ /// or 0 if an error occurred and the error handler did not throw.
+ template <typename Const_Buffers, typename Error_Handler>
+ std::size_t write_some(const Const_Buffers& buffers,
+ Error_Handler error_handler)
+ {
+ return stream_impl_.write_some(buffers, error_handler);
+ }
+
+ /// Start an asynchronous write. The data being written must be valid for the
+ /// lifetime of the asynchronous operation.
+ template <typename Const_Buffers, typename Handler>
+ void async_write_some(const Const_Buffers& buffers, Handler handler)
+ {
+ stream_impl_.async_write_some(buffers, handler);
+ }
+
+ /// Fill the buffer with some data. Returns the number of bytes placed in the
+ /// buffer as a result of the operation. Throws an exception on failure.
+ std::size_t fill()
+ {
+ return stream_impl_.fill();
+ }
+
+ /// Fill the buffer with some data. Returns the number of bytes placed in the
+ /// buffer as a result of the operation, or 0 if an error occurred and the
+ /// error handler did not throw.
+ template <typename Error_Handler>
+ std::size_t fill(Error_Handler error_handler)
+ {
+ return stream_impl_.fill(error_handler);
+ }
+
+ /// Start an asynchronous fill.
+ template <typename Handler>
+ void async_fill(Handler handler)
+ {
+ stream_impl_.async_fill(handler);
+ }
+
+ /// Read some data from the stream. Returns the number of bytes read. Throws
+ /// an exception on failure.
+ template <typename Mutable_Buffers>
+ std::size_t read_some(const Mutable_Buffers& buffers)
+ {
+ return stream_impl_.read_some(buffers);
+ }
+
+ /// Read some data from the stream. Returns the number of bytes read or 0 if
+ /// an error occurred and the error handler did not throw an exception.
+ template <typename Mutable_Buffers, typename Error_Handler>
+ std::size_t read_some(const Mutable_Buffers& buffers,
+ Error_Handler error_handler)
+ {
+ return stream_impl_.read_some(buffers, error_handler);
+ }
+
+ /// Start an asynchronous read. The buffer into which the data will be read
+ /// must be valid for the lifetime of the asynchronous operation.
+ template <typename Mutable_Buffers, typename Handler>
+ void async_read_some(const Mutable_Buffers& buffers, Handler handler)
+ {
+ stream_impl_.async_read_some(buffers, handler);
+ }
+
+ /// Peek at the incoming data on the stream. Returns the number of bytes read.
+ /// Throws an exception on failure.
+ template <typename Mutable_Buffers>
+ std::size_t peek(const Mutable_Buffers& buffers)
+ {
+ return stream_impl_.peek(buffers);
+ }
+
+ /// Peek at the incoming data on the stream. Returns the number of bytes read,
+ /// or 0 if an error occurred and the error handler did not throw.
+ template <typename Mutable_Buffers, typename Error_Handler>
+ std::size_t peek(const Mutable_Buffers& buffers, Error_Handler error_handler)
+ {
+ return stream_impl_.peek(buffers, error_handler);
+ }
+
+ /// Determine the amount of data that may be read without blocking.
+ std::size_t in_avail()
+ {
+ return stream_impl_.in_avail();
+ }
+
+ /// Determine the amount of data that may be read without blocking.
+ template <typename Error_Handler>
+ std::size_t in_avail(Error_Handler error_handler)
+ {
+ return stream_impl_.in_avail(error_handler);
+ }
+
+private:
+ // The buffered write stream.
+ typedef buffered_write_stream<Stream> write_stream_type;
+ write_stream_type inner_stream_impl_;
+
+ // The buffered read stream.
+ typedef buffered_read_stream<write_stream_type&> read_stream_type;
+ read_stream_type stream_impl_;
+};
+
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_BUFFERED_STREAM_HPP
diff --git a/library/include/libtorrent/asio/buffered_stream_fwd.hpp b/library/include/libtorrent/asio/buffered_stream_fwd.hpp
new file mode 100644
index 000000000..4590ae8e3
--- /dev/null
+++ b/library/include/libtorrent/asio/buffered_stream_fwd.hpp
@@ -0,0 +1,29 @@
+//
+// buffered_stream_fwd.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_BUFFERED_STREAM_FWD_HPP
+#define ASIO_BUFFERED_STREAM_FWD_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+namespace asio {
+
+template <typename Stream>
+class buffered_stream;
+
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_BUFFERED_STREAM_FWD_HPP
diff --git a/library/include/libtorrent/asio/buffered_write_stream.hpp b/library/include/libtorrent/asio/buffered_write_stream.hpp
new file mode 100644
index 000000000..d4bd49b92
--- /dev/null
+++ b/library/include/libtorrent/asio/buffered_write_stream.hpp
@@ -0,0 +1,362 @@
+//
+// buffered_write_stream.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_BUFFERED_WRITE_STREAM_HPP
+#define ASIO_BUFFERED_WRITE_STREAM_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/detail/push_options.hpp"
+#include <cstddef>
+#include <cstring>
+#include <boost/config.hpp>
+#include <boost/type_traits.hpp>
+#include "asio/detail/pop_options.hpp"
+
+#include "asio/buffered_write_stream_fwd.hpp"
+#include "asio/buffer.hpp"
+#include "asio/completion_condition.hpp"
+#include "asio/error.hpp"
+#include "asio/io_service.hpp"
+#include "asio/write.hpp"
+#include "asio/detail/bind_handler.hpp"
+#include "asio/detail/buffered_stream_storage.hpp"
+#include "asio/detail/noncopyable.hpp"
+
+namespace asio {
+
+/// Adds buffering to the write-related operations of a stream.
+/**
+ * The buffered_write_stream class template can be used to add buffering to the
+ * synchronous and asynchronous write operations of a stream.
+ *
+ * @par Thread Safety:
+ * @e Distinct @e objects: Safe.@n
+ * @e Shared @e objects: Unsafe.
+ *
+ * @par Concepts:
+ * Async_Object, Async_Read_Stream, Async_Write_Stream, Error_Source, Stream,
+ * Sync_Read_Stream, Sync_Write_Stream.
+ */
+template <typename Stream>
+class buffered_write_stream
+ : private noncopyable
+{
+public:
+ /// The type of the next layer.
+ typedef typename boost::remove_reference<Stream>::type next_layer_type;
+
+ /// The type of the lowest layer.
+ typedef typename next_layer_type::lowest_layer_type lowest_layer_type;
+
+ /// The type used for reporting errors.
+ typedef typename next_layer_type::error_type error_type;
+
+#if defined(GENERATING_DOCUMENTATION)
+ /// The default buffer size.
+ static const std::size_t default_buffer_size = implementation_defined;
+#else
+ BOOST_STATIC_CONSTANT(std::size_t, default_buffer_size = 1024);
+#endif
+
+ /// Construct, passing the specified argument to initialise the next layer.
+ template <typename Arg>
+ explicit buffered_write_stream(Arg& a)
+ : next_layer_(a),
+ storage_(default_buffer_size)
+ {
+ }
+
+ /// Construct, passing the specified argument to initialise the next layer.
+ template <typename Arg>
+ buffered_write_stream(Arg& a, std::size_t buffer_size)
+ : next_layer_(a),
+ storage_(buffer_size)
+ {
+ }
+
+ /// Get a reference to the next layer.
+ next_layer_type& next_layer()
+ {
+ return next_layer_;
+ }
+
+ /// Get a reference to the lowest layer.
+ lowest_layer_type& lowest_layer()
+ {
+ return next_layer_.lowest_layer();
+ }
+
+ /// Get the io_service associated with the object.
+ asio::io_service& io_service()
+ {
+ return next_layer_.io_service();
+ }
+
+ /// Close the stream.
+ void close()
+ {
+ next_layer_.close();
+ }
+
+ /// Close the stream.
+ template <typename Error_Handler>
+ void close(Error_Handler error_handler)
+ {
+ next_layer_.close(error_handler);
+ }
+
+ /// Flush all data from the buffer to the next layer. Returns the number of
+ /// bytes written to the next layer on the last write operation. Throws an
+ /// exception on failure.
+ std::size_t flush()
+ {
+ std::size_t bytes_written = write(next_layer_,
+ buffer(storage_.data(), storage_.size()));
+ storage_.consume(bytes_written);
+ return bytes_written;
+ }
+
+ /// Flush all data from the buffer to the next layer. Returns the number of
+ /// bytes written to the next layer on the last write operation, or 0 if an
+ /// error occurred and the error handler did not throw.
+ template <typename Error_Handler>
+ std::size_t flush(Error_Handler error_handler)
+ {
+ std::size_t bytes_written = write(next_layer_,
+ buffer(storage_.data(), storage_.size()),
+ transfer_all(), error_handler);
+ storage_.consume(bytes_written);
+ return bytes_written;
+ }
+
+ template <typename Handler>
+ class flush_handler
+ {
+ public:
+ flush_handler(asio::io_service& io_service,
+ detail::buffered_stream_storage& storage, Handler handler)
+ : io_service_(io_service),
+ storage_(storage),
+ handler_(handler)
+ {
+ }
+
+ void operator()(const error_type& e, std::size_t bytes_written)
+ {
+ storage_.consume(bytes_written);
+ io_service_.dispatch(detail::bind_handler(handler_, e, bytes_written));
+ }
+
+ private:
+ asio::io_service& io_service_;
+ detail::buffered_stream_storage& storage_;
+ Handler handler_;
+ };
+
+ /// Start an asynchronous flush.
+ template <typename Handler>
+ void async_flush(Handler handler)
+ {
+ async_write(next_layer_, buffer(storage_.data(), storage_.size()),
+ flush_handler<Handler>(io_service(), storage_, handler));
+ }
+
+ /// Write the given data to the stream. Returns the number of bytes written.
+ /// Throws an exception on failure.
+ template <typename Const_Buffers>
+ std::size_t write_some(const Const_Buffers& buffers)
+ {
+ if (storage_.size() == storage_.capacity())
+ flush();
+ return copy(buffers);
+ }
+
+ /// Write the given data to the stream. Returns the number of bytes written,
+ /// or 0 if an error occurred and the error handler did not throw.
+ template <typename Const_Buffers, typename Error_Handler>
+ std::size_t write_some(const Const_Buffers& buffers,
+ Error_Handler error_handler)
+ {
+ if (storage_.size() == storage_.capacity() && !flush(error_handler))
+ return 0;
+ return copy(buffers);
+ }
+
+ template <typename Const_Buffers, typename Handler>
+ class write_some_handler
+ {
+ public:
+ write_some_handler(asio::io_service& io_service,
+ detail::buffered_stream_storage& storage,
+ const Const_Buffers& buffers, Handler handler)
+ : io_service_(io_service),
+ storage_(storage),
+ buffers_(buffers),
+ handler_(handler)
+ {
+ }
+
+ void operator()(const error_type& e, std::size_t)
+ {
+ if (e)
+ {
+ std::size_t length = 0;
+ io_service_.dispatch(detail::bind_handler(handler_, e, length));
+ }
+ else
+ {
+ using namespace std; // For memcpy.
+
+ std::size_t orig_size = storage_.size();
+ std::size_t space_avail = storage_.capacity() - orig_size;
+ std::size_t bytes_copied = 0;
+
+ typename Const_Buffers::const_iterator iter = buffers_.begin();
+ typename Const_Buffers::const_iterator end = buffers_.end();
+ for (; iter != end && space_avail > 0; ++iter)
+ {
+ std::size_t bytes_avail = buffer_size(*iter);
+ std::size_t length = (bytes_avail < space_avail)
+ ? bytes_avail : space_avail;
+ storage_.resize(orig_size + bytes_copied + length);
+ memcpy(storage_.data() + orig_size + bytes_copied,
+ buffer_cast<const void*>(*iter), length);
+ bytes_copied += length;
+ space_avail -= length;
+ }
+
+ io_service_.dispatch(detail::bind_handler(handler_, e, bytes_copied));
+ }
+ }
+
+ private:
+ asio::io_service& io_service_;
+ detail::buffered_stream_storage& storage_;
+ Const_Buffers buffers_;
+ Handler handler_;
+ };
+
+ /// Start an asynchronous write. The data being written must be valid for the
+ /// lifetime of the asynchronous operation.
+ template <typename Const_Buffers, typename Handler>
+ void async_write_some(const Const_Buffers& buffers, Handler handler)
+ {
+ if (storage_.size() == storage_.capacity())
+ {
+ async_flush(write_some_handler<Const_Buffers, Handler>(
+ io_service(), storage_, buffers, handler));
+ }
+ else
+ {
+ std::size_t bytes_copied = copy(buffers);
+ io_service().post(detail::bind_handler(handler, 0, bytes_copied));
+ }
+ }
+
+ /// Read some data from the stream. Returns the number of bytes read. Throws
+ /// an exception on failure.
+ template <typename Mutable_Buffers>
+ std::size_t read_some(const Mutable_Buffers& buffers)
+ {
+ return next_layer_.read_some(buffers);
+ }
+
+ /// Read some data from the stream. Returns the number of bytes read or 0 if
+ /// an error occurred and the error handler did not throw an exception.
+ template <typename Mutable_Buffers, typename Error_Handler>
+ std::size_t read_some(const Mutable_Buffers& buffers,
+ Error_Handler error_handler)
+ {
+ return next_layer_.read_some(buffers, error_handler);
+ }
+
+ /// Start an asynchronous read. The buffer into which the data will be read
+ /// must be valid for the lifetime of the asynchronous operation.
+ template <typename Mutable_Buffers, typename Handler>
+ void async_read_some(const Mutable_Buffers& buffers, Handler handler)
+ {
+ next_layer_.async_read_some(buffers, handler);
+ }
+
+ /// Peek at the incoming data on the stream. Returns the number of bytes read.
+ /// Throws an exception on failure.
+ template <typename Mutable_Buffers>
+ std::size_t peek(const Mutable_Buffers& buffers)
+ {
+ return next_layer_.peek(buffers);
+ }
+
+ /// Peek at the incoming data on the stream. Returns the number of bytes read,
+ /// or 0 if an error occurred and the error handler did not throw.
+ template <typename Mutable_Buffers, typename Error_Handler>
+ std::size_t peek(const Mutable_Buffers& buffers, Error_Handler error_handler)
+ {
+ return next_layer_.peek(buffers, error_handler);
+ }
+
+ /// Determine the amount of data that may be read without blocking.
+ std::size_t in_avail()
+ {
+ return next_layer_.in_avail();
+ }
+
+ /// Determine the amount of data that may be read without blocking.
+ template <typename Error_Handler>
+ std::size_t in_avail(Error_Handler error_handler)
+ {
+ return next_layer_.in_avail(error_handler);
+ }
+
+private:
+ /// Copy data into the internal buffer from the specified source buffer.
+ /// Returns the number of bytes copied.
+ template <typename Const_Buffers>
+ std::size_t copy(const Const_Buffers& buffers)
+ {
+ using namespace std; // For memcpy.
+
+ std::size_t orig_size = storage_.size();
+ std::size_t space_avail = storage_.capacity() - orig_size;
+ std::size_t bytes_copied = 0;
+
+ typename Const_Buffers::const_iterator iter = buffers.begin();
+ typename Const_Buffers::const_iterator end = buffers.end();
+ for (; iter != end && space_avail > 0; ++iter)
+ {
+ std::size_t bytes_avail = buffer_size(*iter);
+ std::size_t length = (bytes_avail < space_avail)
+ ? bytes_avail : space_avail;
+ storage_.resize(orig_size + bytes_copied + length);
+ memcpy(storage_.data() + orig_size + bytes_copied,
+ buffer_cast<const void*>(*iter), length);
+ bytes_copied += length;
+ space_avail -= length;
+ }
+
+ return bytes_copied;
+ }
+
+ /// The next layer.
+ Stream next_layer_;
+
+ // The data in the buffer.
+ detail::buffered_stream_storage storage_;
+};
+
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_BUFFERED_WRITE_STREAM_HPP
diff --git a/library/include/libtorrent/asio/buffered_write_stream_fwd.hpp b/library/include/libtorrent/asio/buffered_write_stream_fwd.hpp
new file mode 100644
index 000000000..6cabef816
--- /dev/null
+++ b/library/include/libtorrent/asio/buffered_write_stream_fwd.hpp
@@ -0,0 +1,29 @@
+//
+// buffered_write_stream_fwd.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_BUFFERED_WRITE_STREAM_FWD_HPP
+#define ASIO_BUFFERED_WRITE_STREAM_FWD_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+namespace asio {
+
+template <typename Stream>
+class buffered_write_stream;
+
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_BUFFERED_WRITE_STREAM_FWD_HPP
diff --git a/library/include/libtorrent/asio/completion_condition.hpp b/library/include/libtorrent/asio/completion_condition.hpp
new file mode 100644
index 000000000..3f8f7b611
--- /dev/null
+++ b/library/include/libtorrent/asio/completion_condition.hpp
@@ -0,0 +1,101 @@
+//
+// completion_condition.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_COMPLETION_CONDITION_HPP
+#define ASIO_COMPLETION_CONDITION_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/detail/push_options.hpp"
+#include <cstddef>
+#include <boost/config.hpp>
+#include "asio/detail/pop_options.hpp"
+
+namespace asio {
+
+namespace detail {
+
+class transfer_all_t
+{
+public:
+ typedef bool result_type;
+
+ template <typename Error>
+ bool operator()(const Error& err, std::size_t)
+ {
+ return !!err;
+ }
+};
+
+class transfer_at_least_t
+{
+public:
+ typedef bool result_type;
+
+ explicit transfer_at_least_t(std::size_t minimum)
+ : minimum_(minimum)
+ {
+ }
+
+ template <typename Error>
+ bool operator()(const Error& err, std::size_t bytes_transferred)
+ {
+ return !!err || bytes_transferred >= minimum_;
+ }
+
+private:
+ std::size_t minimum_;
+};
+
+} // namespace detail
+
+/**
+ * @defgroup completion_condition Completion Condition Function Objects
+ *
+ * Function objects used for determining when a read or write operation should
+ * complete.
+ */
+/*@{*/
+
+/// Return a completion condition function object that indicates that a read or
+/// write operation should continue until all of the data has been transferred,
+/// or until an error occurs.
+#if defined(GENERATING_DOCUMENTATION)
+unspecified transfer_all();
+#else
+inline detail::transfer_all_t transfer_all()
+{
+ return detail::transfer_all_t();
+}
+#endif
+
+/// Return a completion condition function object that indicates that a read or
+/// write operation should continue until a minimum number of bytes has been
+/// transferred, or until an error occurs.
+#if defined(GENERATING_DOCUMENTATION)
+unspecified transfer_at_least(std::size_t minimum);
+#else
+inline detail::transfer_at_least_t transfer_at_least(std::size_t minimum)
+{
+ return detail::transfer_at_least_t(minimum);
+}
+#endif
+
+/*@}*/
+
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_COMPLETION_CONDITION_HPP
diff --git a/library/include/libtorrent/asio/datagram_socket_service.hpp b/library/include/libtorrent/asio/datagram_socket_service.hpp
new file mode 100644
index 000000000..77a3af42d
--- /dev/null
+++ b/library/include/libtorrent/asio/datagram_socket_service.hpp
@@ -0,0 +1,295 @@
+//
+// datagram_socket_service.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DATAGRAM_SOCKET_SERVICE_HPP
+#define ASIO_DATAGRAM_SOCKET_SERVICE_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/detail/push_options.hpp"
+#include <cstddef>
+#include <boost/config.hpp>
+#include "asio/detail/pop_options.hpp"
+
+#include "asio/io_service.hpp"
+#include "asio/detail/epoll_reactor.hpp"
+#include "asio/detail/kqueue_reactor.hpp"
+#include "asio/detail/select_reactor.hpp"
+#include "asio/detail/reactive_socket_service.hpp"
+#include "asio/detail/win_iocp_socket_service.hpp"
+
+namespace asio {
+
+/// Default service implementation for a datagram socket.
+template <typename Protocol>
+class datagram_socket_service
+ : public asio::io_service::service
+{
+public:
+ /// The protocol type.
+ typedef Protocol protocol_type;
+
+ /// The endpoint type.
+ typedef typename Protocol::endpoint endpoint_type;
+
+private:
+ // The type of the platform-specific implementation.
+#if defined(ASIO_HAS_IOCP)
+ typedef detail::win_iocp_socket_service<Protocol> service_impl_type;
+#elif defined(ASIO_HAS_EPOLL)
+ typedef detail::reactive_socket_service<
+ Protocol, detail::epoll_reactor<false> > service_impl_type;
+#elif defined(ASIO_HAS_KQUEUE)
+ typedef detail::reactive_socket_service<
+ Protocol, detail::kqueue_reactor<false> > service_impl_type;
+#else
+ typedef detail::reactive_socket_service<
+ Protocol, detail::select_reactor<false> > service_impl_type;
+#endif
+
+public:
+ /// The type of a datagram socket.
+#if defined(GENERATING_DOCUMENTATION)
+ typedef implementation_defined implementation_type;
+#else
+ typedef typename service_impl_type::implementation_type implementation_type;
+#endif
+
+ /// The native socket type.
+#if defined(GENERATING_DOCUMENTATION)
+ typedef implementation_defined native_type;
+#else
+ typedef typename service_impl_type::native_type native_type;
+#endif
+
+ /// Construct a new datagram socket service for the specified io_service.
+ explicit datagram_socket_service(asio::io_service& io_service)
+ : asio::io_service::service(io_service),
+ service_impl_(asio::use_service<service_impl_type>(io_service))
+ {
+ }
+
+ /// Destroy all user-defined handler objects owned by the service.
+ void shutdown_service()
+ {
+ }
+
+ /// Construct a new datagram socket implementation.
+ void construct(implementation_type& impl)
+ {
+ service_impl_.construct(impl);
+ }
+
+ /// Destroy a datagram socket implementation.
+ void destroy(implementation_type& impl)
+ {
+ service_impl_.destroy(impl);
+ }
+
+ // Open a new datagram socket implementation.
+ template <typename Error_Handler>
+ void open(implementation_type& impl, const protocol_type& protocol,
+ Error_Handler error_handler)
+ {
+ if (protocol.type() == SOCK_DGRAM)
+ service_impl_.open(impl, protocol, error_handler);
+ else
+ error_handler(asio::error(asio::error::invalid_argument));
+ }
+
+ /// Assign an existing native socket to a datagram socket.
+ template <typename Error_Handler>
+ void assign(implementation_type& impl, const protocol_type& protocol,
+ const native_type& native_socket, Error_Handler error_handler)
+ {
+ service_impl_.assign(impl, protocol, native_socket, error_handler);
+ }
+
+ /// Close a datagram socket implementation.
+ template <typename Error_Handler>
+ void close(implementation_type& impl, Error_Handler error_handler)
+ {
+ service_impl_.close(impl, error_handler);
+ }
+
+ /// Get the native socket implementation.
+ native_type native(implementation_type& impl)
+ {
+ return service_impl_.native(impl);
+ }
+
+ /// Cancel all asynchronous operations associated with the socket.
+ template <typename Error_Handler>
+ void cancel(implementation_type& impl, Error_Handler error_handler)
+ {
+ service_impl_.cancel(impl, error_handler);
+ }
+
+ // Bind the datagram socket to the specified local endpoint.
+ template <typename Error_Handler>
+ void bind(implementation_type& impl, const endpoint_type& endpoint,
+ Error_Handler error_handler)
+ {
+ service_impl_.bind(impl, endpoint, error_handler);
+ }
+
+ /// Connect the datagram socket to the specified endpoint.
+ template <typename Error_Handler>
+ void connect(implementation_type& impl, const endpoint_type& peer_endpoint,
+ Error_Handler error_handler)
+ {
+ service_impl_.connect(impl, peer_endpoint, error_handler);
+ }
+
+ /// Start an asynchronous connect.
+ template <typename Handler>
+ void async_connect(implementation_type& impl,
+ const endpoint_type& peer_endpoint, Handler handler)
+ {
+ service_impl_.async_connect(impl, peer_endpoint, handler);
+ }
+
+ /// Set a socket option.
+ template <typename Option, typename Error_Handler>
+ void set_option(implementation_type& impl, const Option& option,
+ Error_Handler error_handler)
+ {
+ service_impl_.set_option(impl, option, error_handler);
+ }
+
+ /// Get a socket option.
+ template <typename Option, typename Error_Handler>
+ void get_option(const implementation_type& impl, Option& option,
+ Error_Handler error_handler) const
+ {
+ service_impl_.get_option(impl, option, error_handler);
+ }
+
+ /// Perform an IO control command on the socket.
+ template <typename IO_Control_Command, typename Error_Handler>
+ void io_control(implementation_type& impl, IO_Control_Command& command,
+ Error_Handler error_handler)
+ {
+ service_impl_.io_control(impl, command, error_handler);
+ }
+
+ /// Get the local endpoint.
+ template <typename Error_Handler>
+ endpoint_type local_endpoint(const implementation_type& impl,
+ Error_Handler error_handler) const
+ {
+ endpoint_type endpoint;
+ service_impl_.get_local_endpoint(impl, endpoint, error_handler);
+ return endpoint;
+ }
+
+ /// Get the remote endpoint.
+ template <typename Error_Handler>
+ endpoint_type remote_endpoint(const implementation_type& impl,
+ Error_Handler error_handler) const
+ {
+ endpoint_type endpoint;
+ service_impl_.get_remote_endpoint(impl, endpoint, error_handler);
+ return endpoint;
+ }
+
+ /// Disable sends or receives on the socket.
+ template <typename Error_Handler>
+ void shutdown(implementation_type& impl, socket_base::shutdown_type what,
+ Error_Handler error_handler)
+ {
+ service_impl_.shutdown(impl, what, error_handler);
+ }
+
+ /// Send the given data to the peer.
+ template <typename Const_Buffers, typename Error_Handler>
+ std::size_t send(implementation_type& impl, const Const_Buffers& buffers,
+ socket_base::message_flags flags, Error_Handler error_handler)
+ {
+ return service_impl_.send(impl, buffers, flags, error_handler);
+ }
+
+ /// Start an asynchronous send.
+ template <typename Const_Buffers, typename Handler>
+ void async_send(implementation_type& impl, const Const_Buffers& buffers,
+ socket_base::message_flags flags, Handler handler)
+ {
+ service_impl_.async_send(impl, buffers, flags, handler);
+ }
+
+ /// Send a datagram to the specified endpoint.
+ template <typename Const_Buffers, typename Error_Handler>
+ std::size_t send_to(implementation_type& impl, const Const_Buffers& buffers,
+ const endpoint_type& destination, socket_base::message_flags flags,
+ Error_Handler error_handler)
+ {
+ return service_impl_.send_to(impl, buffers, destination, flags,
+ error_handler);
+ }
+
+ /// Start an asynchronous send.
+ template <typename Const_Buffers, typename Handler>
+ void async_send_to(implementation_type& impl, const Const_Buffers& buffers,
+ const endpoint_type& destination, socket_base::message_flags flags,
+ Handler handler)
+ {
+ service_impl_.async_send_to(impl, buffers, destination, flags, handler);
+ }
+
+ /// Receive some data from the peer.
+ template <typename Mutable_Buffers, typename Error_Handler>
+ std::size_t receive(implementation_type& impl, const Mutable_Buffers& buffers,
+ socket_base::message_flags flags, Error_Handler error_handler)
+ {
+ return service_impl_.receive(impl, buffers, flags, error_handler);
+ }
+
+ /// Start an asynchronous receive.
+ template <typename Mutable_Buffers, typename Handler>
+ void async_receive(implementation_type& impl, const Mutable_Buffers& buffers,
+ socket_base::message_flags flags, Handler handler)
+ {
+ service_impl_.async_receive(impl, buffers, flags, handler);
+ }
+
+ /// Receive a datagram with the endpoint of the sender.
+ template <typename Mutable_Buffers, typename Error_Handler>
+ std::size_t receive_from(implementation_type& impl,
+ const Mutable_Buffers& buffers, endpoint_type& sender_endpoint,
+ socket_base::message_flags flags, Error_Handler error_handler)
+ {
+ return service_impl_.receive_from(impl, buffers, sender_endpoint, flags,
+ error_handler);
+ }
+
+ /// Start an asynchronous receive that will get the endpoint of the sender.
+ template <typename Mutable_Buffers, typename Handler>
+ void async_receive_from(implementation_type& impl,
+ const Mutable_Buffers& buffers, endpoint_type& sender_endpoint,
+ socket_base::message_flags flags, Handler handler)
+ {
+ service_impl_.async_receive_from(impl, buffers, sender_endpoint, flags,
+ handler);
+ }
+
+private:
+ // The service that provides the platform-specific implementation.
+ service_impl_type& service_impl_;
+};
+
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_DATAGRAM_SOCKET_SERVICE_HPP
diff --git a/library/include/libtorrent/asio/deadline_timer.hpp b/library/include/libtorrent/asio/deadline_timer.hpp
new file mode 100644
index 000000000..7deafa60b
--- /dev/null
+++ b/library/include/libtorrent/asio/deadline_timer.hpp
@@ -0,0 +1,37 @@
+//
+// deadline_timer.hpp
+// ~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DEADLINE_TIMER_HPP
+#define ASIO_DEADLINE_TIMER_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/detail/socket_types.hpp" // Must come before posix_time.
+
+#include "asio/detail/push_options.hpp"
+#include <boost/date_time/posix_time/posix_time_types.hpp>
+#include "asio/detail/pop_options.hpp"
+
+#include "asio/basic_deadline_timer.hpp"
+
+namespace asio {
+
+/// Typedef for the typical usage of timer.
+typedef basic_deadline_timer<boost::posix_time::ptime> deadline_timer;
+
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_DEADLINE_TIMER_HPP
diff --git a/library/include/libtorrent/asio/deadline_timer_service.hpp b/library/include/libtorrent/asio/deadline_timer_service.hpp
new file mode 100644
index 000000000..2415d9be6
--- /dev/null
+++ b/library/include/libtorrent/asio/deadline_timer_service.hpp
@@ -0,0 +1,152 @@
+//
+// deadline_timer_service.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DEADLINE_TIMER_SERVICE_HPP
+#define ASIO_DEADLINE_TIMER_SERVICE_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/detail/push_options.hpp"
+#include <cstddef>
+#include <boost/config.hpp>
+#include "asio/detail/pop_options.hpp"
+
+#include "asio/io_service.hpp"
+#include "asio/time_traits.hpp"
+#include "asio/detail/deadline_timer_service.hpp"
+#include "asio/detail/epoll_reactor.hpp"
+#include "asio/detail/kqueue_reactor.hpp"
+#include "asio/detail/select_reactor.hpp"
+
+namespace asio {
+
+/// Default service implementation for a timer.
+template <typename Time_Type,
+ typename Time_Traits = asio::time_traits<Time_Type> >
+class deadline_timer_service
+ : public asio::io_service::service
+{
+public:
+ /// The time traits type.
+ typedef Time_Traits traits_type;
+
+ /// The time type.
+ typedef typename traits_type::time_type time_type;
+
+ /// The duration type.
+ typedef typename traits_type::duration_type duration_type;
+
+private:
+ // The type of the platform-specific implementation.
+#if defined(ASIO_HAS_IOCP)
+ typedef detail::deadline_timer_service<
+ traits_type, detail::select_reactor<true> > service_impl_type;
+#elif defined(ASIO_HAS_EPOLL)
+ typedef detail::deadline_timer_service<
+ traits_type, detail::epoll_reactor<false> > service_impl_type;
+#elif defined(ASIO_HAS_KQUEUE)
+ typedef detail::deadline_timer_service<
+ traits_type, detail::kqueue_reactor<false> > service_impl_type;
+#else
+ typedef detail::deadline_timer_service<
+ traits_type, detail::select_reactor<false> > service_impl_type;
+#endif
+
+public:
+ /// The implementation type of the deadline timer.
+#if defined(GENERATING_DOCUMENTATION)
+ typedef implementation_defined implementation_type;
+#else
+ typedef typename service_impl_type::implementation_type implementation_type;
+#endif
+
+ /// Construct a new timer service for the specified io_service.
+ explicit deadline_timer_service(asio::io_service& io_service)
+ : asio::io_service::service(io_service),
+ service_impl_(asio::use_service<service_impl_type>(io_service))
+ {
+ }
+
+ /// Destroy all user-defined handler objects owned by the service.
+ void shutdown_service()
+ {
+ }
+
+ /// Construct a new timer implementation.
+ void construct(implementation_type& impl)
+ {
+ service_impl_.construct(impl);
+ }
+
+ /// Destroy a timer implementation.
+ void destroy(implementation_type& impl)
+ {
+ service_impl_.destroy(impl);
+ }
+
+ /// Cancel any asynchronous wait operations associated with the timer.
+ std::size_t cancel(implementation_type& impl)
+ {
+ return service_impl_.cancel(impl);
+ }
+
+ /// Get the expiry time for the timer as an absolute time.
+ time_type expires_at(const implementation_type& impl) const
+ {
+ return service_impl_.expires_at(impl);
+ }
+
+ /// Set the expiry time for the timer as an absolute time.
+ std::size_t expires_at(implementation_type& impl,
+ const time_type& expiry_time)
+ {
+ return service_impl_.expires_at(impl, expiry_time);
+ }
+
+ /// Get the expiry time for the timer relative to now.
+ duration_type expires_from_now(const implementation_type& impl) const
+ {
+ return service_impl_.expires_from_now(impl);
+ }
+
+ /// Set the expiry time for the timer relative to now.
+ std::size_t expires_from_now(implementation_type& impl,
+ const duration_type& expiry_time)
+ {
+ return service_impl_.expires_from_now(impl, expiry_time);
+ }
+
+ // Perform a blocking wait on the timer.
+ void wait(implementation_type& impl)
+ {
+ service_impl_.wait(impl);
+ }
+
+ // Start an asynchronous wait on the timer.
+ template <typename Handler>
+ void async_wait(implementation_type& impl, Handler handler)
+ {
+ service_impl_.async_wait(impl, handler);
+ }
+
+private:
+ // The service that provides the platform-specific implementation.
+ service_impl_type& service_impl_;
+};
+
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_DEADLINE_TIMER_SERVICE_HPP
diff --git a/library/include/libtorrent/asio/detail/bind_handler.hpp b/library/include/libtorrent/asio/detail/bind_handler.hpp
new file mode 100644
index 000000000..59c2bcef2
--- /dev/null
+++ b/library/include/libtorrent/asio/detail/bind_handler.hpp
@@ -0,0 +1,349 @@
+//
+// bind_handler.hpp
+// ~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_BIND_HANDLER_HPP
+#define ASIO_DETAIL_BIND_HANDLER_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/detail/handler_alloc_helpers.hpp"
+#include "asio/detail/handler_invoke_helpers.hpp"
+
+namespace asio {
+namespace detail {
+
+template <typename Handler, typename Arg1>
+class binder1
+{
+public:
+ binder1(const Handler& handler, const Arg1& arg1)
+ : handler_(handler),
+ arg1_(arg1)
+ {
+ }
+
+ void operator()()
+ {
+ handler_(arg1_);
+ }
+
+ void operator()() const
+ {
+ handler_(arg1_);
+ }
+
+//private:
+ Handler handler_;
+ Arg1 arg1_;
+};
+
+template <typename Handler, typename Arg1>
+inline void* asio_handler_allocate(std::size_t size,
+ binder1<Handler, Arg1>* this_handler)
+{
+ return asio_handler_alloc_helpers::allocate(
+ size, &this_handler->handler_);
+}
+
+template <typename Handler, typename Arg1>
+inline void asio_handler_deallocate(void* pointer, std::size_t size,
+ binder1<Handler, Arg1>* this_handler)
+{
+ asio_handler_alloc_helpers::deallocate(
+ pointer, size, &this_handler->handler_);
+}
+
+template <typename Function, typename Handler, typename Arg1>
+inline void asio_handler_invoke(const Function& function,
+ binder1<Handler, Arg1>* this_handler)
+{
+ asio_handler_invoke_helpers::invoke(
+ function, &this_handler->handler_);
+}
+
+template <typename Handler, typename Arg1>
+inline binder1<Handler, Arg1> bind_handler(const Handler& handler,
+ const Arg1& arg1)
+{
+ return binder1<Handler, Arg1>(handler, arg1);
+}
+
+template <typename Handler, typename Arg1, typename Arg2>
+class binder2
+{
+public:
+ binder2(const Handler& handler, const Arg1& arg1, const Arg2& arg2)
+ : handler_(handler),
+ arg1_(arg1),
+ arg2_(arg2)
+ {
+ }
+
+ void operator()()
+ {
+ handler_(arg1_, arg2_);
+ }
+
+ void operator()() const
+ {
+ handler_(arg1_, arg2_);
+ }
+
+//private:
+ Handler handler_;
+ Arg1 arg1_;
+ Arg2 arg2_;
+};
+
+template <typename Handler, typename Arg1, typename Arg2>
+inline void* asio_handler_allocate(std::size_t size,
+ binder2<Handler, Arg1, Arg2>* this_handler)
+{
+ return asio_handler_alloc_helpers::allocate(
+ size, &this_handler->handler_);
+}
+
+template <typename Handler, typename Arg1, typename Arg2>
+inline void asio_handler_deallocate(void* pointer, std::size_t size,
+ binder2<Handler, Arg1, Arg2>* this_handler)
+{
+ asio_handler_alloc_helpers::deallocate(
+ pointer, size, &this_handler->handler_);
+}
+
+template <typename Function, typename Handler, typename Arg1, typename Arg2>
+inline void asio_handler_invoke(const Function& function,
+ binder2<Handler, Arg1, Arg2>* this_handler)
+{
+ asio_handler_invoke_helpers::invoke(
+ function, &this_handler->handler_);
+}
+
+template <typename Handler, typename Arg1, typename Arg2>
+inline binder2<Handler, Arg1, Arg2> bind_handler(const Handler& handler,
+ const Arg1& arg1, const Arg2& arg2)
+{
+ return binder2<Handler, Arg1, Arg2>(handler, arg1, arg2);
+}
+
+template <typename Handler, typename Arg1, typename Arg2, typename Arg3>
+class binder3
+{
+public:
+ binder3(const Handler& handler, const Arg1& arg1, const Arg2& arg2,
+ const Arg3& arg3)
+ : handler_(handler),
+ arg1_(arg1),
+ arg2_(arg2),
+ arg3_(arg3)
+ {
+ }
+
+ void operator()()
+ {
+ handler_(arg1_, arg2_, arg3_);
+ }
+
+ void operator()() const
+ {
+ handler_(arg1_, arg2_, arg3_);
+ }
+
+//private:
+ Handler handler_;
+ Arg1 arg1_;
+ Arg2 arg2_;
+ Arg3 arg3_;
+};
+
+template <typename Handler, typename Arg1, typename Arg2, typename Arg3>
+inline void* asio_handler_allocate(std::size_t size,
+ binder3<Handler, Arg1, Arg2, Arg3>* this_handler)
+{
+ return asio_handler_alloc_helpers::allocate(
+ size, &this_handler->handler_);
+}
+
+template <typename Handler, typename Arg1, typename Arg2, typename Arg3>
+inline void asio_handler_deallocate(void* pointer, std::size_t size,
+ binder3<Handler, Arg1, Arg2, Arg3>* this_handler)
+{
+ asio_handler_alloc_helpers::deallocate(
+ pointer, size, &this_handler->handler_);
+}
+
+template <typename Function, typename Handler, typename Arg1, typename Arg2,
+ typename Arg3>
+inline void asio_handler_invoke(const Function& function,
+ binder3<Handler, Arg1, Arg2, Arg3>* this_handler)
+{
+ asio_handler_invoke_helpers::invoke(
+ function, &this_handler->handler_);
+}
+
+template <typename Handler, typename Arg1, typename Arg2, typename Arg3>
+inline binder3<Handler, Arg1, Arg2, Arg3> bind_handler(const Handler& handler,
+ const Arg1& arg1, const Arg2& arg2, const Arg3& arg3)
+{
+ return binder3<Handler, Arg1, Arg2, Arg3>(handler, arg1, arg2, arg3);
+}
+
+template <typename Handler, typename Arg1, typename Arg2, typename Arg3,
+ typename Arg4>
+class binder4
+{
+public:
+ binder4(const Handler& handler, const Arg1& arg1, const Arg2& arg2,
+ const Arg3& arg3, const Arg4& arg4)
+ : handler_(handler),
+ arg1_(arg1),
+ arg2_(arg2),
+ arg3_(arg3),
+ arg4_(arg4)
+ {
+ }
+
+ void operator()()
+ {
+ handler_(arg1_, arg2_, arg3_, arg4_);
+ }
+
+ void operator()() const
+ {
+ handler_(arg1_, arg2_, arg3_, arg4_);
+ }
+
+//private:
+ Handler handler_;
+ Arg1 arg1_;
+ Arg2 arg2_;
+ Arg3 arg3_;
+ Arg4 arg4_;
+};
+
+template <typename Handler, typename Arg1, typename Arg2, typename Arg3,
+ typename Arg4>
+inline void* asio_handler_allocate(std::size_t size,
+ binder4<Handler, Arg1, Arg2, Arg3, Arg4>* this_handler)
+{
+ return asio_handler_alloc_helpers::allocate(
+ size, &this_handler->handler_);
+}
+
+template <typename Handler, typename Arg1, typename Arg2, typename Arg3,
+ typename Arg4>
+inline void asio_handler_deallocate(void* pointer, std::size_t size,
+ binder4<Handler, Arg1, Arg2, Arg3, Arg4>* this_handler)
+{
+ asio_handler_alloc_helpers::deallocate(
+ pointer, size, &this_handler->handler_);
+}
+
+template <typename Function, typename Handler, typename Arg1, typename Arg2,
+ typename Arg3, typename Arg4>
+inline void asio_handler_invoke(const Function& function,
+ binder4<Handler, Arg1, Arg2, Arg3, Arg4>* this_handler)
+{
+ asio_handler_invoke_helpers::invoke(
+ function, &this_handler->handler_);
+}
+
+template <typename Handler, typename Arg1, typename Arg2, typename Arg3,
+ typename Arg4>
+inline binder4<Handler, Arg1, Arg2, Arg3, Arg4> bind_handler(
+ const Handler& handler, const Arg1& arg1, const Arg2& arg2,
+ const Arg3& arg3, const Arg4& arg4)
+{
+ return binder4<Handler, Arg1, Arg2, Arg3, Arg4>(handler, arg1, arg2, arg3,
+ arg4);
+}
+
+template <typename Handler, typename Arg1, typename Arg2, typename Arg3,
+ typename Arg4, typename Arg5>
+class binder5
+{
+public:
+ binder5(const Handler& handler, const Arg1& arg1, const Arg2& arg2,
+ const Arg3& arg3, const Arg4& arg4, const Arg5& arg5)
+ : handler_(handler),
+ arg1_(arg1),
+ arg2_(arg2),
+ arg3_(arg3),
+ arg4_(arg4),
+ arg5_(arg5)
+ {
+ }
+
+ void operator()()
+ {
+ handler_(arg1_, arg2_, arg3_, arg4_, arg5_);
+ }
+
+ void operator()() const
+ {
+ handler_(arg1_, arg2_, arg3_, arg4_, arg5_);
+ }
+
+//private:
+ Handler handler_;
+ Arg1 arg1_;
+ Arg2 arg2_;
+ Arg3 arg3_;
+ Arg4 arg4_;
+ Arg5 arg5_;
+};
+
+template <typename Handler, typename Arg1, typename Arg2, typename Arg3,
+ typename Arg4, typename Arg5>
+inline void* asio_handler_allocate(std::size_t size,
+ binder5<Handler, Arg1, Arg2, Arg3, Arg4, Arg5>* this_handler)
+{
+ return asio_handler_alloc_helpers::allocate(
+ size, &this_handler->handler_);
+}
+
+template <typename Handler, typename Arg1, typename Arg2, typename Arg3,
+ typename Arg4, typename Arg5>
+inline void asio_handler_deallocate(void* pointer, std::size_t size,
+ binder5<Handler, Arg1, Arg2, Arg3, Arg4, Arg5>* this_handler)
+{
+ asio_handler_alloc_helpers::deallocate(
+ pointer, size, &this_handler->handler_);
+}
+
+template <typename Function, typename Handler, typename Arg1, typename Arg2,
+ typename Arg3, typename Arg4, typename Arg5>
+inline void asio_handler_invoke(const Function& function,
+ binder5<Handler, Arg1, Arg2, Arg3, Arg4, Arg5>* this_handler)
+{
+ asio_handler_invoke_helpers::invoke(
+ function, &this_handler->handler_);
+}
+
+template <typename Handler, typename Arg1, typename Arg2, typename Arg3,
+ typename Arg4, typename Arg5>
+inline binder5<Handler, Arg1, Arg2, Arg3, Arg4, Arg5> bind_handler(
+ const Handler& handler, const Arg1& arg1, const Arg2& arg2,
+ const Arg3& arg3, const Arg4& arg4, const Arg5& arg5)
+{
+ return binder5<Handler, Arg1, Arg2, Arg3, Arg4, Arg5>(handler, arg1, arg2,
+ arg3, arg4, arg5);
+}
+
+} // namespace detail
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_DETAIL_BIND_HANDLER_HPP
diff --git a/library/include/libtorrent/asio/detail/buffer_resize_guard.hpp b/library/include/libtorrent/asio/detail/buffer_resize_guard.hpp
new file mode 100644
index 000000000..d72753010
--- /dev/null
+++ b/library/include/libtorrent/asio/detail/buffer_resize_guard.hpp
@@ -0,0 +1,70 @@
+//
+// buffer_resize_guard.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_BUFFER_RESIZE_GUARD_HPP
+#define ASIO_DETAIL_BUFFER_RESIZE_GUARD_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/detail/push_options.hpp"
+#include <limits>
+#include <boost/config.hpp>
+#include "asio/detail/pop_options.hpp"
+
+namespace asio {
+namespace detail {
+
+// Helper class to manage buffer resizing in an exception safe way.
+template <typename Buffer>
+class buffer_resize_guard
+{
+public:
+ // Constructor.
+ buffer_resize_guard(Buffer& buffer)
+ : buffer_(buffer),
+ old_size_(buffer.size())
+ {
+ }
+
+ // Destructor rolls back the buffer resize unless commit was called.
+ ~buffer_resize_guard()
+ {
+ if (old_size_
+ != std::numeric_limits<size_t>::max BOOST_PREVENT_MACRO_SUBSTITUTION())
+ {
+ buffer_.resize(old_size_);
+ }
+ }
+
+ // Commit the resize transaction.
+ void commit()
+ {
+ old_size_
+ = std::numeric_limits<size_t>::max BOOST_PREVENT_MACRO_SUBSTITUTION();
+ }
+
+private:
+ // The buffer being managed.
+ Buffer& buffer_;
+
+ // The size of the buffer at the time the guard was constructed.
+ size_t old_size_;
+};
+
+} // namespace detail
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_DETAIL_BUFFER_RESIZE_GUARD_HPP
diff --git a/library/include/libtorrent/asio/detail/buffered_stream_storage.hpp b/library/include/libtorrent/asio/detail/buffered_stream_storage.hpp
new file mode 100644
index 000000000..17517f9b5
--- /dev/null
+++ b/library/include/libtorrent/asio/detail/buffered_stream_storage.hpp
@@ -0,0 +1,127 @@
+//
+// buffered_stream_storage.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_BUFFERED_STREAM_STORAGE_HPP
+#define ASIO_DETAIL_BUFFERED_STREAM_STORAGE_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/detail/push_options.hpp"
+#include <boost/config.hpp>
+#include <cassert>
+#include <cstddef>
+#include <cstring>
+#include <vector>
+#include "asio/detail/pop_options.hpp"
+
+namespace asio {
+namespace detail {
+
+class buffered_stream_storage
+{
+public:
+ // The type of the bytes stored in the buffer.
+ typedef unsigned char byte_type;
+
+ // The type used for offsets into the buffer.
+ typedef std::size_t size_type;
+
+ // Constructor.
+ explicit buffered_stream_storage(std::size_t capacity)
+ : begin_offset_(0),
+ end_offset_(0),
+ buffer_(capacity)
+ {
+ }
+
+ /// Clear the buffer.
+ void clear()
+ {
+ begin_offset_ = 0;
+ end_offset_ = 0;
+ }
+
+ // Return a pointer to the beginning of the unread data.
+ byte_type* data()
+ {
+ return &buffer_[0] + begin_offset_;
+ }
+
+ // Return a pointer to the beginning of the unread data.
+ const byte_type* data() const
+ {
+ return &buffer_[0] + begin_offset_;
+ }
+
+ // Is there no unread data in the buffer.
+ bool empty() const
+ {
+ return begin_offset_ == end_offset_;
+ }
+
+ // Return the amount of unread data the is in the buffer.
+ size_type size() const
+ {
+ return end_offset_ - begin_offset_;
+ }
+
+ // Resize the buffer to the specified length.
+ void resize(size_type length)
+ {
+ assert(length <= capacity());
+ if (begin_offset_ + length <= capacity())
+ {
+ end_offset_ = begin_offset_ + length;
+ }
+ else
+ {
+ using namespace std; // For memmove.
+ memmove(&buffer_[0], &buffer_[0] + begin_offset_, size());
+ end_offset_ = length;
+ begin_offset_ = 0;
+ }
+ }
+
+ // Return the maximum size for data in the buffer.
+ size_type capacity() const
+ {
+ return buffer_.size();
+ }
+
+ // Consume multiple bytes from the beginning of the buffer.
+ void consume(size_type count)
+ {
+ assert(begin_offset_ + count <= end_offset_);
+ begin_offset_ += count;
+ if (empty())
+ clear();
+ }
+
+private:
+ // The offset to the beginning of the unread data.
+ size_type begin_offset_;
+
+ // The offset to the end of the unread data.
+ size_type end_offset_;
+
+ // The data in the buffer.
+ std::vector<byte_type> buffer_;
+};
+
+} // namespace detail
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_DETAIL_BUFFERED_STREAM_STORAGE_HPP
diff --git a/library/include/libtorrent/asio/detail/call_stack.hpp b/library/include/libtorrent/asio/detail/call_stack.hpp
new file mode 100644
index 000000000..37256ee59
--- /dev/null
+++ b/library/include/libtorrent/asio/detail/call_stack.hpp
@@ -0,0 +1,90 @@
+//
+// call_stack.hpp
+// ~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_CALL_STACK_HPP
+#define ASIO_DETAIL_CALL_STACK_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/detail/noncopyable.hpp"
+#include "asio/detail/tss_ptr.hpp"
+
+namespace asio {
+namespace detail {
+
+// Helper class to determine whether or not the current thread is inside an
+// invocation of io_service::run() for a specified io_service object.
+template <typename Owner>
+class call_stack
+{
+public:
+ // Context class automatically pushes an owner on to the stack.
+ class context
+ : private noncopyable
+ {
+ public:
+ // Push the owner on to the stack.
+ explicit context(Owner* d)
+ : owner_(d),
+ next_(call_stack<Owner>::top_)
+ {
+ call_stack<Owner>::top_ = this;
+ }
+
+ // Pop the owner from the stack.
+ ~context()
+ {
+ call_stack<Owner>::top_ = next_;
+ }
+
+ private:
+ friend class call_stack<Owner>;
+
+ // The owner associated with the context.
+ Owner* owner_;
+
+ // The next element in the stack.
+ context* next_;
+ };
+
+ friend class context;
+
+ // Determine whether the specified owner is on the stack.
+ static bool contains(Owner* d)
+ {
+ context* elem = top_;
+ while (elem)
+ {
+ if (elem->owner_ == d)
+ return true;
+ elem = elem->next_;
+ }
+ return false;
+ }
+
+private:
+ // The top of the stack of calls for the current thread.
+ static tss_ptr<context> top_;
+};
+
+template <typename Owner>
+tss_ptr<typename call_stack<Owner>::context>
+call_stack<Owner>::top_;
+
+} // namespace detail
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_DETAIL_CALL_STACK_HPP
diff --git a/library/include/libtorrent/asio/detail/const_buffers_iterator.hpp b/library/include/libtorrent/asio/detail/const_buffers_iterator.hpp
new file mode 100644
index 000000000..f48d2df85
--- /dev/null
+++ b/library/include/libtorrent/asio/detail/const_buffers_iterator.hpp
@@ -0,0 +1,150 @@
+//
+// const_buffers_iterator.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_CONST_BUFFERS_ITERATOR_HPP
+#define ASIO_DETAIL_CONST_BUFFERS_ITERATOR_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/detail/push_options.hpp"
+#include <cstddef>
+#include <boost/config.hpp>
+#include <boost/iterator/iterator_facade.hpp>
+#include "asio/detail/pop_options.hpp"
+
+#include "asio/buffer.hpp"
+
+namespace asio {
+namespace detail {
+
+// A proxy iterator for a sub-range in a list of buffers.
+template <typename Const_Buffers>
+class const_buffers_iterator
+ : public boost::iterator_facade<const_buffers_iterator<Const_Buffers>,
+ const char, boost::bidirectional_traversal_tag>
+{
+public:
+ // Default constructor creates an iterator in an undefined state.
+ const_buffers_iterator()
+ {
+ }
+
+ // Create an iterator for the specified position.
+ const_buffers_iterator(const Const_Buffers& buffers, std::size_t position)
+ : begin_(buffers.begin()),
+ current_(buffers.begin()),
+ end_(buffers.end()),
+ position_(0)
+ {
+ while (current_ != end_)
+ {
+ current_buffer_ = *current_;
+ std::size_t buffer_size = asio::buffer_size(current_buffer_);
+ if (position - position_ < buffer_size)
+ {
+ current_buffer_position_ = position - position_;
+ position_ = position;
+ return;
+ }
+ position_ += buffer_size;
+ ++current_;
+ }
+ current_buffer_ = asio::const_buffer();
+ current_buffer_position_ = 0;
+ }
+
+ std::size_t position() const
+ {
+ return position_;
+ }
+
+private:
+ friend class boost::iterator_core_access;
+
+ void increment()
+ {
+ if (current_ == end_)
+ return;
+
+ ++position_;
+
+ ++current_buffer_position_;
+ if (current_buffer_position_ != asio::buffer_size(current_buffer_))
+ return;
+
+ ++current_;
+ current_buffer_position_ = 0;
+ while (current_ != end_)
+ {
+ current_buffer_ = *current_;
+ if (asio::buffer_size(current_buffer_) > 0)
+ return;
+ ++current_;
+ }
+ }
+
+ void decrement()
+ {
+ if (position_ == 0)
+ return;
+
+ --position_;
+
+ if (current_buffer_position_ != 0)
+ {
+ --current_buffer_position_;
+ return;
+ }
+
+ typename Const_Buffers::const_iterator iter = current_;
+ while (iter != begin_)
+ {
+ --iter;
+ asio::const_buffer buffer = *iter;
+ std::size_t buffer_size = asio::buffer_size(buffer);
+ if (buffer_size > 0)
+ {
+ current_ = iter;
+ current_buffer_ = buffer;
+ current_buffer_position_ = buffer_size - 1;
+ return;
+ }
+ }
+ }
+
+ bool equal(const const_buffers_iterator& other) const
+ {
+ return position_ == other.position_;
+ }
+
+ const char& dereference() const
+ {
+ return asio::buffer_cast<const char*>(
+ current_buffer_)[current_buffer_position_];
+ }
+
+ asio::const_buffer current_buffer_;
+ std::size_t current_buffer_position_;
+ typename Const_Buffers::const_iterator begin_;
+ typename Const_Buffers::const_iterator current_;
+ typename Const_Buffers::const_iterator end_;
+ std::size_t position_;
+};
+
+} // namespace detail
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_DETAIL_CONST_BUFFERS_ITERATOR_HPP
diff --git a/library/include/libtorrent/asio/detail/consuming_buffers.hpp b/library/include/libtorrent/asio/detail/consuming_buffers.hpp
new file mode 100644
index 000000000..7877f95f2
--- /dev/null
+++ b/library/include/libtorrent/asio/detail/consuming_buffers.hpp
@@ -0,0 +1,196 @@
+//
+// consuming_buffers.hpp
+// ~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_CONSUMING_BUFFERS_HPP
+#define ASIO_DETAIL_CONSUMING_BUFFERS_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/detail/push_options.hpp"
+#include <algorithm>
+#include <cstddef>
+#include <boost/config.hpp>
+#include <boost/iterator/iterator_facade.hpp>
+#include "asio/detail/pop_options.hpp"
+
+namespace asio {
+namespace detail {
+
+// A proxy iterator for a sub-range in a list of buffers.
+template <typename Buffer, typename Buffer_Iterator>
+class consuming_buffers_iterator
+ : public boost::iterator_facade<
+ consuming_buffers_iterator<Buffer, Buffer_Iterator>,
+ const Buffer,
+ boost::forward_traversal_tag>
+{
+public:
+ // Default constructor creates an end iterator.
+ consuming_buffers_iterator()
+ : at_end_(true)
+ {
+ }
+
+ // Construct with a buffer for the first entry and an iterator
+ // range for the remaining entries.
+ consuming_buffers_iterator(bool at_end, const Buffer& first,
+ Buffer_Iterator begin_remainder, Buffer_Iterator end_remainder)
+ : at_end_(at_end),
+ first_(first),
+ begin_remainder_(begin_remainder),
+ end_remainder_(end_remainder)
+ {
+ }
+
+private:
+ friend class boost::iterator_core_access;
+
+ void increment()
+ {
+ if (!at_end_)
+ {
+ if (begin_remainder_ == end_remainder_)
+ at_end_ = true;
+ else
+ first_ = *begin_remainder_++;
+ }
+ }
+
+ bool equal(const consuming_buffers_iterator& other) const
+ {
+ if (at_end_ && other.at_end_)
+ return true;
+ return !at_end_ && !other.at_end_
+ && buffer_cast<const void*>(first_)
+ == buffer_cast<const void*>(other.first_)
+ && buffer_size(first_) == buffer_size(other.first_)
+ && begin_remainder_ == other.begin_remainder_
+ && end_remainder_ == other.end_remainder_;
+ }
+
+ const Buffer& dereference() const
+ {
+ return first_;
+ }
+
+ bool at_end_;
+ Buffer first_;
+ Buffer_Iterator begin_remainder_;
+ Buffer_Iterator end_remainder_;
+};
+
+// A proxy for a sub-range in a list of buffers.
+template <typename Buffer, typename Buffers>
+class consuming_buffers
+{
+public:
+ // The type for each element in the list of buffers.
+ typedef Buffer value_type;
+
+ // A forward-only iterator type that may be used to read elements.
+ typedef consuming_buffers_iterator<Buffer, typename Buffers::const_iterator>
+ const_iterator;
+
+ // Construct to represent the entire list of buffers.
+ consuming_buffers(const Buffers& buffers)
+ : buffers_(buffers),
+ at_end_(buffers_.begin() == buffers_.end()),
+ first_(*buffers_.begin()),
+ begin_remainder_(buffers_.begin())
+ {
+ if (!at_end_)
+ ++begin_remainder_;
+ }
+
+ // Copy constructor.
+ consuming_buffers(const consuming_buffers& other)
+ : buffers_(other.buffers_),
+ at_end_(other.at_end_),
+ first_(other.first_),
+ begin_remainder_(buffers_.begin())
+ {
+ typename Buffers::const_iterator first = other.buffers_.begin();
+ typename Buffers::const_iterator second = other.begin_remainder_;
+ std::advance(begin_remainder_, std::distance(first, second));
+ }
+
+ // Assignment operator.
+ consuming_buffers& operator=(const consuming_buffers& other)
+ {
+ buffers_ = other.buffers_;
+ at_end_ = other.at_end_;
+ first_ = other.first_;
+ begin_remainder_ = buffers_.begin();
+ typename Buffers::const_iterator first = other.buffers_.begin();
+ typename Buffers::const_iterator second = other.begin_remainder_;
+ std::advance(begin_remainder_, std::distance(first, second));
+ return *this;
+ }
+
+ // Get a forward-only iterator to the first element.
+ const_iterator begin() const
+ {
+ return const_iterator(at_end_, first_, begin_remainder_, buffers_.end());
+ }
+
+ // Get a forward-only iterator for one past the last element.
+ const_iterator end() const
+ {
+ return const_iterator();
+ }
+
+ // Consume the specified number of bytes from the buffers.
+ void consume(std::size_t size)
+ {
+ // Remove buffers from the start until the specified size is reached.
+ while (size > 0 && !at_end_)
+ {
+ if (buffer_size(first_) <= size)
+ {
+ size -= buffer_size(first_);
+ if (begin_remainder_ == buffers_.end())
+ at_end_ = true;
+ else
+ first_ = *begin_remainder_++;
+ }
+ else
+ {
+ first_ = first_ + size;
+ size = 0;
+ }
+ }
+
+ // Remove any more empty buffers at the start.
+ while (!at_end_ && buffer_size(first_) == 0)
+ {
+ if (begin_remainder_ == buffers_.end())
+ at_end_ = true;
+ else
+ first_ = *begin_remainder_++;
+ }
+ }
+
+private:
+ Buffers buffers_;
+ bool at_end_;
+ Buffer first_;
+ typename Buffers::const_iterator begin_remainder_;
+};
+
+} // namespace detail
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_DETAIL_CONSUMING_BUFFERS_HPP
diff --git a/library/include/libtorrent/asio/detail/deadline_timer_service.hpp b/library/include/libtorrent/asio/detail/deadline_timer_service.hpp
new file mode 100644
index 000000000..c5d15a5df
--- /dev/null
+++ b/library/include/libtorrent/asio/detail/deadline_timer_service.hpp
@@ -0,0 +1,182 @@
+//
+// deadline_timer_service.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_DEADLINE_TIMER_SERVICE_HPP
+#define ASIO_DETAIL_DEADLINE_TIMER_SERVICE_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/detail/push_options.hpp"
+#include <cstddef>
+#include <boost/config.hpp>
+#include <boost/date_time/posix_time/posix_time_types.hpp>
+#include "asio/detail/pop_options.hpp"
+
+#include "asio/error.hpp"
+#include "asio/io_service.hpp"
+#include "asio/detail/bind_handler.hpp"
+#include "asio/detail/noncopyable.hpp"
+#include "asio/detail/socket_ops.hpp"
+#include "asio/detail/socket_types.hpp"
+#include "asio/detail/timer_queue.hpp"
+
+namespace asio {
+namespace detail {
+
+template <typename Time_Traits, typename Timer_Scheduler>
+class deadline_timer_service
+ : public asio::io_service::service
+{
+public:
+ // The time type.
+ typedef typename Time_Traits::time_type time_type;
+
+ // The duration type.
+ typedef typename Time_Traits::duration_type duration_type;
+
+ // The implementation type of the timer. This type is dependent on the
+ // underlying implementation of the timer service.
+ struct implementation_type
+ : private asio::detail::noncopyable
+ {
+ time_type expiry;
+ bool might_have_pending_waits;
+ };
+
+ // Constructor.
+ deadline_timer_service(asio::io_service& io_service)
+ : asio::io_service::service(io_service),
+ scheduler_(asio::use_service<Timer_Scheduler>(io_service))
+ {
+ scheduler_.add_timer_queue(timer_queue_);
+ }
+
+ // Destroy all user-defined handler objects owned by the service.
+ void shutdown_service()
+ {
+ }
+
+ // Construct a new timer implementation.
+ void construct(implementation_type& impl)
+ {
+ impl.expiry = time_type();
+ impl.might_have_pending_waits = false;
+ }
+
+ // Destroy a timer implementation.
+ void destroy(implementation_type& impl)
+ {
+ cancel(impl);
+ }
+
+ // Cancel any asynchronous wait operations associated with the timer.
+ std::size_t cancel(implementation_type& impl)
+ {
+ if (!impl.might_have_pending_waits)
+ return 0;
+ std::size_t count = scheduler_.cancel_timer(timer_queue_, &impl);
+ impl.might_have_pending_waits = false;
+ return count;
+ }
+
+ // Get the expiry time for the timer as an absolute time.
+ time_type expires_at(const implementation_type& impl) const
+ {
+ return impl.expiry;
+ }
+
+ // Set the expiry time for the timer as an absolute time.
+ std::size_t expires_at(implementation_type& impl,
+ const time_type& expiry_time)
+ {
+ std::size_t count = cancel(impl);
+ impl.expiry = expiry_time;
+ return count;
+ }
+
+ // Get the expiry time for the timer relative to now.
+ duration_type expires_from_now(const implementation_type& impl) const
+ {
+ return Time_Traits::subtract(expires_at(impl), Time_Traits::now());
+ }
+
+ // Set the expiry time for the timer relative to now.
+ std::size_t expires_from_now(implementation_type& impl,
+ const duration_type& expiry_time)
+ {
+ return expires_at(impl, Time_Traits::add(Time_Traits::now(), expiry_time));
+ }
+
+ // Perform a blocking wait on the timer.
+ void wait(implementation_type& impl)
+ {
+ time_type now = Time_Traits::now();
+ while (Time_Traits::less_than(now, impl.expiry))
+ {
+ boost::posix_time::time_duration timeout =
+ Time_Traits::to_posix_duration(Time_Traits::subtract(impl.expiry, now));
+ ::timeval tv;
+ tv.tv_sec = timeout.total_seconds();
+ tv.tv_usec = timeout.total_microseconds() % 1000000;
+ socket_ops::select(0, 0, 0, 0, &tv);
+ now = Time_Traits::now();
+ }
+ }
+
+ template <typename Handler>
+ class wait_handler
+ {
+ public:
+ wait_handler(asio::io_service& io_service, Handler handler)
+ : io_service_(io_service),
+ work_(io_service),
+ handler_(handler)
+ {
+ }
+
+ void operator()(int result)
+ {
+ asio::error e(result);
+ io_service_.post(detail::bind_handler(handler_, e));
+ }
+
+ private:
+ asio::io_service& io_service_;
+ asio::io_service::work work_;
+ Handler handler_;
+ };
+
+ // Start an asynchronous wait on the timer.
+ template <typename Handler>
+ void async_wait(implementation_type& impl, Handler handler)
+ {
+ impl.might_have_pending_waits = true;
+ scheduler_.schedule_timer(timer_queue_, impl.expiry,
+ wait_handler<Handler>(io_service(), handler), &impl);
+ }
+
+private:
+ // The queue of timers.
+ timer_queue<Time_Traits> timer_queue_;
+
+ // The object that schedules and executes timers. Usually a reactor.
+ Timer_Scheduler& scheduler_;
+};
+
+} // namespace detail
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_DETAIL_DEADLINE_TIMER_SERVICE_HPP
diff --git a/library/include/libtorrent/asio/detail/epoll_reactor.hpp b/library/include/libtorrent/asio/detail/epoll_reactor.hpp
new file mode 100644
index 000000000..5f46a93ba
--- /dev/null
+++ b/library/include/libtorrent/asio/detail/epoll_reactor.hpp
@@ -0,0 +1,593 @@
+//
+// epoll_reactor.hpp
+// ~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_EPOLL_REACTOR_HPP
+#define ASIO_DETAIL_EPOLL_REACTOR_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/detail/epoll_reactor_fwd.hpp"
+
+#if defined(ASIO_HAS_EPOLL)
+
+#include "asio/detail/push_options.hpp"
+#include <cstddef>
+#include <vector>
+#include <sys/epoll.h>
+#include <boost/config.hpp>
+#include <boost/date_time/posix_time/posix_time_types.hpp>
+#include <boost/throw_exception.hpp>
+#include "asio/detail/pop_options.hpp"
+
+#include "asio/io_service.hpp"
+#include "asio/system_exception.hpp"
+#include "asio/detail/bind_handler.hpp"
+#include "asio/detail/hash_map.hpp"
+#include "asio/detail/mutex.hpp"
+#include "asio/detail/task_io_service.hpp"
+#include "asio/detail/thread.hpp"
+#include "asio/detail/reactor_op_queue.hpp"
+#include "asio/detail/select_interrupter.hpp"
+#include "asio/detail/signal_blocker.hpp"
+#include "asio/detail/socket_types.hpp"
+#include "asio/detail/timer_queue.hpp"
+
+namespace asio {
+namespace detail {
+
+template <bool Own_Thread>
+class epoll_reactor
+ : public asio::io_service::service
+{
+public:
+ // Constructor.
+ epoll_reactor(asio::io_service& io_service)
+ : asio::io_service::service(io_service),
+ mutex_(),
+ epoll_fd_(do_epoll_create()),
+ wait_in_progress_(false),
+ interrupter_(),
+ read_op_queue_(),
+ write_op_queue_(),
+ except_op_queue_(),
+ pending_cancellations_(),
+ stop_thread_(false),
+ thread_(0),
+ shutdown_(false)
+ {
+ // Start the reactor's internal thread only if needed.
+ if (Own_Thread)
+ {
+ asio::detail::signal_blocker sb;
+ thread_ = new asio::detail::thread(
+ bind_handler(&epoll_reactor::call_run_thread, this));
+ }
+
+ // Add the interrupter's descriptor to epoll.
+ epoll_event ev = { 0, { 0 } };
+ ev.events = EPOLLIN | EPOLLERR;
+ ev.data.fd = interrupter_.read_descriptor();
+ epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, interrupter_.read_descriptor(), &ev);
+ }
+
+ // Destructor.
+ ~epoll_reactor()
+ {
+ shutdown_service();
+ close(epoll_fd_);
+ }
+
+ // Destroy all user-defined handler objects owned by the service.
+ void shutdown_service()
+ {
+ asio::detail::mutex::scoped_lock lock(mutex_);
+ shutdown_ = true;
+ stop_thread_ = true;
+ lock.unlock();
+
+ if (thread_)
+ {
+ interrupter_.interrupt();
+ thread_->join();
+ delete thread_;
+ thread_ = 0;
+ }
+
+ read_op_queue_.destroy_operations();
+ write_op_queue_.destroy_operations();
+ except_op_queue_.destroy_operations();
+
+ for (std::size_t i = 0; i < timer_queues_.size(); ++i)
+ timer_queues_[i]->destroy_timers();
+ timer_queues_.clear();
+ }
+
+ // Register a socket with the reactor. Returns 0 on success, system error
+ // code on failure.
+ int register_descriptor(socket_type descriptor)
+ {
+ // No need to lock according to epoll documentation.
+
+ epoll_event ev = { 0, { 0 } };
+ ev.events = 0;
+ ev.data.fd = descriptor;
+ int result = epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, descriptor, &ev);
+ if (result != 0)
+ return errno;
+ return 0;
+ }
+
+ // Start a new read operation. The handler object will be invoked when the
+ // given descriptor is ready to be read, or an error has occurred.
+ template <typename Handler>
+ void start_read_op(socket_type descriptor, Handler handler)
+ {
+ asio::detail::mutex::scoped_lock lock(mutex_);
+
+ if (shutdown_)
+ return;
+
+ if (!read_op_queue_.has_operation(descriptor))
+ if (handler(0))
+ return;
+
+ if (read_op_queue_.enqueue_operation(descriptor, handler))
+ {
+ epoll_event ev = { 0, { 0 } };
+ ev.events = EPOLLIN | EPOLLERR | EPOLLHUP;
+ if (write_op_queue_.has_operation(descriptor))
+ ev.events |= EPOLLOUT;
+ if (except_op_queue_.has_operation(descriptor))
+ ev.events |= EPOLLPRI;
+ ev.data.fd = descriptor;
+
+ int result = epoll_ctl(epoll_fd_, EPOLL_CTL_MOD, descriptor, &ev);
+ if (result != 0)
+ {
+ int error = errno;
+ read_op_queue_.dispatch_all_operations(descriptor, error);
+ }
+ }
+ }
+
+ // Start a new write operation. The handler object will be invoked when the
+ // given descriptor is ready to be written, or an error has occurred.
+ template <typename Handler>
+ void start_write_op(socket_type descriptor, Handler handler)
+ {
+ asio::detail::mutex::scoped_lock lock(mutex_);
+
+ if (shutdown_)
+ return;
+
+ if (!write_op_queue_.has_operation(descriptor))
+ if (handler(0))
+ return;
+
+ if (write_op_queue_.enqueue_operation(descriptor, handler))
+ {
+ epoll_event ev = { 0, { 0 } };
+ ev.events = EPOLLOUT | EPOLLERR | EPOLLHUP;
+ if (read_op_queue_.has_operation(descriptor))
+ ev.events |= EPOLLIN;
+ if (except_op_queue_.has_operation(descriptor))
+ ev.events |= EPOLLPRI;
+ ev.data.fd = descriptor;
+
+ int result = epoll_ctl(epoll_fd_, EPOLL_CTL_MOD, descriptor, &ev);
+ if (result != 0)
+ {
+ int error = errno;
+ write_op_queue_.dispatch_all_operations(descriptor, error);
+ }
+ }
+ }
+
+ // Start a new exception operation. The handler object will be invoked when
+ // the given descriptor has exception information, or an error has occurred.
+ template <typename Handler>
+ void start_except_op(socket_type descriptor, Handler handler)
+ {
+ asio::detail::mutex::scoped_lock lock(mutex_);
+
+ if (shutdown_)
+ return;
+
+ if (except_op_queue_.enqueue_operation(descriptor, handler))
+ {
+ epoll_event ev = { 0, { 0 } };
+ ev.events = EPOLLPRI | EPOLLERR | EPOLLHUP;
+ if (read_op_queue_.has_operation(descriptor))
+ ev.events |= EPOLLIN;
+ if (write_op_queue_.has_operation(descriptor))
+ ev.events |= EPOLLOUT;
+ ev.data.fd = descriptor;
+
+ int result = epoll_ctl(epoll_fd_, EPOLL_CTL_MOD, descriptor, &ev);
+ if (result != 0)
+ {
+ int error = errno;
+ except_op_queue_.dispatch_all_operations(descriptor, error);
+ }
+ }
+ }
+
+ // Start new write and exception operations. The handler object will be
+ // invoked when the given descriptor is ready for writing or has exception
+ // information available, or an error has occurred.
+ template <typename Handler>
+ void start_write_and_except_ops(socket_type descriptor, Handler handler)
+ {
+ asio::detail::mutex::scoped_lock lock(mutex_);
+
+ if (shutdown_)
+ return;
+
+ bool need_mod = write_op_queue_.enqueue_operation(descriptor, handler);
+ need_mod = except_op_queue_.enqueue_operation(descriptor, handler)
+ && need_mod;
+ if (need_mod)
+ {
+ epoll_event ev = { 0, { 0 } };
+ ev.events = EPOLLOUT | EPOLLPRI | EPOLLERR | EPOLLHUP;
+ if (read_op_queue_.has_operation(descriptor))
+ ev.events |= EPOLLIN;
+ ev.data.fd = descriptor;
+
+ int result = epoll_ctl(epoll_fd_, EPOLL_CTL_MOD, descriptor, &ev);
+ if (result != 0)
+ {
+ int error = errno;
+ write_op_queue_.dispatch_all_operations(descriptor, error);
+ except_op_queue_.dispatch_all_operations(descriptor, error);
+ }
+ }
+ }
+
+ // Cancel all operations associated with the given descriptor. The
+ // handlers associated with the descriptor will be invoked with the
+ // operation_aborted error.
+ void cancel_ops(socket_type descriptor)
+ {
+ asio::detail::mutex::scoped_lock lock(mutex_);
+ cancel_ops_unlocked(descriptor);
+ }
+
+ // Enqueue cancellation of all operations associated with the given
+ // descriptor. The handlers associated with the descriptor will be invoked
+ // with the operation_aborted error. This function does not acquire the
+ // epoll_reactor's mutex, and so should only be used from within a reactor
+ // handler.
+ void enqueue_cancel_ops_unlocked(socket_type descriptor)
+ {
+ pending_cancellations_.push_back(descriptor);
+ }
+
+ // Cancel any operations that are running against the descriptor and remove
+ // its registration from the reactor.
+ void close_descriptor(socket_type descriptor)
+ {
+ asio::detail::mutex::scoped_lock lock(mutex_);
+
+ // Remove the descriptor from epoll.
+ epoll_event ev = { 0, { 0 } };
+ epoll_ctl(epoll_fd_, EPOLL_CTL_DEL, descriptor, &ev);
+
+ // Cancel any outstanding operations associated with the descriptor.
+ cancel_ops_unlocked(descriptor);
+ }
+
+ // Add a new timer queue to the reactor.
+ template <typename Time_Traits>
+ void add_timer_queue(timer_queue<Time_Traits>& timer_queue)
+ {
+ asio::detail::mutex::scoped_lock lock(mutex_);
+ timer_queues_.push_back(&timer_queue);
+ }
+
+ // Schedule a timer in the given timer queue to expire at the specified
+ // absolute time. The handler object will be invoked when the timer expires.
+ template <typename Time_Traits, typename Handler>
+ void schedule_timer(timer_queue<Time_Traits>& timer_queue,
+ const typename Time_Traits::time_type& time, Handler handler, void* token)
+ {
+ asio::detail::mutex::scoped_lock lock(mutex_);
+ if (!shutdown_)
+ if (timer_queue.enqueue_timer(time, handler, token))
+ interrupter_.interrupt();
+ }
+
+ // Cancel the timer associated with the given token. Returns the number of
+ // handlers that have been posted or dispatched.
+ template <typename Time_Traits>
+ std::size_t cancel_timer(timer_queue<Time_Traits>& timer_queue, void* token)
+ {
+ asio::detail::mutex::scoped_lock lock(mutex_);
+ return timer_queue.cancel_timer(token);
+ }
+
+private:
+ friend class task_io_service<epoll_reactor<Own_Thread> >;
+
+ // Run epoll once until interrupted or events are ready to be dispatched.
+ void run(bool block)
+ {
+ asio::detail::mutex::scoped_lock lock(mutex_);
+
+ // Dispatch any operation cancellations that were made while the select
+ // loop was not running.
+ read_op_queue_.dispatch_cancellations();
+ write_op_queue_.dispatch_cancellations();
+ except_op_queue_.dispatch_cancellations();
+
+ // Check if the thread is supposed to stop.
+ if (stop_thread_)
+ {
+ // Clean up operations. We must not hold the lock since the operations may
+ // make calls back into this reactor.
+ lock.unlock();
+ read_op_queue_.cleanup_operations();
+ write_op_queue_.cleanup_operations();
+ except_op_queue_.cleanup_operations();
+ return;
+ }
+
+ // We can return immediately if there's no work to do and the reactor is
+ // not supposed to block.
+ if (!block && read_op_queue_.empty() && write_op_queue_.empty()
+ && except_op_queue_.empty() && all_timer_queues_are_empty())
+ {
+ // Clean up operations. We must not hold the lock since the operations may
+ // make calls back into this reactor.
+ lock.unlock();
+ read_op_queue_.cleanup_operations();
+ write_op_queue_.cleanup_operations();
+ except_op_queue_.cleanup_operations();
+ return;
+ }
+
+ int timeout = block ? get_timeout() : 0;
+ wait_in_progress_ = true;
+ lock.unlock();
+
+ // Block on the epoll descriptor.
+ epoll_event events[128];
+ int num_events = epoll_wait(epoll_fd_, events, 128, timeout);
+
+ lock.lock();
+ wait_in_progress_ = false;
+
+ // Block signals while dispatching operations.
+ asio::detail::signal_blocker sb;
+
+ // Dispatch the waiting events.
+ for (int i = 0; i < num_events; ++i)
+ {
+ int descriptor = events[i].data.fd;
+ if (descriptor == interrupter_.read_descriptor())
+ {
+ interrupter_.reset();
+ }
+ else
+ {
+ if (events[i].events & (EPOLLERR | EPOLLHUP))
+ {
+ except_op_queue_.dispatch_all_operations(descriptor, 0);
+ read_op_queue_.dispatch_all_operations(descriptor, 0);
+ write_op_queue_.dispatch_all_operations(descriptor, 0);
+
+ epoll_event ev = { 0, { 0 } };
+ ev.events = 0;
+ ev.data.fd = descriptor;
+ epoll_ctl(epoll_fd_, EPOLL_CTL_MOD, descriptor, &ev);
+ }
+ else
+ {
+ bool more_reads = false;
+ bool more_writes = false;
+ bool more_except = false;
+
+ // Exception operations must be processed first to ensure that any
+ // out-of-band data is read before normal data.
+ if (events[i].events & EPOLLPRI)
+ more_except = except_op_queue_.dispatch_operation(descriptor, 0);
+ else
+ more_except = except_op_queue_.has_operation(descriptor);
+
+ if (events[i].events & EPOLLIN)
+ more_reads = read_op_queue_.dispatch_operation(descriptor, 0);
+ else
+ more_reads = read_op_queue_.has_operation(descriptor);
+
+ if (events[i].events & EPOLLOUT)
+ more_writes = write_op_queue_.dispatch_operation(descriptor, 0);
+ else
+ more_writes = write_op_queue_.has_operation(descriptor);
+
+ epoll_event ev = { 0, { 0 } };
+ ev.events = EPOLLERR | EPOLLHUP;
+ if (more_reads)
+ ev.events |= EPOLLIN;
+ if (more_writes)
+ ev.events |= EPOLLOUT;
+ if (more_except)
+ ev.events |= EPOLLPRI;
+ ev.data.fd = descriptor;
+ int result = epoll_ctl(epoll_fd_, EPOLL_CTL_MOD, descriptor, &ev);
+ if (result != 0)
+ {
+ int error = errno;
+ read_op_queue_.dispatch_all_operations(descriptor, error);
+ write_op_queue_.dispatch_all_operations(descriptor, error);
+ except_op_queue_.dispatch_all_operations(descriptor, error);
+ }
+ }
+ }
+ }
+ read_op_queue_.dispatch_cancellations();
+ write_op_queue_.dispatch_cancellations();
+ except_op_queue_.dispatch_cancellations();
+ for (std::size_t i = 0; i < timer_queues_.size(); ++i)
+ timer_queues_[i]->dispatch_timers();
+
+ // Issue any pending cancellations.
+ for (size_t i = 0; i < pending_cancellations_.size(); ++i)
+ cancel_ops_unlocked(pending_cancellations_[i]);
+ pending_cancellations_.clear();
+
+ // Clean up operations. We must not hold the lock since the operations may
+ // make calls back into this reactor.
+ lock.unlock();
+ read_op_queue_.cleanup_operations();
+ write_op_queue_.cleanup_operations();
+ except_op_queue_.cleanup_operations();
+ }
+
+ // Run the select loop in the thread.
+ void run_thread()
+ {
+ asio::detail::mutex::scoped_lock lock(mutex_);
+ while (!stop_thread_)
+ {
+ lock.unlock();
+ run(true);
+ lock.lock();
+ }
+ }
+
+ // Entry point for the select loop thread.
+ static void call_run_thread(epoll_reactor* reactor)
+ {
+ reactor->run_thread();
+ }
+
+ // Interrupt the select loop.
+ void interrupt()
+ {
+ interrupter_.interrupt();
+ }
+
+ // The hint to pass to epoll_create to size its data structures.
+ enum { epoll_size = 20000 };
+
+ // Create the epoll file descriptor. Throws an exception if the descriptor
+ // cannot be created.
+ static int do_epoll_create()
+ {
+ int fd = epoll_create(epoll_size);
+ if (fd == -1)
+ {
+ system_exception e("epoll", errno);
+ boost::throw_exception(e);
+ }
+ return fd;
+ }
+
+ // Check if all timer queues are empty.
+ bool all_timer_queues_are_empty() const
+ {
+ for (std::size_t i = 0; i < timer_queues_.size(); ++i)
+ if (!timer_queues_[i]->empty())
+ return false;
+ return true;
+ }
+
+ // Get the timeout value for the epoll_wait call. The timeout value is
+ // returned as a number of milliseconds. A return value of -1 indicates
+ // that epoll_wait should block indefinitely.
+ int get_timeout()
+ {
+ if (all_timer_queues_are_empty())
+ return -1;
+
+ // By default we will wait no longer than 5 minutes. This will ensure that
+ // any changes to the system clock are detected after no longer than this.
+ boost::posix_time::time_duration minimum_wait_duration
+ = boost::posix_time::minutes(5);
+
+ for (std::size_t i = 0; i < timer_queues_.size(); ++i)
+ {
+ boost::posix_time::time_duration wait_duration
+ = timer_queues_[i]->wait_duration();
+ if (wait_duration < minimum_wait_duration)
+ minimum_wait_duration = wait_duration;
+ }
+
+ if (minimum_wait_duration > boost::posix_time::time_duration())
+ {
+ return minimum_wait_duration.total_milliseconds();
+ }
+ else
+ {
+ return 0;
+ }
+ }
+
+ // Cancel all operations associated with the given descriptor. The do_cancel
+ // function of the handler objects will be invoked. This function does not
+ // acquire the epoll_reactor's mutex.
+ void cancel_ops_unlocked(socket_type descriptor)
+ {
+ bool interrupt = read_op_queue_.cancel_operations(descriptor);
+ interrupt = write_op_queue_.cancel_operations(descriptor) || interrupt;
+ interrupt = except_op_queue_.cancel_operations(descriptor) || interrupt;
+ if (interrupt)
+ interrupter_.interrupt();
+ }
+
+ // Mutex to protect access to internal data.
+ asio::detail::mutex mutex_;
+
+ // The epoll file descriptor.
+ int epoll_fd_;
+
+ // Whether the epoll_wait call is currently in progress
+ bool wait_in_progress_;
+
+ // The interrupter is used to break a blocking epoll_wait call.
+ select_interrupter interrupter_;
+
+ // The queue of read operations.
+ reactor_op_queue<socket_type> read_op_queue_;
+
+ // The queue of write operations.
+ reactor_op_queue<socket_type> write_op_queue_;
+
+ // The queue of except operations.
+ reactor_op_queue<socket_type> except_op_queue_;
+
+ // The timer queues.
+ std::vector<timer_queue_base*> timer_queues_;
+
+ // The descriptors that are pending cancellation.
+ std::vector<socket_type> pending_cancellations_;
+
+ // Does the reactor loop thread need to stop.
+ bool stop_thread_;
+
+ // The thread that is running the reactor loop.
+ asio::detail::thread* thread_;
+
+ // Whether the service has been shut down.
+ bool shutdown_;
+};
+
+} // namespace detail
+} // namespace asio
+
+#endif // defined(ASIO_HAS_EPOLL)
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_DETAIL_EPOLL_REACTOR_HPP
diff --git a/library/include/libtorrent/asio/detail/epoll_reactor_fwd.hpp b/library/include/libtorrent/asio/detail/epoll_reactor_fwd.hpp
new file mode 100644
index 000000000..5a60b759b
--- /dev/null
+++ b/library/include/libtorrent/asio/detail/epoll_reactor_fwd.hpp
@@ -0,0 +1,47 @@
+//
+// epoll_reactor_fwd.hpp
+// ~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_EPOLL_REACTOR_FWD_HPP
+#define ASIO_DETAIL_EPOLL_REACTOR_FWD_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#if !defined(ASIO_DISABLE_EPOLL)
+#if defined(__linux__) // This service is only supported on Linux.
+
+#include "asio/detail/push_options.hpp"
+#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
+
+namespace asio {
+namespace detail {
+
+template <bool Own_Thread>
+class epoll_reactor;
+
+} // namespace detail
+} // namespace asio
+
+#endif // LINUX_VERSION_CODE >= KERNEL_VERSION (2,5,45)
+#endif // defined(__linux__)
+#endif // !defined(ASIO_DISABLE_EPOLL)
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_DETAIL_EPOLL_REACTOR_FWD_HPP
diff --git a/library/include/libtorrent/asio/detail/event.hpp b/library/include/libtorrent/asio/detail/event.hpp
new file mode 100644
index 000000000..8f8b753e7
--- /dev/null
+++ b/library/include/libtorrent/asio/detail/event.hpp
@@ -0,0 +1,50 @@
+//
+// event.hpp
+// ~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_EVENT_HPP
+#define ASIO_DETAIL_EVENT_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/detail/push_options.hpp"
+#include <boost/config.hpp>
+#include "asio/detail/pop_options.hpp"
+
+#if !defined(BOOST_HAS_THREADS)
+# include "asio/detail/null_event.hpp"
+#elif defined(BOOST_WINDOWS)
+# include "asio/detail/win_event.hpp"
+#elif defined(BOOST_HAS_PTHREADS)
+# include "asio/detail/posix_event.hpp"
+#else
+# error Only Windows and POSIX are supported!
+#endif
+
+namespace asio {
+namespace detail {
+
+#if !defined(BOOST_HAS_THREADS)
+typedef null_event event;
+#elif defined(BOOST_WINDOWS)
+typedef win_event event;
+#elif defined(BOOST_HAS_PTHREADS)
+typedef posix_event event;
+#endif
+
+} // namespace detail
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_DETAIL_EVENT_HPP
diff --git a/library/include/libtorrent/asio/detail/fd_set_adapter.hpp b/library/include/libtorrent/asio/detail/fd_set_adapter.hpp
new file mode 100644
index 000000000..5266dd68c
--- /dev/null
+++ b/library/include/libtorrent/asio/detail/fd_set_adapter.hpp
@@ -0,0 +1,41 @@
+//
+// fd_set_adapter.hpp
+// ~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_FD_SET_ADAPTER_HPP
+#define ASIO_DETAIL_FD_SET_ADAPTER_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/detail/push_options.hpp"
+#include <boost/config.hpp>
+#include "asio/detail/pop_options.hpp"
+
+#include "asio/detail/posix_fd_set_adapter.hpp"
+#include "asio/detail/win_fd_set_adapter.hpp"
+
+namespace asio {
+namespace detail {
+
+#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+typedef win_fd_set_adapter fd_set_adapter;
+#else
+typedef posix_fd_set_adapter fd_set_adapter;
+#endif
+
+} // namespace detail
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_DETAIL_FD_SET_ADAPTER_HPP
diff --git a/library/include/libtorrent/asio/detail/handler_alloc_helpers.hpp b/library/include/libtorrent/asio/detail/handler_alloc_helpers.hpp
new file mode 100644
index 000000000..d78c7e6af
--- /dev/null
+++ b/library/include/libtorrent/asio/detail/handler_alloc_helpers.hpp
@@ -0,0 +1,256 @@
+//
+// handler_alloc_helpers.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_HANDLER_ALLOC_HELPERS_HPP
+#define ASIO_DETAIL_HANDLER_ALLOC_HELPERS_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/detail/push_options.hpp"
+#include <boost/detail/workaround.hpp>
+#include "asio/detail/pop_options.hpp"
+
+#include "asio/handler_alloc_hook.hpp"
+#include "asio/detail/noncopyable.hpp"
+
+// Calls to asio_handler_allocate and asio_handler_deallocate must be made from
+// a namespace that does not contain any overloads of these functions. The
+// asio_handler_alloc_helpers namespace is defined here for that purpose.
+namespace asio_handler_alloc_helpers {
+
+template <typename Handler>
+inline void* allocate(std::size_t s, Handler* h)
+{
+#if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564))
+ return ::operator new(s);
+#else
+ using namespace asio;
+ return asio_handler_allocate(s, h);
+#endif
+}
+
+template <typename Handler>
+inline void deallocate(void* p, std::size_t s, Handler* h)
+{
+#if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564))
+ ::operator delete(p);
+#else
+ using namespace asio;
+ asio_handler_deallocate(p, s, h);
+#endif
+}
+
+} // namespace asio_handler_alloc_helpers
+
+namespace asio {
+namespace detail {
+
+// Traits for handler allocation.
+template <typename Handler, typename Object>
+struct handler_alloc_traits
+{
+ typedef Handler handler_type;
+ typedef Object value_type;
+ typedef Object* pointer_type;
+ BOOST_STATIC_CONSTANT(std::size_t, value_size = sizeof(Object));
+};
+
+template <typename Alloc_Traits>
+class handler_ptr;
+
+// Helper class to provide RAII on uninitialised handler memory.
+template <typename Alloc_Traits>
+class raw_handler_ptr
+ : private noncopyable
+{
+public:
+ typedef typename Alloc_Traits::handler_type handler_type;
+ typedef typename Alloc_Traits::value_type value_type;
+ typedef typename Alloc_Traits::pointer_type pointer_type;
+ BOOST_STATIC_CONSTANT(std::size_t, value_size = Alloc_Traits::value_size);
+
+ // Constructor allocates the memory.
+ raw_handler_ptr(handler_type& handler)
+ : handler_(handler),
+ pointer_(static_cast<pointer_type>(
+ asio_handler_alloc_helpers::allocate(value_size, &handler_)))
+ {
+ }
+
+ // Destructor automatically deallocates memory, unless it has been stolen by
+ // a handler_ptr object.
+ ~raw_handler_ptr()
+ {
+ if (pointer_)
+ asio_handler_alloc_helpers::deallocate(
+ pointer_, value_size, &handler_);
+ }
+
+private:
+ friend class handler_ptr<Alloc_Traits>;
+ handler_type& handler_;
+ pointer_type pointer_;
+};
+
+// Helper class to provide RAII on uninitialised handler memory.
+template <typename Alloc_Traits>
+class handler_ptr
+ : private noncopyable
+{
+public:
+ typedef typename Alloc_Traits::handler_type handler_type;
+ typedef typename Alloc_Traits::value_type value_type;
+ typedef typename Alloc_Traits::pointer_type pointer_type;
+ BOOST_STATIC_CONSTANT(std::size_t, value_size = Alloc_Traits::value_size);
+ typedef raw_handler_ptr<Alloc_Traits> raw_ptr_type;
+
+ // Take ownership of existing memory.
+ handler_ptr(handler_type& handler, pointer_type pointer)
+ : handler_(handler),
+ pointer_(pointer)
+ {
+ }
+
+ // Construct object in raw memory and take ownership if construction succeeds.
+ handler_ptr(raw_ptr_type& raw_ptr)
+ : handler_(raw_ptr.handler_),
+ pointer_(new (raw_ptr.pointer_) value_type)
+ {
+ raw_ptr.pointer_ = 0;
+ }
+
+ // Construct object in raw memory and take ownership if construction succeeds.
+ template <typename Arg1>
+ handler_ptr(raw_ptr_type& raw_ptr, Arg1& a1)
+ : handler_(raw_ptr.handler_),
+ pointer_(new (raw_ptr.pointer_) value_type(a1))
+ {
+ raw_ptr.pointer_ = 0;
+ }
+
+ // Construct object in raw memory and take ownership if construction succeeds.
+ template <typename Arg1, typename Arg2>
+ handler_ptr(raw_ptr_type& raw_ptr, Arg1& a1, Arg2& a2)
+ : handler_(raw_ptr.handler_),
+ pointer_(new (raw_ptr.pointer_) value_type(a1, a2))
+ {
+ raw_ptr.pointer_ = 0;
+ }
+
+ // Construct object in raw memory and take ownership if construction succeeds.
+ template <typename Arg1, typename Arg2, typename Arg3>
+ handler_ptr(raw_ptr_type& raw_ptr, Arg1& a1, Arg2& a2, Arg3& a3)
+ : handler_(raw_ptr.handler_),
+ pointer_(new (raw_ptr.pointer_) value_type(a1, a2, a3))
+ {
+ raw_ptr.pointer_ = 0;
+ }
+
+ // Construct object in raw memory and take ownership if construction succeeds.
+ template <typename Arg1, typename Arg2, typename Arg3, typename Arg4>
+ handler_ptr(raw_ptr_type& raw_ptr, Arg1& a1, Arg2& a2, Arg3& a3, Arg4& a4)
+ : handler_(raw_ptr.handler_),
+ pointer_(new (raw_ptr.pointer_) value_type(a1, a2, a3, a4))
+ {
+ raw_ptr.pointer_ = 0;
+ }
+
+ // Construct object in raw memory and take ownership if construction succeeds.
+ template <typename Arg1, typename Arg2, typename Arg3, typename Arg4,
+ typename Arg5>
+ handler_ptr(raw_ptr_type& raw_ptr, Arg1& a1, Arg2& a2, Arg3& a3, Arg4& a4,
+ Arg5& a5)
+ : handler_(raw_ptr.handler_),
+ pointer_(new (raw_ptr.pointer_) value_type(a1, a2, a3, a4, a5))
+ {
+ raw_ptr.pointer_ = 0;
+ }
+
+ // Construct object in raw memory and take ownership if construction succeeds.
+ template <typename Arg1, typename Arg2, typename Arg3, typename Arg4,
+ typename Arg5, typename Arg6>
+ handler_ptr(raw_ptr_type& raw_ptr, Arg1& a1, Arg2& a2, Arg3& a3, Arg4& a4,
+ Arg5& a5, Arg6& a6)
+ : handler_(raw_ptr.handler_),
+ pointer_(new (raw_ptr.pointer_) value_type(a1, a2, a3, a4, a5, a6))
+ {
+ raw_ptr.pointer_ = 0;
+ }
+
+ // Construct object in raw memory and take ownership if construction succeeds.
+ template <typename Arg1, typename Arg2, typename Arg3, typename Arg4,
+ typename Arg5, typename Arg6, typename Arg7>
+ handler_ptr(raw_ptr_type& raw_ptr, Arg1& a1, Arg2& a2, Arg3& a3, Arg4& a4,
+ Arg5& a5, Arg6& a6, Arg7& a7)
+ : handler_(raw_ptr.handler_),
+ pointer_(new (raw_ptr.pointer_) value_type(a1, a2, a3, a4, a5, a6, a7))
+ {
+ raw_ptr.pointer_ = 0;
+ }
+
+ // Construct object in raw memory and take ownership if construction succeeds.
+ template <typename Arg1, typename Arg2, typename Arg3, typename Arg4,
+ typename Arg5, typename Arg6, typename Arg7, typename Arg8>
+ handler_ptr(raw_ptr_type& raw_ptr, Arg1& a1, Arg2& a2, Arg3& a3, Arg4& a4,
+ Arg5& a5, Arg6& a6, Arg7& a7, Arg8& a8)
+ : handler_(raw_ptr.handler_),
+ pointer_(new (raw_ptr.pointer_) value_type(
+ a1, a2, a3, a4, a5, a6, a7, a8))
+ {
+ raw_ptr.pointer_ = 0;
+ }
+
+ // Destructor automatically deallocates memory, unless it has been released.
+ ~handler_ptr()
+ {
+ reset();
+ }
+
+ // Get the memory.
+ pointer_type get() const
+ {
+ return pointer_;
+ }
+
+ // Release ownership of the memory.
+ pointer_type release()
+ {
+ pointer_type tmp = pointer_;
+ pointer_ = 0;
+ return tmp;
+ }
+
+ // Explicitly destroy and deallocate the memory.
+ void reset()
+ {
+ if (pointer_)
+ {
+ pointer_->value_type::~value_type();
+ asio_handler_alloc_helpers::deallocate(
+ pointer_, value_size, &handler_);
+ pointer_ = 0;
+ }
+ }
+
+private:
+ handler_type& handler_;
+ pointer_type pointer_;
+};
+
+} // namespace detail
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_DETAIL_HANDLER_ALLOC_HELPERS_HPP
diff --git a/library/include/libtorrent/asio/detail/handler_invoke_helpers.hpp b/library/include/libtorrent/asio/detail/handler_invoke_helpers.hpp
new file mode 100644
index 000000000..6045122c4
--- /dev/null
+++ b/library/include/libtorrent/asio/detail/handler_invoke_helpers.hpp
@@ -0,0 +1,47 @@
+//
+// handler_invoke_helpers.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_HANDLER_INVOKE_HELPERS_HPP
+#define ASIO_DETAIL_HANDLER_INVOKE_HELPERS_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/detail/push_options.hpp"
+#include <boost/detail/workaround.hpp>
+#include "asio/detail/pop_options.hpp"
+
+#include "asio/handler_invoke_hook.hpp"
+
+// Calls to asio_handler_invoke must be made from a namespace that does not
+// contain overloads of this function. The asio_handler_invoke_helpers
+// namespace is defined here for that purpose.
+namespace asio_handler_invoke_helpers {
+
+template <typename Function, typename Context>
+inline void invoke(const Function& function, Context* context)
+{
+#if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564))
+ Function tmp(function);
+ tmp();
+#else
+ using namespace asio;
+ asio_handler_invoke(function, context);
+#endif
+}
+
+} // namespace asio_handler_invoke_helpers
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_DETAIL_HANDLER_INVOKE_HELPERS_HPP
diff --git a/library/include/libtorrent/asio/detail/hash_map.hpp b/library/include/libtorrent/asio/detail/hash_map.hpp
new file mode 100644
index 000000000..c68d78d34
--- /dev/null
+++ b/library/include/libtorrent/asio/detail/hash_map.hpp
@@ -0,0 +1,197 @@
+//
+// hash_map.hpp
+// ~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_HASH_MAP_HPP
+#define ASIO_DETAIL_HASH_MAP_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/detail/push_options.hpp"
+#include <cassert>
+#include <list>
+#include <utility>
+#include <boost/functional/hash.hpp>
+#include "asio/detail/pop_options.hpp"
+
+#include "asio/detail/noncopyable.hpp"
+
+namespace asio {
+namespace detail {
+
+using boost::hash_value;
+
+template <typename K, typename V>
+class hash_map
+ : private noncopyable
+{
+public:
+ // The type of a value in the map.
+ typedef std::pair<const K, V> value_type;
+
+ // The type of a non-const iterator over the hash map.
+ typedef typename std::list<value_type>::iterator iterator;
+
+ // The type of a const iterator over the hash map.
+ typedef typename std::list<value_type>::const_iterator const_iterator;
+
+ // Constructor.
+ hash_map()
+ {
+ // Initialise all buckets to empty.
+ for (size_t i = 0; i < num_buckets; ++i)
+ buckets_[i].first = buckets_[i].last = values_.end();
+ }
+
+ // Get an iterator for the beginning of the map.
+ iterator begin()
+ {
+ return values_.begin();
+ }
+
+ // Get an iterator for the beginning of the map.
+ const_iterator begin() const
+ {
+ return values_.begin();
+ }
+
+ // Get an iterator for the end of the map.
+ iterator end()
+ {
+ return values_.end();
+ }
+
+ // Get an iterator for the end of the map.
+ const_iterator end() const
+ {
+ return values_.end();
+ }
+
+ // Check whether the map is empty.
+ bool empty() const
+ {
+ return values_.empty();
+ }
+
+ // Find an entry in the map.
+ iterator find(const K& k)
+ {
+ size_t bucket = hash_value(k) % num_buckets;
+ iterator it = buckets_[bucket].first;
+ if (it == values_.end())
+ return values_.end();
+ iterator end = buckets_[bucket].last;
+ ++end;
+ while (it != end)
+ {
+ if (it->first == k)
+ return it;
+ ++it;
+ }
+ return values_.end();
+ }
+
+ // Find an entry in the map.
+ const_iterator find(const K& k) const
+ {
+ size_t bucket = hash_value(k) % num_buckets;
+ const_iterator it = buckets_[bucket].first;
+ if (it == values_.end())
+ return it;
+ const_iterator end = buckets_[bucket].last;
+ ++end;
+ while (it != end)
+ {
+ if (it->first == k)
+ return it;
+ ++it;
+ }
+ return values_.end();
+ }
+
+ // Insert a new entry into the map.
+ std::pair<iterator, bool> insert(const value_type& v)
+ {
+ size_t bucket = hash_value(v.first) % num_buckets;
+ iterator it = buckets_[bucket].first;
+ if (it == values_.end())
+ {
+ buckets_[bucket].first = buckets_[bucket].last =
+ values_.insert(values_.end(), v);
+ return std::pair<iterator, bool>(buckets_[bucket].last, true);
+ }
+ iterator end = buckets_[bucket].last;
+ ++end;
+ while (it != end)
+ {
+ if (it->first == v.first)
+ return std::pair<iterator, bool>(it, false);
+ ++it;
+ }
+ buckets_[bucket].last = values_.insert(end, v);
+ return std::pair<iterator, bool>(buckets_[bucket].last, true);
+ }
+
+ // Erase an entry from the map.
+ void erase(iterator it)
+ {
+ assert(it != values_.end());
+
+ size_t bucket = hash_value(it->first) % num_buckets;
+ bool is_first = (it == buckets_[bucket].first);
+ bool is_last = (it == buckets_[bucket].last);
+ if (is_first && is_last)
+ buckets_[bucket].first = buckets_[bucket].last = values_.end();
+ else if (is_first)
+ ++buckets_[bucket].first;
+ else if (is_last)
+ --buckets_[bucket].last;
+
+ values_.erase(it);
+ }
+
+ // Remove all entries from the map.
+ void clear()
+ {
+ // Clear the values.
+ values_.clear();
+
+ // Initialise all buckets to empty.
+ for (size_t i = 0; i < num_buckets; ++i)
+ buckets_[i].first = buckets_[i].last = values_.end();
+ }
+
+private:
+ // The list of all values in the hash map.
+ std::list<value_type> values_;
+
+ // The type for a bucket in the hash table.
+ struct bucket_type
+ {
+ iterator first;
+ iterator last;
+ };
+
+ // The number of buckets in the hash.
+ enum { num_buckets = 1021 };
+
+ // The buckets in the hash.
+ bucket_type buckets_[num_buckets];
+};
+
+} // namespace detail
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_DETAIL_HASH_MAP_HPP
diff --git a/library/include/libtorrent/asio/detail/io_control.hpp b/library/include/libtorrent/asio/detail/io_control.hpp
new file mode 100644
index 000000000..ee9e8ac9f
--- /dev/null
+++ b/library/include/libtorrent/asio/detail/io_control.hpp
@@ -0,0 +1,137 @@
+//
+// io_control.hpp
+// ~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_IO_CONTROL_HPP
+#define ASIO_DETAIL_IO_CONTROL_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/detail/push_options.hpp"
+#include <cstddef>
+#include <boost/config.hpp>
+#include "asio/detail/pop_options.hpp"
+
+#include "asio/detail/socket_types.hpp"
+
+namespace asio {
+namespace detail {
+namespace io_control {
+
+// IO control command for non-blocking I/O.
+class non_blocking_io
+{
+public:
+ // Default constructor.
+ non_blocking_io()
+ : value_(0)
+ {
+ }
+
+ // Construct with a specific command value.
+ non_blocking_io(bool value)
+ : value_(value ? 1 : 0)
+ {
+ }
+
+ // Get the name of the IO control command.
+ int name() const
+ {
+ return FIONBIO;
+ }
+
+ // Set the value of the I/O control command.
+ void set(bool value)
+ {
+ value_ = value ? 1 : 0;
+ }
+
+ // Get the current value of the I/O control command.
+ bool get() const
+ {
+ return value_ != 0;
+ }
+
+ // Get the address of the command data.
+ detail::ioctl_arg_type* data()
+ {
+ return &value_;
+ }
+
+ // Get the address of the command data.
+ const detail::ioctl_arg_type* data() const
+ {
+ return &value_;
+ }
+
+private:
+ detail::ioctl_arg_type value_;
+};
+
+// I/O control command for getting number of bytes available.
+class bytes_readable
+{
+public:
+ // Default constructor.
+ bytes_readable()
+ : value_(0)
+ {
+ }
+
+ // Construct with a specific command value.
+ bytes_readable(std::size_t value)
+ : value_(value)
+ {
+ }
+
+ // Get the name of the IO control command.
+ int name() const
+ {
+ return FIONREAD;
+ }
+
+ // Set the value of the I/O control command.
+ void set(std::size_t value)
+ {
+ value_ = static_cast<detail::ioctl_arg_type>(value);
+ }
+
+ // Get the current value of the I/O control command.
+ std::size_t get() const
+ {
+ return static_cast<std::size_t>(value_);
+ }
+
+ // Get the address of the command data.
+ detail::ioctl_arg_type* data()
+ {
+ return &value_;
+ }
+
+ // Get the address of the command data.
+ const detail::ioctl_arg_type* data() const
+ {
+ return &value_;
+ }
+
+private:
+ detail::ioctl_arg_type value_;
+};
+
+} // namespace io_control
+} // namespace detail
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_DETAIL_IO_CONTROL_HPP
diff --git a/library/include/libtorrent/asio/detail/kqueue_reactor.hpp b/library/include/libtorrent/asio/detail/kqueue_reactor.hpp
new file mode 100644
index 000000000..51cabbc0a
--- /dev/null
+++ b/library/include/libtorrent/asio/detail/kqueue_reactor.hpp
@@ -0,0 +1,596 @@
+//
+// kqueue_reactor.hpp
+// ~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2005 Stefan Arentz (stefan at soze dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_KQUEUE_REACTOR_HPP
+#define ASIO_DETAIL_KQUEUE_REACTOR_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/detail/kqueue_reactor_fwd.hpp"
+
+#if defined(ASIO_HAS_KQUEUE)
+
+#include "asio/detail/push_options.hpp"
+#include <cstddef>
+#include <vector>
+#include <sys/types.h>
+#include <sys/event.h>
+#include <sys/time.h>
+#include <boost/config.hpp>
+#include <boost/date_time/posix_time/posix_time_types.hpp>
+#include <boost/throw_exception.hpp>
+#include "asio/detail/pop_options.hpp"
+
+#include "asio/io_service.hpp"
+#include "asio/system_exception.hpp"
+#include "asio/detail/bind_handler.hpp"
+#include "asio/detail/mutex.hpp"
+#include "asio/detail/task_io_service.hpp"
+#include "asio/detail/thread.hpp"
+#include "asio/detail/reactor_op_queue.hpp"
+#include "asio/detail/select_interrupter.hpp"
+#include "asio/detail/signal_blocker.hpp"
+#include "asio/detail/socket_types.hpp"
+#include "asio/detail/timer_queue.hpp"
+
+// Older versions of Mac OS X may not define EV_OOBAND.
+#if !defined(EV_OOBAND)
+# define EV_OOBAND EV_FLAG1
+#endif // !defined(EV_OOBAND)
+
+namespace asio {
+namespace detail {
+
+template <bool Own_Thread>
+class kqueue_reactor
+ : public asio::io_service::service
+{
+public:
+ // Constructor.
+ kqueue_reactor(asio::io_service& io_service)
+ : asio::io_service::service(io_service),
+ mutex_(),
+ kqueue_fd_(do_kqueue_create()),
+ wait_in_progress_(false),
+ interrupter_(),
+ read_op_queue_(),
+ write_op_queue_(),
+ except_op_queue_(),
+ pending_cancellations_(),
+ stop_thread_(false),
+ thread_(0),
+ shutdown_(false)
+ {
+ // Start the reactor's internal thread only if needed.
+ if (Own_Thread)
+ {
+ asio::detail::signal_blocker sb;
+ thread_ = new asio::detail::thread(
+ bind_handler(&kqueue_reactor::call_run_thread, this));
+ }
+
+ // Add the interrupter's descriptor to the kqueue.
+ struct kevent event;
+ EV_SET(&event, interrupter_.read_descriptor(),
+ EVFILT_READ, EV_ADD, 0, 0, 0);
+ ::kevent(kqueue_fd_, &event, 1, 0, 0, 0);
+ }
+
+ // Destructor.
+ ~kqueue_reactor()
+ {
+ shutdown_service();
+ close(kqueue_fd_);
+ }
+
+ // Destroy all user-defined handler objects owned by the service.
+ void shutdown_service()
+ {
+ asio::detail::mutex::scoped_lock lock(mutex_);
+ shutdown_ = true;
+ stop_thread_ = true;
+ lock.unlock();
+
+ if (thread_)
+ {
+ interrupter_.interrupt();
+ thread_->join();
+ delete thread_;
+ thread_ = 0;
+ }
+
+ read_op_queue_.destroy_operations();
+ write_op_queue_.destroy_operations();
+ except_op_queue_.destroy_operations();
+
+ for (std::size_t i = 0; i < timer_queues_.size(); ++i)
+ timer_queues_[i]->destroy_timers();
+ timer_queues_.clear();
+ }
+
+ // Register a socket with the reactor. Returns 0 on success, system error
+ // code on failure.
+ int register_descriptor(socket_type)
+ {
+ return 0;
+ }
+
+ // Start a new read operation. The handler object will be invoked when the
+ // given descriptor is ready to be read, or an error has occurred.
+ template <typename Handler>
+ void start_read_op(socket_type descriptor, Handler handler)
+ {
+ asio::detail::mutex::scoped_lock lock(mutex_);
+
+ if (shutdown_)
+ return;
+
+ if (!read_op_queue_.has_operation(descriptor))
+ if (handler(0))
+ return;
+
+ if (read_op_queue_.enqueue_operation(descriptor, handler))
+ {
+ struct kevent event;
+ EV_SET(&event, descriptor, EVFILT_READ, EV_ADD, 0, 0, 0);
+ if (::kevent(kqueue_fd_, &event, 1, 0, 0, 0) == -1)
+ {
+ int error = errno;
+ read_op_queue_.dispatch_all_operations(descriptor, error);
+ }
+ }
+ }
+
+ // Start a new write operation. The handler object will be invoked when the
+ // given descriptor is ready to be written, or an error has occurred.
+ template <typename Handler>
+ void start_write_op(socket_type descriptor, Handler handler)
+ {
+ asio::detail::mutex::scoped_lock lock(mutex_);
+
+ if (shutdown_)
+ return;
+
+ if (!write_op_queue_.has_operation(descriptor))
+ if (handler(0))
+ return;
+
+ if (write_op_queue_.enqueue_operation(descriptor, handler))
+ {
+ struct kevent event;
+ EV_SET(&event, descriptor, EVFILT_WRITE, EV_ADD, 0, 0, 0);
+ if (::kevent(kqueue_fd_, &event, 1, 0, 0, 0) == -1)
+ {
+ int error = errno;
+ write_op_queue_.dispatch_all_operations(descriptor, error);
+ }
+ }
+ }
+
+ // Start a new exception operation. The handler object will be invoked when
+ // the given descriptor has exception information, or an error has occurred.
+ template <typename Handler>
+ void start_except_op(socket_type descriptor, Handler handler)
+ {
+ asio::detail::mutex::scoped_lock lock(mutex_);
+
+ if (shutdown_)
+ return;
+
+ if (except_op_queue_.enqueue_operation(descriptor, handler))
+ {
+ struct kevent event;
+ if (read_op_queue_.has_operation(descriptor))
+ EV_SET(&event, descriptor, EVFILT_READ, EV_ADD, 0, 0, 0);
+ else
+ EV_SET(&event, descriptor, EVFILT_READ, EV_ADD, EV_OOBAND, 0, 0);
+ if (::kevent(kqueue_fd_, &event, 1, 0, 0, 0) == -1)
+ {
+ int error = errno;
+ except_op_queue_.dispatch_all_operations(descriptor, error);
+ }
+ }
+ }
+
+ // Start new write and exception operations. The handler object will be
+ // invoked when the given descriptor is ready for writing or has exception
+ // information available, or an error has occurred.
+ template <typename Handler>
+ void start_write_and_except_ops(socket_type descriptor, Handler handler)
+ {
+ asio::detail::mutex::scoped_lock lock(mutex_);
+
+ if (shutdown_)
+ return;
+
+ if (write_op_queue_.enqueue_operation(descriptor, handler))
+ {
+ struct kevent event;
+ EV_SET(&event, descriptor, EVFILT_WRITE, EV_ADD, 0, 0, 0);
+ if (::kevent(kqueue_fd_, &event, 1, 0, 0, 0) == -1)
+ {
+ int error = errno;
+ write_op_queue_.dispatch_all_operations(descriptor, error);
+ }
+ }
+
+ if (except_op_queue_.enqueue_operation(descriptor, handler))
+ {
+ struct kevent event;
+ if (read_op_queue_.has_operation(descriptor))
+ EV_SET(&event, descriptor, EVFILT_READ, EV_ADD, 0, 0, 0);
+ else
+ EV_SET(&event, descriptor, EVFILT_READ, EV_ADD, EV_OOBAND, 0, 0);
+ if (::kevent(kqueue_fd_, &event, 1, 0, 0, 0) == -1)
+ {
+ int error = errno;
+ except_op_queue_.dispatch_all_operations(descriptor, error);
+ write_op_queue_.dispatch_all_operations(descriptor, error);
+ }
+ }
+ }
+
+ // Cancel all operations associated with the given descriptor. The
+ // handlers associated with the descriptor will be invoked with the
+ // operation_aborted error.
+ void cancel_ops(socket_type descriptor)
+ {
+ asio::detail::mutex::scoped_lock lock(mutex_);
+ cancel_ops_unlocked(descriptor);
+ }
+
+ // Enqueue cancellation of all operations associated with the given
+ // descriptor. The handlers associated with the descriptor will be invoked
+ // with the operation_aborted error. This function does not acquire the
+ // kqueue_reactor's mutex, and so should only be used from within a reactor
+ // handler.
+ void enqueue_cancel_ops_unlocked(socket_type descriptor)
+ {
+ pending_cancellations_.push_back(descriptor);
+ }
+
+ // Cancel any operations that are running against the descriptor and remove
+ // its registration from the reactor.
+ void close_descriptor(socket_type descriptor)
+ {
+ asio::detail::mutex::scoped_lock lock(mutex_);
+
+ // Remove the descriptor from kqueue.
+ struct kevent event[2];
+ EV_SET(&event[0], descriptor, EVFILT_READ, EV_DELETE, 0, 0, 0);
+ EV_SET(&event[1], descriptor, EVFILT_WRITE, EV_DELETE, 0, 0, 0);
+ ::kevent(kqueue_fd_, event, 2, 0, 0, 0);
+
+ // Cancel any outstanding operations associated with the descriptor.
+ cancel_ops_unlocked(descriptor);
+ }
+
+ // Add a new timer queue to the reactor.
+ template <typename Time_Traits>
+ void add_timer_queue(timer_queue<Time_Traits>& timer_queue)
+ {
+ asio::detail::mutex::scoped_lock lock(mutex_);
+ timer_queues_.push_back(&timer_queue);
+ }
+
+ // Schedule a timer in the given timer queue to expire at the specified
+ // absolute time. The handler object will be invoked when the timer expires.
+ template <typename Time_Traits, typename Handler>
+ void schedule_timer(timer_queue<Time_Traits>& timer_queue,
+ const typename Time_Traits::time_type& time, Handler handler, void* token)
+ {
+ asio::detail::mutex::scoped_lock lock(mutex_);
+ if (!shutdown_)
+ if (timer_queue.enqueue_timer(time, handler, token))
+ interrupter_.interrupt();
+ }
+
+ // Cancel the timer associated with the given token. Returns the number of
+ // handlers that have been posted or dispatched.
+ template <typename Time_Traits>
+ std::size_t cancel_timer(timer_queue<Time_Traits>& timer_queue, void* token)
+ {
+ asio::detail::mutex::scoped_lock lock(mutex_);
+ return timer_queue.cancel_timer(token);
+ }
+
+private:
+ friend class task_io_service<kqueue_reactor<Own_Thread> >;
+
+ // Run the kqueue loop.
+ void run(bool block)
+ {
+ asio::detail::mutex::scoped_lock lock(mutex_);
+
+ // Dispatch any operation cancellations that were made while the select
+ // loop was not running.
+ read_op_queue_.dispatch_cancellations();
+ write_op_queue_.dispatch_cancellations();
+ except_op_queue_.dispatch_cancellations();
+
+ // Check if the thread is supposed to stop.
+ if (stop_thread_)
+ {
+ // Clean up operations. We must not hold the lock since the operations may
+ // make calls back into this reactor.
+ lock.unlock();
+ read_op_queue_.cleanup_operations();
+ write_op_queue_.cleanup_operations();
+ except_op_queue_.cleanup_operations();
+ return;
+ }
+
+ // We can return immediately if there's no work to do and the reactor is
+ // not supposed to block.
+ if (!block && read_op_queue_.empty() && write_op_queue_.empty()
+ && except_op_queue_.empty() && all_timer_queues_are_empty())
+ {
+ // Clean up operations. We must not hold the lock since the operations may
+ // make calls back into this reactor.
+ lock.unlock();
+ read_op_queue_.cleanup_operations();
+ write_op_queue_.cleanup_operations();
+ except_op_queue_.cleanup_operations();
+ return;
+ }
+
+ // Determine how long to block while waiting for events.
+ timespec timeout_buf = { 0, 0 };
+ timespec* timeout = block ? get_timeout(timeout_buf) : &timeout_buf;
+
+ wait_in_progress_ = true;
+ lock.unlock();
+
+ // Block on the kqueue descriptor.
+ struct kevent events[128];
+ int num_events = kevent(kqueue_fd_, 0, 0, events, 128, timeout);
+
+ lock.lock();
+ wait_in_progress_ = false;
+
+ // Block signals while dispatching operations.
+ asio::detail::signal_blocker sb;
+
+ // Dispatch the waiting events.
+ for (int i = 0; i < num_events; ++i)
+ {
+ int descriptor = events[i].ident;
+ if (descriptor == interrupter_.read_descriptor())
+ {
+ interrupter_.reset();
+ }
+ else if (events[i].filter == EVFILT_READ)
+ {
+ // Dispatch operations associated with the descriptor.
+ bool more_reads = false;
+ bool more_except = false;
+ if (events[i].flags & EV_ERROR)
+ {
+ int error = events[i].data;
+ except_op_queue_.dispatch_all_operations(descriptor, error);
+ read_op_queue_.dispatch_all_operations(descriptor, error);
+ }
+ else if (events[i].flags & EV_OOBAND)
+ {
+ more_except = except_op_queue_.dispatch_operation(descriptor, 0);
+ if (events[i].data > 0)
+ more_reads = read_op_queue_.dispatch_operation(descriptor, 0);
+ else
+ more_reads = read_op_queue_.has_operation(descriptor);
+ }
+ else
+ {
+ more_reads = read_op_queue_.dispatch_operation(descriptor, 0);
+ more_except = except_op_queue_.has_operation(descriptor);
+ }
+
+ // Update the descriptor in the kqueue.
+ struct kevent event;
+ if (more_reads)
+ EV_SET(&event, descriptor, EVFILT_READ, EV_ADD, 0, 0, 0);
+ else if (more_except)
+ EV_SET(&event, descriptor, EVFILT_READ, EV_ADD, EV_OOBAND, 0, 0);
+ else
+ EV_SET(&event, descriptor, EVFILT_READ, EV_DELETE, 0, 0, 0);
+ if (::kevent(kqueue_fd_, &event, 1, 0, 0, 0) == -1)
+ {
+ int error = errno;
+ except_op_queue_.dispatch_all_operations(descriptor, error);
+ read_op_queue_.dispatch_all_operations(descriptor, error);
+ }
+ }
+ else if (events[i].filter == EVFILT_WRITE)
+ {
+ // Dispatch operations associated with the descriptor.
+ bool more_writes = false;
+ if (events[i].flags & EV_ERROR)
+ {
+ int error = events[i].data;
+ write_op_queue_.dispatch_all_operations(descriptor, error);
+ }
+ else
+ {
+ more_writes = write_op_queue_.dispatch_operation(descriptor, 0);
+ }
+
+ // Update the descriptor in the kqueue.
+ struct kevent event;
+ if (more_writes)
+ EV_SET(&event, descriptor, EVFILT_WRITE, EV_ADD, 0, 0, 0);
+ else
+ EV_SET(&event, descriptor, EVFILT_WRITE, EV_DELETE, 0, 0, 0);
+ if (::kevent(kqueue_fd_, &event, 1, 0, 0, 0) == -1)
+ {
+ int error = errno;
+ write_op_queue_.dispatch_all_operations(descriptor, error);
+ }
+ }
+ }
+
+ read_op_queue_.dispatch_cancellations();
+ write_op_queue_.dispatch_cancellations();
+ except_op_queue_.dispatch_cancellations();
+ for (std::size_t i = 0; i < timer_queues_.size(); ++i)
+ timer_queues_[i]->dispatch_timers();
+
+ // Issue any pending cancellations.
+ for (std::size_t i = 0; i < pending_cancellations_.size(); ++i)
+ cancel_ops_unlocked(pending_cancellations_[i]);
+ pending_cancellations_.clear();
+
+ // Clean up operations. We must not hold the lock since the operations may
+ // make calls back into this reactor.
+ lock.unlock();
+ read_op_queue_.cleanup_operations();
+ write_op_queue_.cleanup_operations();
+ except_op_queue_.cleanup_operations();
+ }
+
+ // Run the select loop in the thread.
+ void run_thread()
+ {
+ asio::detail::mutex::scoped_lock lock(mutex_);
+ while (!stop_thread_)
+ {
+ lock.unlock();
+ run(true);
+ lock.lock();
+ }
+ }
+
+ // Entry point for the select loop thread.
+ static void call_run_thread(kqueue_reactor* reactor)
+ {
+ reactor->run_thread();
+ }
+
+ // Interrupt the select loop.
+ void interrupt()
+ {
+ interrupter_.interrupt();
+ }
+
+ // Create the kqueue file descriptor. Throws an exception if the descriptor
+ // cannot be created.
+ static int do_kqueue_create()
+ {
+ int fd = kqueue();
+ if (fd == -1)
+ {
+ system_exception e("kqueue", errno);
+ boost::throw_exception(e);
+ }
+ return fd;
+ }
+
+ // Check if all timer queues are empty.
+ bool all_timer_queues_are_empty() const
+ {
+ for (std::size_t i = 0; i < timer_queues_.size(); ++i)
+ if (!timer_queues_[i]->empty())
+ return false;
+ return true;
+ }
+
+ // Get the timeout value for the kevent call.
+ timespec* get_timeout(timespec& ts)
+ {
+ if (all_timer_queues_are_empty())
+ return 0;
+
+ // By default we will wait no longer than 5 minutes. This will ensure that
+ // any changes to the system clock are detected after no longer than this.
+ boost::posix_time::time_duration minimum_wait_duration
+ = boost::posix_time::minutes(5);
+
+ for (std::size_t i = 0; i < timer_queues_.size(); ++i)
+ {
+ boost::posix_time::time_duration wait_duration
+ = timer_queues_[i]->wait_duration();
+ if (wait_duration < minimum_wait_duration)
+ minimum_wait_duration = wait_duration;
+ }
+
+ if (minimum_wait_duration > boost::posix_time::time_duration())
+ {
+ ts.tv_sec = minimum_wait_duration.total_seconds();
+ ts.tv_nsec = minimum_wait_duration.total_nanoseconds() % 1000000000;
+ }
+ else
+ {
+ ts.tv_sec = 0;
+ ts.tv_nsec = 0;
+ }
+
+ return &ts;
+ }
+
+ // Cancel all operations associated with the given descriptor. The do_cancel
+ // function of the handler objects will be invoked. This function does not
+ // acquire the epoll_reactor's mutex.
+ void cancel_ops_unlocked(socket_type descriptor)
+ {
+ bool interrupt = read_op_queue_.cancel_operations(descriptor);
+ interrupt = write_op_queue_.cancel_operations(descriptor) || interrupt;
+ interrupt = except_op_queue_.cancel_operations(descriptor) || interrupt;
+ if (interrupt)
+ interrupter_.interrupt();
+ }
+
+ // Mutex to protect access to internal data.
+ asio::detail::mutex mutex_;
+
+ // The epoll file descriptor.
+ int kqueue_fd_;
+
+ // Whether the kqueue wait call is currently in progress
+ bool wait_in_progress_;
+
+ // The interrupter is used to break a blocking epoll_wait call.
+ select_interrupter interrupter_;
+
+ // The queue of read operations.
+ reactor_op_queue<socket_type> read_op_queue_;
+
+ // The queue of write operations.
+ reactor_op_queue<socket_type> write_op_queue_;
+
+ // The queue of except operations.
+ reactor_op_queue<socket_type> except_op_queue_;
+
+ // The timer queues.
+ std::vector<timer_queue_base*> timer_queues_;
+
+ // The descriptors that are pending cancellation.
+ std::vector<socket_type> pending_cancellations_;
+
+ // Does the reactor loop thread need to stop.
+ bool stop_thread_;
+
+ // The thread that is running the reactor loop.
+ asio::detail::thread* thread_;
+
+ // Whether the service has been shut down.
+ bool shutdown_;
+};
+
+} // namespace detail
+} // namespace asio
+
+#endif // defined(ASIO_HAS_KQUEUE)
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_DETAIL_KQUEUE_REACTOR_HPP
diff --git a/library/include/libtorrent/asio/detail/kqueue_reactor_fwd.hpp b/library/include/libtorrent/asio/detail/kqueue_reactor_fwd.hpp
new file mode 100644
index 000000000..b9bc5a02d
--- /dev/null
+++ b/library/include/libtorrent/asio/detail/kqueue_reactor_fwd.hpp
@@ -0,0 +1,41 @@
+//
+// kqueue_reactor_fwd.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2005 Stefan Arentz (stefan at soze dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_KQUEUE_REACTOR_FWD_HPP
+#define ASIO_DETAIL_KQUEUE_REACTOR_FWD_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#if !defined(ASIO_DISABLE_KQUEUE)
+#if defined(__MACH__) && defined(__APPLE__)
+
+// Define this to indicate that epoll is supported on the target platform.
+#define ASIO_HAS_KQUEUE 1
+
+namespace asio {
+namespace detail {
+
+template <bool Own_Thread>
+class kqueue_reactor;
+
+} // namespace detail
+} // namespace asio
+
+#endif // defined(__MACH__) && defined(__APPLE__)
+#endif // !defined(ASIO_DISABLE_KQUEUE)
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_DETAIL_KQUEUE_REACTOR_FWD_HPP
diff --git a/library/include/libtorrent/asio/detail/mutex.hpp b/library/include/libtorrent/asio/detail/mutex.hpp
new file mode 100644
index 000000000..e861556d0
--- /dev/null
+++ b/library/include/libtorrent/asio/detail/mutex.hpp
@@ -0,0 +1,50 @@
+//
+// mutex.hpp
+// ~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_MUTEX_HPP
+#define ASIO_DETAIL_MUTEX_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/detail/push_options.hpp"
+#include <boost/config.hpp>
+#include "asio/detail/pop_options.hpp"
+
+#if !defined(BOOST_HAS_THREADS)
+# include "asio/detail/null_mutex.hpp"
+#elif defined(BOOST_WINDOWS)
+# include "asio/detail/win_mutex.hpp"
+#elif defined(BOOST_HAS_PTHREADS)
+# include "asio/detail/posix_mutex.hpp"
+#else
+# error Only Windows and POSIX are supported!
+#endif
+
+namespace asio {
+namespace detail {
+
+#if !defined(BOOST_HAS_THREADS)
+typedef null_mutex mutex;
+#elif defined(BOOST_WINDOWS)
+typedef win_mutex mutex;
+#elif defined(BOOST_HAS_PTHREADS)
+typedef posix_mutex mutex;
+#endif
+
+} // namespace detail
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_DETAIL_MUTEX_HPP
diff --git a/library/include/libtorrent/asio/detail/noncopyable.hpp b/library/include/libtorrent/asio/detail/noncopyable.hpp
new file mode 100644
index 000000000..004a68bc6
--- /dev/null
+++ b/library/include/libtorrent/asio/detail/noncopyable.hpp
@@ -0,0 +1,55 @@
+//
+// noncopyable.hpp
+// ~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_NONCOPYABLE_HPP
+#define ASIO_DETAIL_NONCOPYABLE_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/detail/push_options.hpp"
+#include <boost/config.hpp>
+#include <boost/noncopyable.hpp>
+#include <boost/detail/workaround.hpp>
+#include "asio/detail/pop_options.hpp"
+
+namespace asio {
+namespace detail {
+
+#if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564))
+// Redefine the noncopyable class for Borland C++ since that compiler does not
+// apply the empty base optimisation unless the base class contains a dummy
+// char data member.
+class noncopyable
+{
+protected:
+ noncopyable() {}
+ ~noncopyable() {}
+private:
+ noncopyable(const noncopyable&);
+ const noncopyable& operator=(const noncopyable&);
+ char dummy_;
+};
+#else
+using boost::noncopyable;
+#endif
+
+} // namespace detail
+
+using asio::detail::noncopyable;
+
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_DETAIL_NONCOPYABLE_HPP
diff --git a/library/include/libtorrent/asio/detail/null_event.hpp b/library/include/libtorrent/asio/detail/null_event.hpp
new file mode 100644
index 000000000..91bfc329e
--- /dev/null
+++ b/library/include/libtorrent/asio/detail/null_event.hpp
@@ -0,0 +1,68 @@
+//
+// null_event.hpp
+// ~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_NULL_EVENT_HPP
+#define ASIO_DETAIL_NULL_EVENT_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/detail/push_options.hpp"
+#include <boost/config.hpp>
+#include "asio/detail/pop_options.hpp"
+
+#if !defined(BOOST_HAS_THREADS)
+
+#include "asio/detail/noncopyable.hpp"
+
+namespace asio {
+namespace detail {
+
+class null_event
+ : private noncopyable
+{
+public:
+ // Constructor.
+ null_event()
+ {
+ }
+
+ // Destructor.
+ ~null_event()
+ {
+ }
+
+ // Signal the event.
+ void signal()
+ {
+ }
+
+ // Reset the event.
+ void clear()
+ {
+ }
+
+ // Wait for the event to become signalled.
+ void wait()
+ {
+ }
+};
+
+} // namespace detail
+} // namespace asio
+
+#endif // !defined(BOOST_HAS_THREADS)
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_DETAIL_NULL_EVENT_HPP
diff --git a/library/include/libtorrent/asio/detail/null_mutex.hpp b/library/include/libtorrent/asio/detail/null_mutex.hpp
new file mode 100644
index 000000000..814a18f5f
--- /dev/null
+++ b/library/include/libtorrent/asio/detail/null_mutex.hpp
@@ -0,0 +1,66 @@
+//
+// null_mutex.hpp
+// ~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_NULL_MUTEX_HPP
+#define ASIO_DETAIL_NULL_MUTEX_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/detail/push_options.hpp"
+#include <boost/config.hpp>
+#include "asio/detail/pop_options.hpp"
+
+#if !defined(BOOST_HAS_THREADS)
+
+#include "asio/detail/noncopyable.hpp"
+#include "asio/detail/scoped_lock.hpp"
+
+namespace asio {
+namespace detail {
+
+class null_mutex
+ : private noncopyable
+{
+public:
+ typedef asio::detail::scoped_lock<null_mutex> scoped_lock;
+
+ // Constructor.
+ null_mutex()
+ {
+ }
+
+ // Destructor.
+ ~null_mutex()
+ {
+ }
+
+ // Lock the mutex.
+ void lock()
+ {
+ }
+
+ // Unlock the mutex.
+ void unlock()
+ {
+ }
+};
+
+} // namespace detail
+} // namespace asio
+
+#endif // !defined(BOOST_HAS_THREADS)
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_DETAIL_NULL_MUTEX_HPP
diff --git a/library/include/libtorrent/asio/detail/null_signal_blocker.hpp b/library/include/libtorrent/asio/detail/null_signal_blocker.hpp
new file mode 100644
index 000000000..ff7cfcaf0
--- /dev/null
+++ b/library/include/libtorrent/asio/detail/null_signal_blocker.hpp
@@ -0,0 +1,63 @@
+//
+// null_signal_blocker.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_NULL_SIGNAL_BLOCKER_HPP
+#define ASIO_DETAIL_NULL_SIGNAL_BLOCKER_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/detail/push_options.hpp"
+#include <boost/config.hpp>
+#include "asio/detail/pop_options.hpp"
+
+#if !defined(BOOST_HAS_THREADS)
+
+#include "asio/detail/noncopyable.hpp"
+
+namespace asio {
+namespace detail {
+
+class null_signal_blocker
+ : private noncopyable
+{
+public:
+ // Constructor blocks all signals for the calling thread.
+ null_signal_blocker()
+ {
+ }
+
+ // Destructor restores the previous signal mask.
+ ~null_signal_blocker()
+ {
+ }
+
+ // Block all signals for the calling thread.
+ void block()
+ {
+ }
+
+ // Restore the previous signal mask.
+ void unblock()
+ {
+ }
+};
+
+} // namespace detail
+} // namespace asio
+
+#endif // !defined(BOOST_HAS_THREADS)
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_DETAIL_NULL_SIGNAL_BLOCKER_HPP
diff --git a/library/include/libtorrent/asio/detail/null_thread.hpp b/library/include/libtorrent/asio/detail/null_thread.hpp
new file mode 100644
index 000000000..653caa651
--- /dev/null
+++ b/library/include/libtorrent/asio/detail/null_thread.hpp
@@ -0,0 +1,67 @@
+//
+// null_thread.hpp
+// ~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_NULL_THREAD_HPP
+#define ASIO_DETAIL_NULL_THREAD_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/detail/push_options.hpp"
+#include <boost/config.hpp>
+#include "asio/detail/pop_options.hpp"
+
+#if !defined(BOOST_HAS_THREADS)
+
+#include "asio/detail/push_options.hpp"
+#include <boost/throw_exception.hpp>
+#include "asio/detail/pop_options.hpp"
+
+#include "asio/error.hpp"
+#include "asio/system_exception.hpp"
+#include "asio/detail/noncopyable.hpp"
+
+namespace asio {
+namespace detail {
+
+class null_thread
+ : private noncopyable
+{
+public:
+ // Constructor.
+ template <typename Function>
+ null_thread(Function f)
+ {
+ system_exception e("thread", asio::error::not_supported);
+ boost::throw_exception(e);
+ }
+
+ // Destructor.
+ ~null_thread()
+ {
+ }
+
+ // Wait for the thread to exit.
+ void join()
+ {
+ }
+};
+
+} // namespace detail
+} // namespace asio
+
+#endif // !defined(BOOST_HAS_THREADS)
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_DETAIL_NULL_THREAD_HPP
diff --git a/library/include/libtorrent/asio/detail/null_tss_ptr.hpp b/library/include/libtorrent/asio/detail/null_tss_ptr.hpp
new file mode 100644
index 000000000..707e31e79
--- /dev/null
+++ b/library/include/libtorrent/asio/detail/null_tss_ptr.hpp
@@ -0,0 +1,70 @@
+//
+// null_tss_ptr.hpp
+// ~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_NULL_TSS_PTR_HPP
+#define ASIO_DETAIL_NULL_TSS_PTR_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/detail/push_options.hpp"
+#include <boost/config.hpp>
+#include "asio/detail/pop_options.hpp"
+
+#if !defined(BOOST_HAS_THREADS)
+
+#include "asio/detail/noncopyable.hpp"
+
+namespace asio {
+namespace detail {
+
+template <typename T>
+class null_tss_ptr
+ : private noncopyable
+{
+public:
+ // Constructor.
+ null_tss_ptr()
+ : value_(0)
+ {
+ }
+
+ // Destructor.
+ ~null_tss_ptr()
+ {
+ }
+
+ // Get the value.
+ operator T*() const
+ {
+ return value_;
+ }
+
+ // Set the value.
+ void operator=(T* value)
+ {
+ value_ = value;
+ }
+
+private:
+ T* value_;
+};
+
+} // namespace detail
+} // namespace asio
+
+#endif // !defined(BOOST_HAS_THREADS)
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_DETAIL_NULL_TSS_PTR_HPP
diff --git a/library/include/libtorrent/asio/detail/old_win_sdk_compat.hpp b/library/include/libtorrent/asio/detail/old_win_sdk_compat.hpp
new file mode 100644
index 000000000..24c42ad67
--- /dev/null
+++ b/library/include/libtorrent/asio/detail/old_win_sdk_compat.hpp
@@ -0,0 +1,312 @@
+//
+// old_win_sdk_compat.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_OLD_WIN_SDK_COMPAT_HPP
+#define ASIO_DETAIL_OLD_WIN_SDK_COMPAT_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/detail/push_options.hpp"
+#include <boost/config.hpp>
+#include "asio/detail/pop_options.hpp"
+
+#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+
+// Guess whether we are building against on old Platform SDK.
+#if !defined(IPPROTO_IPV6)
+#define ASIO_HAS_OLD_WIN_SDK 1
+#endif // !defined(IPPROTO_IPV6)
+
+#if defined(ASIO_HAS_OLD_WIN_SDK)
+
+// Emulation of types that are missing from old Platform SDKs.
+
+namespace asio {
+namespace detail {
+
+enum
+{
+ sockaddr_storage_maxsize = 128, // Maximum size.
+ sockaddr_storage_alignsize = (sizeof(__int64)), // Desired alignment.
+ sockaddr_storage_pad1size = (sockaddr_storage_alignsize - sizeof(short)),
+ sockaddr_storage_pad2size = (sockaddr_storage_maxsize -
+ (sizeof(short) + sockaddr_storage_pad1size + sockaddr_storage_alignsize))
+};
+
+struct sockaddr_storage_emulation
+{
+ short ss_family;
+ char __ss_pad1[sockaddr_storage_pad1size];
+ __int64 __ss_align;
+ char __ss_pad2[sockaddr_storage_pad2size];
+};
+
+struct in6_addr_emulation
+{
+ u_char s6_addr[16];
+};
+
+struct sockaddr_in6_emulation
+{
+ short sin6_family;
+ u_short sin6_port;
+ u_long sin6_flowinfo;
+ in6_addr_emulation sin6_addr;
+ u_long sin6_scope_id;
+};
+
+struct ipv6_mreq_emulation
+{
+ in6_addr_emulation ipv6mr_multiaddr;
+ unsigned int ipv6mr_interface;
+};
+
+#if !defined(IN6ADDR_ANY_INIT)
+# define IN6ADDR_ANY_INIT { 0 }
+#endif
+
+#if !defined(IN6ADDR_LOOPBACK_INIT)
+# define IN6ADDR_LOOPBACK_INIT { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 }
+#endif
+
+struct addrinfo_emulation
+{
+ int ai_flags;
+ int ai_family;
+ int ai_socktype;
+ int ai_protocol;
+ size_t ai_addrlen;
+ char* ai_canonname;
+ sockaddr* ai_addr;
+ addrinfo_emulation* ai_next;
+};
+
+#if !defined(AI_PASSIVE)
+# define AI_PASSIVE 0x1
+#endif
+
+#if !defined(AI_CANONNAME)
+# define AI_CANONNAME 0x2
+#endif
+
+#if !defined(AI_NUMERICHOST)
+# define AI_NUMERICHOST 0x4
+#endif
+
+#if !defined(EAI_AGAIN)
+# define EAI_AGAIN WSATRY_AGAIN
+#endif
+
+#if !defined(EAI_BADFLAGS)
+# define EAI_BADFLAGS WSAEINVAL
+#endif
+
+#if !defined(EAI_FAIL)
+# define EAI_FAIL WSANO_RECOVERY
+#endif
+
+#if !defined(EAI_FAMILY)
+# define EAI_FAMILY WSAEAFNOSUPPORT
+#endif
+
+#if !defined(EAI_MEMORY)
+# define EAI_MEMORY WSA_NOT_ENOUGH_MEMORY
+#endif
+
+#if !defined(EAI_NODATA)
+# define EAI_NODATA WSANO_DATA
+#endif
+
+#if !defined(EAI_NONAME)
+# define EAI_NONAME WSAHOST_NOT_FOUND
+#endif
+
+#if !defined(EAI_SERVICE)
+# define EAI_SERVICE WSATYPE_NOT_FOUND
+#endif
+
+#if !defined(EAI_SOCKTYPE)
+# define EAI_SOCKTYPE WSAESOCKTNOSUPPORT
+#endif
+
+#if !defined(NI_NOFQDN)
+# define NI_NOFQDN 0x01
+#endif
+
+#if !defined(NI_NUMERICHOST)
+# define NI_NUMERICHOST 0x02
+#endif
+
+#if !defined(NI_NAMEREQD)
+# define NI_NAMEREQD 0x04
+#endif
+
+#if !defined(NI_NUMERICSERV)
+# define NI_NUMERICSERV 0x08
+#endif
+
+#if !defined(NI_DGRAM)
+# define NI_DGRAM 0x10
+#endif
+
+#if !defined(IPPROTO_IPV6)
+# define IPPROTO_IPV6 41
+#endif
+
+#if !defined(IPV6_MULTICAST_IF)
+# define IPV6_MULTICAST_IF 9
+#endif
+
+#if !defined(IPV6_MULTICAST_HOPS)
+# define IPV6_MULTICAST_HOPS 10
+#endif
+
+#if !defined(IPV6_MULTICAST_LOOP)
+# define IPV6_MULTICAST_LOOP 11
+#endif
+
+#if !defined(IPV6_JOIN_GROUP)
+# define IPV6_JOIN_GROUP 12
+#endif
+
+#if !defined(IPV6_LEAVE_GROUP)
+# define IPV6_LEAVE_GROUP 13
+#endif
+
+inline int IN6_IS_ADDR_UNSPECIFIED(const in6_addr_emulation* a)
+{
+ return ((a->s6_addr[0] == 0)
+ && (a->s6_addr[1] == 0)
+ && (a->s6_addr[2] == 0)
+ && (a->s6_addr[3] == 0)
+ && (a->s6_addr[4] == 0)
+ && (a->s6_addr[5] == 0)
+ && (a->s6_addr[6] == 0)
+ && (a->s6_addr[7] == 0)
+ && (a->s6_addr[8] == 0)
+ && (a->s6_addr[9] == 0)
+ && (a->s6_addr[10] == 0)
+ && (a->s6_addr[11] == 0)
+ && (a->s6_addr[12] == 0)
+ && (a->s6_addr[13] == 0)
+ && (a->s6_addr[14] == 0)
+ && (a->s6_addr[15] == 0));
+}
+
+inline int IN6_IS_ADDR_LOOPBACK(const in6_addr_emulation* a)
+{
+ return ((a->s6_addr[0] == 0)
+ && (a->s6_addr[1] == 0)
+ && (a->s6_addr[2] == 0)
+ && (a->s6_addr[3] == 0)
+ && (a->s6_addr[4] == 0)
+ && (a->s6_addr[5] == 0)
+ && (a->s6_addr[6] == 0)
+ && (a->s6_addr[7] == 0)
+ && (a->s6_addr[8] == 0)
+ && (a->s6_addr[9] == 0)
+ && (a->s6_addr[10] == 0)
+ && (a->s6_addr[11] == 0)
+ && (a->s6_addr[12] == 0)
+ && (a->s6_addr[13] == 0)
+ && (a->s6_addr[14] == 0)
+ && (a->s6_addr[15] == 1));
+}
+
+inline int IN6_IS_ADDR_MULTICAST(const in6_addr_emulation* a)
+{
+ return (a->s6_addr[0] == 0xff);
+}
+
+inline int IN6_IS_ADDR_LINKLOCAL(const in6_addr_emulation* a)
+{
+ return ((a->s6_addr[0] == 0xfe) && ((a->s6_addr[1] & 0xc0) == 0x80));
+}
+
+inline int IN6_IS_ADDR_SITELOCAL(const in6_addr_emulation* a)
+{
+ return ((a->s6_addr[0] == 0xfe) && ((a->s6_addr[1] & 0xc0) == 0xc0));
+}
+
+inline int IN6_IS_ADDR_V4MAPPED(const in6_addr_emulation* a)
+{
+ return ((a->s6_addr[0] == 0)
+ && (a->s6_addr[1] == 0)
+ && (a->s6_addr[2] == 0)
+ && (a->s6_addr[3] == 0)
+ && (a->s6_addr[4] == 0)
+ && (a->s6_addr[5] == 0)
+ && (a->s6_addr[6] == 0)
+ && (a->s6_addr[7] == 0)
+ && (a->s6_addr[8] == 0)
+ && (a->s6_addr[9] == 0)
+ && (a->s6_addr[10] == 0xff)
+ && (a->s6_addr[11] == 0xff));
+}
+
+inline int IN6_IS_ADDR_V4COMPAT(const in6_addr_emulation* a)
+{
+ return ((a->s6_addr[0] == 0)
+ && (a->s6_addr[1] == 0)
+ && (a->s6_addr[2] == 0)
+ && (a->s6_addr[3] == 0)
+ && (a->s6_addr[4] == 0)
+ && (a->s6_addr[5] == 0)
+ && (a->s6_addr[6] == 0)
+ && (a->s6_addr[7] == 0)
+ && (a->s6_addr[8] == 0)
+ && (a->s6_addr[9] == 0)
+ && (a->s6_addr[10] == 0xff)
+ && (a->s6_addr[11] == 0xff)
+ && !((a->s6_addr[12] == 0)
+ && (a->s6_addr[13] == 0)
+ && (a->s6_addr[14] == 0)
+ && ((a->s6_addr[15] == 0) || (a->s6_addr[15] == 1))));
+}
+
+inline int IN6_IS_ADDR_MC_NODELOCAL(const in6_addr_emulation* a)
+{
+ return IN6_IS_ADDR_MULTICAST(a) && ((a->s6_addr[1] & 0xf) == 1);
+}
+
+inline int IN6_IS_ADDR_MC_LINKLOCAL(const in6_addr_emulation* a)
+{
+ return IN6_IS_ADDR_MULTICAST(a) && ((a->s6_addr[1] & 0xf) == 2);
+}
+
+inline int IN6_IS_ADDR_MC_SITELOCAL(const in6_addr_emulation* a)
+{
+ return IN6_IS_ADDR_MULTICAST(a) && ((a->s6_addr[1] & 0xf) == 5);
+}
+
+inline int IN6_IS_ADDR_MC_ORGLOCAL(const in6_addr_emulation* a)
+{
+ return IN6_IS_ADDR_MULTICAST(a) && ((a->s6_addr[1] & 0xf) == 8);
+}
+
+inline int IN6_IS_ADDR_MC_GLOBAL(const in6_addr_emulation* a)
+{
+ return IN6_IS_ADDR_MULTICAST(a) && ((a->s6_addr[1] & 0xf) == 0xe);
+}
+
+} // namespace detail
+} // namespace asio
+
+#endif // defined(ASIO_HAS_OLD_WIN_SDK)
+
+#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_DETAIL_OLD_WIN_SDK_COMPAT_HPP
diff --git a/library/include/libtorrent/asio/detail/pipe_select_interrupter.hpp b/library/include/libtorrent/asio/detail/pipe_select_interrupter.hpp
new file mode 100644
index 000000000..b73fb3f2e
--- /dev/null
+++ b/library/include/libtorrent/asio/detail/pipe_select_interrupter.hpp
@@ -0,0 +1,104 @@
+//
+// pipe_select_interrupter.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_PIPE_SELECT_INTERRUPTER_HPP
+#define ASIO_DETAIL_PIPE_SELECT_INTERRUPTER_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/detail/push_options.hpp"
+#include <boost/config.hpp>
+#include "asio/detail/pop_options.hpp"
+
+#if !defined(BOOST_WINDOWS) && !defined(__CYGWIN__)
+
+#include "asio/detail/push_options.hpp"
+#include <fcntl.h>
+#include "asio/detail/pop_options.hpp"
+
+#include "asio/detail/socket_types.hpp"
+
+namespace asio {
+namespace detail {
+
+class pipe_select_interrupter
+{
+public:
+ // Constructor.
+ pipe_select_interrupter()
+ {
+ int pipe_fds[2];
+ if (pipe(pipe_fds) == 0)
+ {
+ read_descriptor_ = pipe_fds[0];
+ ::fcntl(read_descriptor_, F_SETFL, O_NONBLOCK);
+ write_descriptor_ = pipe_fds[1];
+ ::fcntl(write_descriptor_, F_SETFL, O_NONBLOCK);
+ }
+ }
+
+ // Destructor.
+ ~pipe_select_interrupter()
+ {
+ if (read_descriptor_ != -1)
+ ::close(read_descriptor_);
+ if (write_descriptor_ != -1)
+ ::close(write_descriptor_);
+ }
+
+ // Interrupt the select call.
+ void interrupt()
+ {
+ char byte = 0;
+ ::write(write_descriptor_, &byte, 1);
+ }
+
+ // Reset the select interrupt. Returns true if the call was interrupted.
+ bool reset()
+ {
+ char data[1024];
+ int bytes_read = ::read(read_descriptor_, data, sizeof(data));
+ bool was_interrupted = (bytes_read > 0);
+ while (bytes_read == sizeof(data))
+ bytes_read = ::read(read_descriptor_, data, sizeof(data));
+ return was_interrupted;
+ }
+
+ // Get the read descriptor to be passed to select.
+ int read_descriptor() const
+ {
+ return read_descriptor_;
+ }
+
+private:
+ // The read end of a connection used to interrupt the select call. This file
+ // descriptor is passed to select such that when it is time to stop, a single
+ // byte will be written on the other end of the connection and this
+ // descriptor will become readable.
+ int read_descriptor_;
+
+ // The write end of a connection used to interrupt the select call. A single
+ // byte may be written to this to wake up the select which is waiting for the
+ // other end to become readable.
+ int write_descriptor_;
+};
+
+} // namespace detail
+} // namespace asio
+
+#endif // !defined(BOOST_WINDOWS) && !defined(__CYGWIN__)
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_DETAIL_PIPE_SELECT_INTERRUPTER_HPP
diff --git a/library/include/libtorrent/asio/detail/pop_options.hpp b/library/include/libtorrent/asio/detail/pop_options.hpp
new file mode 100644
index 000000000..4c942e8d3
--- /dev/null
+++ b/library/include/libtorrent/asio/detail/pop_options.hpp
@@ -0,0 +1,88 @@
+//
+// pop_options.hpp
+// ~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+// No header guard
+
+#if defined(__COMO__)
+
+// Comeau C++
+
+#elif defined(__DMC__)
+
+// Digital Mars C++
+
+#elif defined(__INTEL_COMPILER) || defined(__ICL) \
+ || defined(__ICC) || defined(__ECC)
+
+// Intel C++
+
+#elif defined(__GNUC__)
+
+// GNU C++
+
+# if defined(__MINGW32__) || defined(__CYGWIN__)
+# pragma pack (pop)
+# endif
+
+#elif defined(__KCC)
+
+// Kai C++
+
+#elif defined(__sgi)
+
+// SGI MIPSpro C++
+
+#elif defined(__DECCXX)
+
+// Compaq Tru64 Unix cxx
+
+#elif defined(__ghs)
+
+// Greenhills C++
+
+#elif defined(__BORLANDC__)
+
+// Borland C++
+
+# pragma option pop
+# pragma nopushoptwarn
+# pragma nopackwarning
+
+#elif defined(__MWERKS__)
+
+// Metrowerks CodeWarrior
+
+#elif defined(__SUNPRO_CC)
+
+// Sun Workshop Compiler C++
+
+#elif defined(__HP_aCC)
+
+// HP aCC
+
+#elif defined(__MRC__) || defined(__SC__)
+
+// MPW MrCpp or SCpp
+
+#elif defined(__IBMCPP__)
+
+// IBM Visual Age
+
+#elif defined(_MSC_VER)
+
+// Microsoft Visual C++
+//
+// Must remain the last #elif since some other vendors (Metrowerks, for example)
+// also #define _MSC_VER
+
+# pragma warning (pop)
+# pragma pack (pop)
+
+#endif
diff --git a/library/include/libtorrent/asio/detail/posix_event.hpp b/library/include/libtorrent/asio/detail/posix_event.hpp
new file mode 100644
index 000000000..9d84bfa58
--- /dev/null
+++ b/library/include/libtorrent/asio/detail/posix_event.hpp
@@ -0,0 +1,107 @@
+//
+// posix_event.hpp
+// ~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_POSIX_EVENT_HPP
+#define ASIO_DETAIL_POSIX_EVENT_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/detail/push_options.hpp"
+#include <boost/config.hpp>
+#include "asio/detail/pop_options.hpp"
+
+#if defined(BOOST_HAS_PTHREADS)
+
+#include "asio/detail/push_options.hpp"
+#include <boost/throw_exception.hpp>
+#include <pthread.h>
+#include "asio/detail/pop_options.hpp"
+
+#include "asio/system_exception.hpp"
+#include "asio/detail/noncopyable.hpp"
+
+namespace asio {
+namespace detail {
+
+class posix_event
+ : private noncopyable
+{
+public:
+ // Constructor.
+ posix_event()
+ : signalled_(false)
+ {
+ int error = ::pthread_mutex_init(&mutex_, 0);
+ if (error != 0)
+ {
+ system_exception e("event", error);
+ boost::throw_exception(e);
+ }
+
+ error = ::pthread_cond_init(&cond_, 0);
+ if (error != 0)
+ {
+ ::pthread_mutex_destroy(&mutex_);
+ system_exception e("event", error);
+ boost::throw_exception(e);
+ }
+ }
+
+ // Destructor.
+ ~posix_event()
+ {
+ ::pthread_cond_destroy(&cond_);
+ ::pthread_mutex_destroy(&mutex_);
+ }
+
+ // Signal the event.
+ void signal()
+ {
+ ::pthread_mutex_lock(&mutex_); // Ignore EINVAL and EDEADLK.
+ signalled_ = true;
+ ::pthread_cond_signal(&cond_); // Ignore EINVAL.
+ ::pthread_mutex_unlock(&mutex_); // Ignore EINVAL and EPERM.
+ }
+
+ // Reset the event.
+ void clear()
+ {
+ ::pthread_mutex_lock(&mutex_); // Ignore EINVAL and EDEADLK.
+ signalled_ = false;
+ ::pthread_mutex_unlock(&mutex_); // Ignore EINVAL and EPERM.
+ }
+
+ // Wait for the event to become signalled.
+ void wait()
+ {
+ ::pthread_mutex_lock(&mutex_); // Ignore EINVAL and EDEADLK.
+ while (!signalled_)
+ ::pthread_cond_wait(&cond_, &mutex_); // Ignore EINVAL.
+ ::pthread_mutex_unlock(&mutex_); // Ignore EINVAL and EPERM.
+ }
+
+private:
+ ::pthread_mutex_t mutex_;
+ ::pthread_cond_t cond_;
+ bool signalled_;
+};
+
+} // namespace detail
+} // namespace asio
+
+#endif // defined(BOOST_HAS_PTHREADS)
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_DETAIL_POSIX_EVENT_HPP
diff --git a/library/include/libtorrent/asio/detail/posix_fd_set_adapter.hpp b/library/include/libtorrent/asio/detail/posix_fd_set_adapter.hpp
new file mode 100644
index 000000000..56d8ff3dc
--- /dev/null
+++ b/library/include/libtorrent/asio/detail/posix_fd_set_adapter.hpp
@@ -0,0 +1,71 @@
+//
+// posix_fd_set_adapter.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_POSIX_FD_SET_ADAPTER_HPP
+#define ASIO_DETAIL_POSIX_FD_SET_ADAPTER_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/detail/socket_types.hpp"
+
+#if !defined(BOOST_WINDOWS) && !defined(__CYGWIN__)
+
+namespace asio {
+namespace detail {
+
+// Adapts the FD_SET type to meet the Descriptor_Set concept's requirements.
+class posix_fd_set_adapter
+{
+public:
+ posix_fd_set_adapter()
+ : max_descriptor_(invalid_socket)
+ {
+ FD_ZERO(&fd_set_);
+ }
+
+ void set(socket_type descriptor)
+ {
+ if (max_descriptor_ == invalid_socket || descriptor > max_descriptor_)
+ max_descriptor_ = descriptor;
+ FD_SET(descriptor, &fd_set_);
+ }
+
+ bool is_set(socket_type descriptor) const
+ {
+ return FD_ISSET(descriptor, &fd_set_) != 0;
+ }
+
+ operator fd_set*()
+ {
+ return &fd_set_;
+ }
+
+ socket_type max_descriptor() const
+ {
+ return max_descriptor_;
+ }
+
+private:
+ fd_set fd_set_;
+ socket_type max_descriptor_;
+};
+
+} // namespace detail
+} // namespace asio
+
+#endif // !defined(BOOST_WINDOWS) && !defined(__CYGWIN__)
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_DETAIL_POSIX_FD_SET_ADAPTER_HPP
diff --git a/library/include/libtorrent/asio/detail/posix_mutex.hpp b/library/include/libtorrent/asio/detail/posix_mutex.hpp
new file mode 100644
index 000000000..4e9df4a75
--- /dev/null
+++ b/library/include/libtorrent/asio/detail/posix_mutex.hpp
@@ -0,0 +1,94 @@
+//
+// posix_mutex.hpp
+// ~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_POSIX_MUTEX_HPP
+#define ASIO_DETAIL_POSIX_MUTEX_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/detail/push_options.hpp"
+#include <boost/config.hpp>
+#include "asio/detail/pop_options.hpp"
+
+#if defined(BOOST_HAS_PTHREADS)
+
+#include "asio/detail/push_options.hpp"
+#include <boost/throw_exception.hpp>
+#include <pthread.h>
+#include "asio/detail/pop_options.hpp"
+
+#include "asio/system_exception.hpp"
+#include "asio/detail/noncopyable.hpp"
+#include "asio/detail/scoped_lock.hpp"
+
+namespace asio {
+namespace detail {
+
+class posix_mutex
+ : private noncopyable
+{
+public:
+ typedef asio::detail::scoped_lock<posix_mutex> scoped_lock;
+
+ // Constructor.
+ posix_mutex()
+ {
+ int error = ::pthread_mutex_init(&mutex_, 0);
+ if (error != 0)
+ {
+ system_exception e("mutex", error);
+ boost::throw_exception(e);
+ }
+ }
+
+ // Destructor.
+ ~posix_mutex()
+ {
+ ::pthread_mutex_destroy(&mutex_);
+ }
+
+ // Lock the mutex.
+ void lock()
+ {
+ int error = ::pthread_mutex_lock(&mutex_);
+ if (error != 0)
+ {
+ system_exception e("mutex", error);
+ boost::throw_exception(e);
+ }
+ }
+
+ // Unlock the mutex.
+ void unlock()
+ {
+ int error = ::pthread_mutex_unlock(&mutex_);
+ if (error != 0)
+ {
+ system_exception e("mutex", error);
+ boost::throw_exception(e);
+ }
+ }
+
+private:
+ ::pthread_mutex_t mutex_;
+};
+
+} // namespace detail
+} // namespace asio
+
+#endif // defined(BOOST_HAS_PTHREADS)
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_DETAIL_POSIX_MUTEX_HPP
diff --git a/library/include/libtorrent/asio/detail/posix_signal_blocker.hpp b/library/include/libtorrent/asio/detail/posix_signal_blocker.hpp
new file mode 100644
index 000000000..c11fc5ef6
--- /dev/null
+++ b/library/include/libtorrent/asio/detail/posix_signal_blocker.hpp
@@ -0,0 +1,89 @@
+//
+// posix_signal_blocker.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_POSIX_SIGNAL_BLOCKER_HPP
+#define ASIO_DETAIL_POSIX_SIGNAL_BLOCKER_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/detail/push_options.hpp"
+#include <boost/config.hpp>
+#include "asio/detail/pop_options.hpp"
+
+#if defined(BOOST_HAS_PTHREADS)
+
+#include "asio/detail/push_options.hpp"
+#include <csignal>
+#include <pthread.h>
+#include "asio/detail/pop_options.hpp"
+
+#include "asio/detail/noncopyable.hpp"
+
+namespace asio {
+namespace detail {
+
+class posix_signal_blocker
+ : private noncopyable
+{
+public:
+ // Constructor blocks all signals for the calling thread.
+ posix_signal_blocker()
+ : blocked_(false)
+ {
+ sigset_t new_mask;
+ sigfillset(&new_mask);
+ blocked_ = (pthread_sigmask(SIG_BLOCK, &new_mask, &old_mask_) == 0);
+ }
+
+ // Destructor restores the previous signal mask.
+ ~posix_signal_blocker()
+ {
+ if (blocked_)
+ pthread_sigmask(SIG_SETMASK, &old_mask_, 0);
+ }
+
+ // Block all signals for the calling thread.
+ void block()
+ {
+ if (!blocked_)
+ {
+ sigset_t new_mask;
+ sigfillset(&new_mask);
+ blocked_ = (pthread_sigmask(SIG_BLOCK, &new_mask, &old_mask_) == 0);
+ }
+ }
+
+ // Restore the previous signal mask.
+ void unblock()
+ {
+ if (blocked_)
+ blocked_ = (pthread_sigmask(SIG_SETMASK, &old_mask_, 0) != 0);
+ }
+
+private:
+ // Have signals been blocked.
+ bool blocked_;
+
+ // The previous signal mask.
+ sigset_t old_mask_;
+};
+
+} // namespace detail
+} // namespace asio
+
+#endif // defined(BOOST_HAS_PTHREADS)
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_DETAIL_POSIX_SIGNAL_BLOCKER_HPP
diff --git a/library/include/libtorrent/asio/detail/posix_thread.hpp b/library/include/libtorrent/asio/detail/posix_thread.hpp
new file mode 100644
index 000000000..7d738fa4b
--- /dev/null
+++ b/library/include/libtorrent/asio/detail/posix_thread.hpp
@@ -0,0 +1,125 @@
+//
+// posix_thread.hpp
+// ~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_POSIX_THREAD_HPP
+#define ASIO_DETAIL_POSIX_THREAD_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/detail/push_options.hpp"
+#include <boost/config.hpp>
+#include "asio/detail/pop_options.hpp"
+
+#if defined(BOOST_HAS_PTHREADS)
+
+#include "asio/detail/push_options.hpp"
+#include <memory>
+#include <boost/throw_exception.hpp>
+#include <pthread.h>
+#include "asio/detail/pop_options.hpp"
+
+#include "asio/system_exception.hpp"
+#include "asio/detail/noncopyable.hpp"
+
+namespace asio {
+namespace detail {
+
+extern "C" void* asio_detail_posix_thread_function(void* arg);
+
+class posix_thread
+ : private noncopyable
+{
+public:
+ // Constructor.
+ template <typename Function>
+ posix_thread(Function f)
+ : joined_(false)
+ {
+ std::auto_ptr<func_base> arg(new func<Function>(f));
+ int error = ::pthread_create(&thread_, 0,
+ asio_detail_posix_thread_function, arg.get());
+ if (error != 0)
+ {
+ system_exception e("thread", error);
+ boost::throw_exception(e);
+ }
+ arg.release();
+ }
+
+ // Destructor.
+ ~posix_thread()
+ {
+ if (!joined_)
+ ::pthread_detach(thread_);
+ }
+
+ // Wait for the thread to exit.
+ void join()
+ {
+ if (!joined_)
+ {
+ ::pthread_join(thread_, 0);
+ joined_ = true;
+ }
+ }
+
+private:
+ friend void* asio_detail_posix_thread_function(void* arg);
+
+ class func_base
+ {
+ public:
+ virtual ~func_base() {}
+ virtual void run() = 0;
+ };
+
+ template <typename Function>
+ class func
+ : public func_base
+ {
+ public:
+ func(Function f)
+ : f_(f)
+ {
+ }
+
+ virtual void run()
+ {
+ f_();
+ }
+
+ private:
+ Function f_;
+ };
+
+ ::pthread_t thread_;
+ bool joined_;
+};
+
+inline void* asio_detail_posix_thread_function(void* arg)
+{
+ std::auto_ptr<posix_thread::func_base> f(
+ static_cast<posix_thread::func_base*>(arg));
+ f->run();
+ return 0;
+}
+
+} // namespace detail
+} // namespace asio
+
+#endif // defined(BOOST_HAS_PTHREADS)
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_DETAIL_POSIX_THREAD_HPP
diff --git a/library/include/libtorrent/asio/detail/posix_tss_ptr.hpp b/library/include/libtorrent/asio/detail/posix_tss_ptr.hpp
new file mode 100644
index 000000000..961120ee9
--- /dev/null
+++ b/library/include/libtorrent/asio/detail/posix_tss_ptr.hpp
@@ -0,0 +1,84 @@
+//
+// posix_tss_ptr.hpp
+// ~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_POSIX_TSS_PTR_HPP
+#define ASIO_DETAIL_POSIX_TSS_PTR_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/detail/push_options.hpp"
+#include <boost/config.hpp>
+#include "asio/detail/pop_options.hpp"
+
+#if defined(BOOST_HAS_PTHREADS)
+
+#include "asio/detail/push_options.hpp"
+#include <boost/throw_exception.hpp>
+#include <pthread.h>
+#include "asio/detail/pop_options.hpp"
+
+#include "asio/system_exception.hpp"
+#include "asio/detail/noncopyable.hpp"
+
+namespace asio {
+namespace detail {
+
+template <typename T>
+class posix_tss_ptr
+ : private noncopyable
+{
+public:
+ // Constructor.
+ posix_tss_ptr()
+ {
+ int error = ::pthread_key_create(&tss_key_, 0);
+ if (error != 0)
+ {
+ system_exception e("tss", error);
+ boost::throw_exception(e);
+ }
+ }
+
+ // Destructor.
+ ~posix_tss_ptr()
+ {
+ ::pthread_key_delete(tss_key_);
+ }
+
+ // Get the value.
+ operator T*() const
+ {
+ return static_cast<T*>(::pthread_getspecific(tss_key_));
+ }
+
+ // Set the value.
+ void operator=(T* value)
+ {
+ ::pthread_setspecific(tss_key_, value);
+ }
+
+private:
+ // Thread-specific storage to allow unlocked access to determine whether a
+ // thread is a member of the pool.
+ pthread_key_t tss_key_;
+};
+
+} // namespace detail
+} // namespace asio
+
+#endif // defined(BOOST_HAS_PTHREADS)
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_DETAIL_POSIX_TSS_PTR_HPP
diff --git a/library/include/libtorrent/asio/detail/push_options.hpp b/library/include/libtorrent/asio/detail/push_options.hpp
new file mode 100644
index 000000000..45493ed29
--- /dev/null
+++ b/library/include/libtorrent/asio/detail/push_options.hpp
@@ -0,0 +1,106 @@
+//
+// push_options.hpp
+// ~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+// No header guard
+
+#if defined(__COMO__)
+
+// Comeau C++
+
+#elif defined(__DMC__)
+
+// Digital Mars C++
+
+#elif defined(__INTEL_COMPILER) || defined(__ICL) \
+ || defined(__ICC) || defined(__ECC)
+
+// Intel C++
+
+#elif defined(__GNUC__)
+
+// GNU C++
+
+# if defined(__MINGW32__) || defined(__CYGWIN__)
+# pragma pack (push, 8)
+# endif
+
+#elif defined(__KCC)
+
+// Kai C++
+
+#elif defined(__sgi)
+
+// SGI MIPSpro C++
+
+#elif defined(__DECCXX)
+
+// Compaq Tru64 Unix cxx
+
+#elif defined(__ghs)
+
+// Greenhills C++
+
+#elif defined(__BORLANDC__)
+
+// Borland C++
+
+# pragma option push -a8 -b -Ve- -Vx- -w-inl -vi-
+# pragma nopushoptwarn
+# pragma nopackwarning
+# if !defined(__MT__)
+# error Multithreaded RTL must be selected.
+# endif // !defined(__MT__)
+
+#elif defined(__MWERKS__)
+
+// Metrowerks CodeWarrior
+
+#elif defined(__SUNPRO_CC)
+
+// Sun Workshop Compiler C++
+
+#elif defined(__HP_aCC)
+
+// HP aCC
+
+#elif defined(__MRC__) || defined(__SC__)
+
+// MPW MrCpp or SCpp
+
+#elif defined(__IBMCPP__)
+
+// IBM Visual Age
+
+#elif defined(_MSC_VER)
+
+// Microsoft Visual C++
+//
+// Must remain the last #elif since some other vendors (Metrowerks, for example)
+// also #define _MSC_VER
+
+# pragma warning (disable:4103)
+# pragma warning (push)
+# pragma warning (disable:4244)
+# pragma warning (disable:4355)
+# pragma warning (disable:4675)
+# pragma pack (push, 8)
+// Note that if the /Og optimisation flag is enabled with MSVC6, the compiler
+// has a tendency to incorrectly optimise away some calls to member template
+// functions, even though those functions contain code that should not be
+// optimised away! Therefore we will always disable this optimisation option
+// for the MSVC6 compiler.
+# if (_MSC_VER < 1300)
+# pragma optimize ("g", off)
+# endif
+# if !defined(_MT)
+# error Multithreaded RTL must be selected.
+# endif // !defined(_MT)
+
+#endif
diff --git a/library/include/libtorrent/asio/detail/reactive_socket_service.hpp b/library/include/libtorrent/asio/detail/reactive_socket_service.hpp
new file mode 100644
index 000000000..361f978af
--- /dev/null
+++ b/library/include/libtorrent/asio/detail/reactive_socket_service.hpp
@@ -0,0 +1,1640 @@
+//
+// reactive_socket_service.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_REACTIVE_SOCKET_SERVICE_HPP
+#define ASIO_DETAIL_REACTIVE_SOCKET_SERVICE_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/detail/push_options.hpp"
+#include <boost/shared_ptr.hpp>
+#include "asio/detail/pop_options.hpp"
+
+#include "asio/buffer.hpp"
+#include "asio/error.hpp"
+#include "asio/error_handler.hpp"
+#include "asio/io_service.hpp"
+#include "asio/socket_base.hpp"
+#include "asio/detail/bind_handler.hpp"
+#include "asio/detail/noncopyable.hpp"
+#include "asio/detail/socket_holder.hpp"
+#include "asio/detail/socket_ops.hpp"
+#include "asio/detail/socket_types.hpp"
+
+namespace asio {
+namespace detail {
+
+template <typename Protocol, typename Reactor>
+class reactive_socket_service
+ : public asio::io_service::service
+{
+public:
+ // The protocol type.
+ typedef Protocol protocol_type;
+
+ // The endpoint type.
+ typedef typename Protocol::endpoint endpoint_type;
+
+ // The native type of a socket.
+ typedef socket_type native_type;
+
+ // The implementation type of the socket.
+ class implementation_type
+ : private asio::detail::noncopyable
+ {
+ public:
+ // Default constructor.
+ implementation_type()
+ : socket_(invalid_socket),
+ flags_(0),
+ protocol_(endpoint_type().protocol())
+ {
+ }
+
+ private:
+ // Only this service will have access to the internal values.
+ friend class reactive_socket_service<Protocol, Reactor>;
+
+ // The native socket representation.
+ socket_type socket_;
+
+ enum
+ {
+ user_set_non_blocking = 1, // The user wants a non-blocking socket.
+ internal_non_blocking = 2, // The socket has been set non-blocking.
+ enable_connection_aborted = 4, // User wants connection_aborted errors.
+ user_set_linger = 8 // The user set the linger option.
+ };
+
+ // Flags indicating the current state of the socket.
+ unsigned char flags_;
+
+ // The protocol associated with the socket.
+ protocol_type protocol_;
+ };
+
+ // The maximum number of buffers to support in a single operation.
+ enum { max_buffers = 16 };
+
+ // Constructor.
+ reactive_socket_service(asio::io_service& io_service)
+ : asio::io_service::service(io_service),
+ reactor_(asio::use_service<Reactor>(io_service))
+ {
+ }
+
+ // Destroy all user-defined handler objects owned by the service.
+ void shutdown_service()
+ {
+ }
+
+ // Construct a new socket implementation.
+ void construct(implementation_type& impl)
+ {
+ impl.socket_ = invalid_socket;
+ impl.flags_ = 0;
+ }
+
+ // Destroy a socket implementation.
+ void destroy(implementation_type& impl)
+ {
+ if (impl.socket_ != invalid_socket)
+ {
+ reactor_.close_descriptor(impl.socket_);
+
+ if (impl.flags_ & implementation_type::internal_non_blocking)
+ {
+ ioctl_arg_type non_blocking = 0;
+ socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking);
+ impl.flags_ &= ~implementation_type::internal_non_blocking;
+ }
+
+ if (impl.flags_ & implementation_type::user_set_linger)
+ {
+ ::linger opt;
+ opt.l_onoff = 0;
+ opt.l_linger = 0;
+ socket_ops::setsockopt(impl.socket_,
+ SOL_SOCKET, SO_LINGER, &opt, sizeof(opt));
+ }
+
+ socket_ops::close(impl.socket_);
+ impl.socket_ = invalid_socket;
+ }
+ }
+
+ // Open a new socket implementation.
+ template <typename Error_Handler>
+ void open(implementation_type& impl, const protocol_type& protocol,
+ Error_Handler error_handler)
+ {
+ close(impl, asio::ignore_error());
+
+ socket_holder sock(socket_ops::socket(protocol.family(),
+ protocol.type(), protocol.protocol()));
+ if (sock.get() == invalid_socket)
+ {
+ error_handler(asio::error(socket_ops::get_error()));
+ return;
+ }
+
+ if (int err = reactor_.register_descriptor(sock.get()))
+ {
+ error_handler(asio::error(err));
+ return;
+ }
+
+ impl.socket_ = sock.release();
+ impl.flags_ = 0;
+ impl.protocol_ = protocol;
+
+ error_handler(asio::error(0));
+ }
+
+ // Assign a native socket to a socket implementation.
+ template <typename Error_Handler>
+ void assign(implementation_type& impl, const protocol_type& protocol,
+ const native_type& native_socket, Error_Handler error_handler)
+ {
+ close(impl, asio::ignore_error());
+
+ if (int err = reactor_.register_descriptor(native_socket))
+ {
+ error_handler(asio::error(err));
+ return;
+ }
+
+ impl.socket_ = native_socket;
+ impl.flags_ = 0;
+ impl.protocol_ = protocol;
+
+ error_handler(asio::error(0));
+ }
+
+ // Destroy a socket implementation.
+ template <typename Error_Handler>
+ void close(implementation_type& impl, Error_Handler error_handler)
+ {
+ if (impl.socket_ != invalid_socket)
+ {
+ reactor_.close_descriptor(impl.socket_);
+
+ if (impl.flags_ & implementation_type::internal_non_blocking)
+ {
+ ioctl_arg_type non_blocking = 0;
+ socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking);
+ impl.flags_ &= ~implementation_type::internal_non_blocking;
+ }
+
+ if (socket_ops::close(impl.socket_) == socket_error_retval)
+ {
+ error_handler(asio::error(socket_ops::get_error()));
+ return;
+ }
+
+ impl.socket_ = invalid_socket;
+ }
+
+ error_handler(asio::error(0));
+ }
+
+ // Get the native socket representation.
+ native_type native(implementation_type& impl)
+ {
+ return impl.socket_;
+ }
+
+ // Cancel all operations associated with the socket.
+ template <typename Error_Handler>
+ void cancel(implementation_type& impl, Error_Handler error_handler)
+ {
+ if (impl.socket_ == invalid_socket)
+ {
+ asio::error error(asio::error::bad_descriptor);
+ error_handler(error);
+ }
+ else
+ {
+ reactor_.cancel_ops(impl.socket_);
+ error_handler(asio::error(0));
+ }
+ }
+
+ // Bind the socket to the specified local endpoint.
+ template <typename Error_Handler>
+ void bind(implementation_type& impl, const endpoint_type& endpoint,
+ Error_Handler error_handler)
+ {
+ if (socket_ops::bind(impl.socket_, endpoint.data(),
+ endpoint.size()) == socket_error_retval)
+ error_handler(asio::error(socket_ops::get_error()));
+ else
+ error_handler(asio::error(0));
+ }
+
+ // Place the socket into the state where it will listen for new connections.
+ template <typename Error_Handler>
+ void listen(implementation_type& impl, int backlog,
+ Error_Handler error_handler)
+ {
+ if (socket_ops::listen(impl.socket_, backlog) == socket_error_retval)
+ error_handler(asio::error(socket_ops::get_error()));
+ else
+ error_handler(asio::error(0));
+ }
+
+ // Set a socket option.
+ template <typename Option, typename Error_Handler>
+ void set_option(implementation_type& impl, const Option& option,
+ Error_Handler error_handler)
+ {
+ if (option.level(impl.protocol_) == custom_socket_option_level
+ && option.name(impl.protocol_) == enable_connection_aborted_option)
+ {
+ if (option.size(impl.protocol_) != sizeof(int))
+ {
+ error_handler(asio::error(asio::error::invalid_argument));
+ }
+ else
+ {
+ if (*reinterpret_cast<const int*>(option.data(impl.protocol_)))
+ impl.flags_ |= implementation_type::enable_connection_aborted;
+ else
+ impl.flags_ &= ~implementation_type::enable_connection_aborted;
+ error_handler(asio::error(0));
+ }
+ }
+ else
+ {
+ if (option.level(impl.protocol_) == SOL_SOCKET
+ && option.name(impl.protocol_) == SO_LINGER)
+ {
+ impl.flags_ |= implementation_type::user_set_linger;
+ }
+
+ if (socket_ops::setsockopt(impl.socket_,
+ option.level(impl.protocol_), option.name(impl.protocol_),
+ option.data(impl.protocol_), option.size(impl.protocol_)))
+ error_handler(asio::error(socket_ops::get_error()));
+ else
+ error_handler(asio::error(0));
+ }
+ }
+
+ // Set a socket option.
+ template <typename Option, typename Error_Handler>
+ void get_option(const implementation_type& impl, Option& option,
+ Error_Handler error_handler) const
+ {
+ if (option.level(impl.protocol_) == custom_socket_option_level
+ && option.name(impl.protocol_) == enable_connection_aborted_option)
+ {
+ if (option.size(impl.protocol_) != sizeof(int))
+ {
+ error_handler(asio::error(asio::error::invalid_argument));
+ }
+ else
+ {
+ int* target = reinterpret_cast<int*>(option.data(impl.protocol_));
+ if (impl.flags_ & implementation_type::enable_connection_aborted)
+ *target = 1;
+ else
+ *target = 0;
+ error_handler(asio::error(0));
+ }
+ }
+ else
+ {
+ size_t size = option.size(impl.protocol_);
+ if (socket_ops::getsockopt(impl.socket_,
+ option.level(impl.protocol_), option.name(impl.protocol_),
+ option.data(impl.protocol_), &size))
+ error_handler(asio::error(socket_ops::get_error()));
+ else
+ error_handler(asio::error(0));
+ }
+ }
+
+ // Perform an IO control command on the socket.
+ template <typename IO_Control_Command, typename Error_Handler>
+ void io_control(implementation_type& impl, IO_Control_Command& command,
+ Error_Handler error_handler)
+ {
+ if (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;
+ error_handler(asio::error(0));
+ }
+ else
+ {
+ if (socket_ops::ioctl(impl.socket_, command.name(),
+ static_cast<ioctl_arg_type*>(command.data())))
+ error_handler(asio::error(socket_ops::get_error()));
+ else
+ error_handler(asio::error(0));
+ }
+ }
+
+ // Get the local endpoint.
+ template <typename Error_Handler>
+ void get_local_endpoint(const implementation_type& impl,
+ endpoint_type& endpoint, Error_Handler error_handler) const
+ {
+ socket_addr_len_type addr_len = endpoint.capacity();
+ if (socket_ops::getsockname(impl.socket_, endpoint.data(), &addr_len))
+ {
+ error_handler(asio::error(socket_ops::get_error()));
+ return;
+ }
+
+ endpoint.resize(addr_len);
+ error_handler(asio::error(0));
+ }
+
+ // Get the remote endpoint.
+ template <typename Error_Handler>
+ void get_remote_endpoint(const implementation_type& impl,
+ endpoint_type& endpoint, Error_Handler error_handler) const
+ {
+ socket_addr_len_type addr_len = endpoint.capacity();
+ if (socket_ops::getpeername(impl.socket_, endpoint.data(), &addr_len))
+ {
+ error_handler(asio::error(socket_ops::get_error()));
+ return;
+ }
+
+ endpoint.resize(addr_len);
+ error_handler(asio::error(0));
+ }
+
+ /// Disable sends or receives on the socket.
+ template <typename Error_Handler>
+ void shutdown(implementation_type& impl, socket_base::shutdown_type what,
+ Error_Handler error_handler)
+ {
+ if (socket_ops::shutdown(impl.socket_, what) != 0)
+ error_handler(asio::error(socket_ops::get_error()));
+ else
+ error_handler(asio::error(0));
+ }
+
+ // Send the given data to the peer.
+ template <typename Const_Buffers, typename Error_Handler>
+ size_t send(implementation_type& impl, const Const_Buffers& buffers,
+ socket_base::message_flags flags, Error_Handler error_handler)
+ {
+ // Copy buffers into array.
+ socket_ops::buf bufs[max_buffers];
+ typename Const_Buffers::const_iterator iter = buffers.begin();
+ typename Const_Buffers::const_iterator end = buffers.end();
+ size_t i = 0;
+ size_t total_buffer_size = 0;
+ for (; iter != end && i < max_buffers; ++iter, ++i)
+ {
+ asio::const_buffer buffer(*iter);
+ socket_ops::init_buf(bufs[i],
+ asio::buffer_cast<const void*>(buffer),
+ asio::buffer_size(buffer));
+ total_buffer_size += asio::buffer_size(buffer);
+ }
+
+ // A request to receive 0 bytes on a stream socket is a no-op.
+ if (impl.protocol_.type() == SOCK_STREAM && total_buffer_size == 0)
+ {
+ error_handler(asio::error(0));
+ return 0;
+ }
+
+ // Send the data.
+ for (;;)
+ {
+ // Try to complete the operation without blocking.
+ int bytes_sent = socket_ops::send(impl.socket_, bufs, i, flags);
+ int error = socket_ops::get_error();
+
+ // Check if operation succeeded.
+ if (bytes_sent >= 0)
+ {
+ error_handler(asio::error(0));
+ return bytes_sent;
+ }
+
+ // Operation failed.
+ if ((impl.flags_ & implementation_type::user_set_non_blocking)
+ || (error != asio::error::would_block
+ && error != asio::error::try_again))
+ {
+ error_handler(asio::error(error));
+ return 0;
+ }
+
+ // Wait for socket to become ready.
+ if (socket_ops::poll_write(impl.socket_) < 0)
+ {
+ error_handler(asio::error(socket_ops::get_error()));
+ return 0;
+ }
+ }
+ }
+
+ template <typename Const_Buffers, typename Handler>
+ class send_handler
+ {
+ public:
+ send_handler(socket_type socket, asio::io_service& io_service,
+ const Const_Buffers& buffers, socket_base::message_flags flags,
+ Handler handler)
+ : socket_(socket),
+ io_service_(io_service),
+ work_(io_service),
+ buffers_(buffers),
+ flags_(flags),
+ handler_(handler)
+ {
+ }
+
+ bool operator()(int result)
+ {
+ // Check whether the operation was successful.
+ if (result != 0)
+ {
+ asio::error error(result);
+ io_service_.post(bind_handler(handler_, error, 0));
+ return true;
+ }
+
+ // Copy buffers into array.
+ socket_ops::buf bufs[max_buffers];
+ typename Const_Buffers::const_iterator iter = buffers_.begin();
+ typename Const_Buffers::const_iterator end = buffers_.end();
+ size_t i = 0;
+ for (; iter != end && i < max_buffers; ++iter, ++i)
+ {
+ asio::const_buffer buffer(*iter);
+ socket_ops::init_buf(bufs[i],
+ asio::buffer_cast<const void*>(buffer),
+ asio::buffer_size(buffer));
+ }
+
+ // Send the data.
+ int bytes = socket_ops::send(socket_, bufs, i, flags_);
+ asio::error error(bytes < 0
+ ? socket_ops::get_error() : asio::error::success);
+
+ // Check if we need to run the operation again.
+ if (error == asio::error::would_block
+ || error == asio::error::try_again)
+ return false;
+
+ io_service_.post(bind_handler(handler_, error, bytes < 0 ? 0 : bytes));
+ return true;
+ }
+
+ private:
+ socket_type socket_;
+ asio::io_service& io_service_;
+ asio::io_service::work work_;
+ Const_Buffers buffers_;
+ socket_base::message_flags flags_;
+ Handler handler_;
+ };
+
+ // Start an asynchronous send. The data being sent must be valid for the
+ // lifetime of the asynchronous operation.
+ template <typename Const_Buffers, typename Handler>
+ void async_send(implementation_type& impl, const Const_Buffers& buffers,
+ socket_base::message_flags flags, Handler handler)
+ {
+ if (impl.socket_ == invalid_socket)
+ {
+ asio::error error(asio::error::bad_descriptor);
+ io_service().post(bind_handler(handler, error, 0));
+ }
+ else
+ {
+ if (impl.protocol_.type() == SOCK_STREAM)
+ {
+ // Determine total size of buffers.
+ typename Const_Buffers::const_iterator iter = buffers.begin();
+ typename Const_Buffers::const_iterator end = buffers.end();
+ size_t i = 0;
+ size_t total_buffer_size = 0;
+ for (; iter != end && i < max_buffers; ++iter, ++i)
+ {
+ asio::const_buffer buffer(*iter);
+ total_buffer_size += asio::buffer_size(buffer);
+ }
+
+ // A request to receive 0 bytes on a stream socket is a no-op.
+ if (total_buffer_size == 0)
+ {
+ asio::error error(asio::error::success);
+ io_service().post(bind_handler(handler, error, 0));
+ return;
+ }
+ }
+
+ // Make socket 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))
+ {
+ asio::error error(socket_ops::get_error());
+ io_service().post(bind_handler(handler, error, 0));
+ return;
+ }
+ impl.flags_ |= implementation_type::internal_non_blocking;
+ }
+
+ reactor_.start_write_op(impl.socket_,
+ send_handler<Const_Buffers, Handler>(
+ impl.socket_, io_service(), buffers, flags, handler));
+ }
+ }
+
+ // Send a datagram to the specified endpoint. Returns the number of bytes
+ // sent.
+ template <typename Const_Buffers, typename Error_Handler>
+ size_t send_to(implementation_type& impl, const Const_Buffers& buffers,
+ const endpoint_type& destination, socket_base::message_flags flags,
+ Error_Handler error_handler)
+ {
+ // Copy buffers into array.
+ socket_ops::buf bufs[max_buffers];
+ typename Const_Buffers::const_iterator iter = buffers.begin();
+ typename Const_Buffers::const_iterator end = buffers.end();
+ size_t i = 0;
+ for (; iter != end && i < max_buffers; ++iter, ++i)
+ {
+ asio::const_buffer buffer(*iter);
+ socket_ops::init_buf(bufs[i],
+ asio::buffer_cast<const void*>(buffer),
+ asio::buffer_size(buffer));
+ }
+
+ // Send the data.
+ for (;;)
+ {
+ // Try to complete the operation without blocking.
+ int bytes_sent = socket_ops::sendto(impl.socket_, bufs, i, flags,
+ destination.data(), destination.size());
+ int error = socket_ops::get_error();
+
+ // Check if operation succeeded.
+ if (bytes_sent >= 0)
+ {
+ error_handler(asio::error(0));
+ return bytes_sent;
+ }
+
+ // Operation failed.
+ if ((impl.flags_ & implementation_type::user_set_non_blocking)
+ || (error != asio::error::would_block
+ && error != asio::error::try_again))
+ {
+ error_handler(asio::error(error));
+ return 0;
+ }
+
+ // Wait for socket to become ready.
+ if (socket_ops::poll_write(impl.socket_) < 0)
+ {
+ error_handler(asio::error(socket_ops::get_error()));
+ return 0;
+ }
+ }
+ }
+
+ template <typename Const_Buffers, typename Handler>
+ class send_to_handler
+ {
+ public:
+ send_to_handler(socket_type socket, asio::io_service& io_service,
+ const Const_Buffers& buffers, const endpoint_type& endpoint,
+ socket_base::message_flags flags, Handler handler)
+ : socket_(socket),
+ io_service_(io_service),
+ work_(io_service),
+ buffers_(buffers),
+ destination_(endpoint),
+ flags_(flags),
+ handler_(handler)
+ {
+ }
+
+ bool operator()(int result)
+ {
+ // Check whether the operation was successful.
+ if (result != 0)
+ {
+ asio::error error(result);
+ io_service_.post(bind_handler(handler_, error, 0));
+ return true;
+ }
+
+ // Copy buffers into array.
+ socket_ops::buf bufs[max_buffers];
+ typename Const_Buffers::const_iterator iter = buffers_.begin();
+ typename Const_Buffers::const_iterator end = buffers_.end();
+ size_t i = 0;
+ for (; iter != end && i < max_buffers; ++iter, ++i)
+ {
+ asio::const_buffer buffer(*iter);
+ socket_ops::init_buf(bufs[i],
+ asio::buffer_cast<const void*>(buffer),
+ asio::buffer_size(buffer));
+ }
+
+ // Send the data.
+ int bytes = socket_ops::sendto(socket_, bufs, i, flags_,
+ destination_.data(), destination_.size());
+ asio::error error(bytes < 0
+ ? socket_ops::get_error() : asio::error::success);
+
+ // Check if we need to run the operation again.
+ if (error == asio::error::would_block
+ || error == asio::error::try_again)
+ return false;
+
+ io_service_.post(bind_handler(handler_, error, bytes < 0 ? 0 : bytes));
+ return true;
+ }
+
+ private:
+ socket_type socket_;
+ asio::io_service& io_service_;
+ asio::io_service::work work_;
+ Const_Buffers buffers_;
+ endpoint_type destination_;
+ socket_base::message_flags flags_;
+ Handler handler_;
+ };
+
+ // Start an asynchronous send. The data being sent must be valid for the
+ // lifetime of the asynchronous operation.
+ template <typename Const_Buffers, typename Handler>
+ void async_send_to(implementation_type& impl, const Const_Buffers& buffers,
+ const endpoint_type& destination, socket_base::message_flags flags,
+ Handler handler)
+ {
+ if (impl.socket_ == invalid_socket)
+ {
+ asio::error error(asio::error::bad_descriptor);
+ io_service().post(bind_handler(handler, error, 0));
+ }
+ else
+ {
+ // Make socket 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))
+ {
+ asio::error error(socket_ops::get_error());
+ io_service().post(bind_handler(handler, error, 0));
+ return;
+ }
+ impl.flags_ |= implementation_type::internal_non_blocking;
+ }
+
+ reactor_.start_write_op(impl.socket_,
+ send_to_handler<Const_Buffers, Handler>(
+ impl.socket_, io_service(), buffers, destination, flags, handler));
+ }
+ }
+
+ // Receive some data from the peer. Returns the number of bytes received.
+ template <typename Mutable_Buffers, typename Error_Handler>
+ size_t receive(implementation_type& impl, const Mutable_Buffers& buffers,
+ socket_base::message_flags flags, Error_Handler error_handler)
+ {
+ // Copy buffers into array.
+ socket_ops::buf bufs[max_buffers];
+ typename Mutable_Buffers::const_iterator iter = buffers.begin();
+ typename Mutable_Buffers::const_iterator end = buffers.end();
+ size_t i = 0;
+ size_t total_buffer_size = 0;
+ for (; iter != end && i < max_buffers; ++iter, ++i)
+ {
+ asio::mutable_buffer buffer(*iter);
+ socket_ops::init_buf(bufs[i],
+ asio::buffer_cast<void*>(buffer),
+ asio::buffer_size(buffer));
+ total_buffer_size += asio::buffer_size(buffer);
+ }
+
+ // A request to receive 0 bytes on a stream socket is a no-op.
+ if (impl.protocol_.type() == SOCK_STREAM && total_buffer_size == 0)
+ {
+ error_handler(asio::error(0));
+ return 0;
+ }
+
+ // Receive some data.
+ for (;;)
+ {
+ // Try to complete the operation without blocking.
+ int bytes_recvd = socket_ops::recv(impl.socket_, bufs, i, flags);
+ int error = socket_ops::get_error();
+
+ // Check if operation succeeded.
+ if (bytes_recvd > 0)
+ {
+ error_handler(asio::error(0));
+ return bytes_recvd;
+ }
+
+ // Check for EOF.
+ if (bytes_recvd == 0)
+ {
+ error_handler(asio::error(asio::error::eof));
+ return 0;
+ }
+
+ // Operation failed.
+ if ((impl.flags_ & implementation_type::user_set_non_blocking)
+ || (error != asio::error::would_block
+ && error != asio::error::try_again))
+ {
+ error_handler(asio::error(error));
+ return 0;
+ }
+
+ // Wait for socket to become ready.
+ if (socket_ops::poll_read(impl.socket_) < 0)
+ {
+ error_handler(asio::error(socket_ops::get_error()));
+ return 0;
+ }
+ }
+ }
+
+ template <typename Mutable_Buffers, typename Handler>
+ class receive_handler
+ {
+ public:
+ receive_handler(socket_type socket, asio::io_service& io_service,
+ const Mutable_Buffers& buffers, socket_base::message_flags flags,
+ Handler handler)
+ : socket_(socket),
+ io_service_(io_service),
+ work_(io_service),
+ buffers_(buffers),
+ flags_(flags),
+ handler_(handler)
+ {
+ }
+
+ bool operator()(int result)
+ {
+ // Check whether the operation was successful.
+ if (result != 0)
+ {
+ asio::error error(result);
+ io_service_.post(bind_handler(handler_, error, 0));
+ return true;
+ }
+
+ // Copy buffers into array.
+ socket_ops::buf bufs[max_buffers];
+ typename Mutable_Buffers::const_iterator iter = buffers_.begin();
+ typename Mutable_Buffers::const_iterator end = buffers_.end();
+ size_t i = 0;
+ for (; iter != end && i < max_buffers; ++iter, ++i)
+ {
+ asio::mutable_buffer buffer(*iter);
+ socket_ops::init_buf(bufs[i],
+ asio::buffer_cast<void*>(buffer),
+ asio::buffer_size(buffer));
+ }
+
+ // Receive some data.
+ int bytes = socket_ops::recv(socket_, bufs, i, flags_);
+ int error_code = asio::error::success;
+ if (bytes < 0)
+ error_code = socket_ops::get_error();
+ else if (bytes == 0)
+ error_code = asio::error::eof;
+ asio::error error(error_code);
+
+ // Check if we need to run the operation again.
+ if (error == asio::error::would_block
+ || error == asio::error::try_again)
+ return false;
+
+ io_service_.post(bind_handler(handler_, error, bytes < 0 ? 0 : bytes));
+ return true;
+ }
+
+ private:
+ socket_type socket_;
+ asio::io_service& io_service_;
+ asio::io_service::work work_;
+ Mutable_Buffers buffers_;
+ socket_base::message_flags flags_;
+ Handler handler_;
+ };
+
+ // Start an asynchronous receive. The buffer for the data being received
+ // must be valid for the lifetime of the asynchronous operation.
+ template <typename Mutable_Buffers, typename Handler>
+ void async_receive(implementation_type& impl, const Mutable_Buffers& buffers,
+ socket_base::message_flags flags, Handler handler)
+ {
+ if (impl.socket_ == invalid_socket)
+ {
+ asio::error error(asio::error::bad_descriptor);
+ io_service().post(bind_handler(handler, error, 0));
+ }
+ else
+ {
+ if (impl.protocol_.type() == SOCK_STREAM)
+ {
+ // Determine total size of buffers.
+ typename Mutable_Buffers::const_iterator iter = buffers.begin();
+ typename Mutable_Buffers::const_iterator end = buffers.end();
+ size_t i = 0;
+ size_t total_buffer_size = 0;
+ for (; iter != end && i < max_buffers; ++iter, ++i)
+ {
+ asio::mutable_buffer buffer(*iter);
+ total_buffer_size += asio::buffer_size(buffer);
+ }
+
+ // A request to receive 0 bytes on a stream socket is a no-op.
+ if (total_buffer_size == 0)
+ {
+ asio::error error(asio::error::success);
+ io_service().post(bind_handler(handler, error, 0));
+ return;
+ }
+ }
+
+ // Make socket 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))
+ {
+ asio::error error(socket_ops::get_error());
+ io_service().post(bind_handler(handler, error, 0));
+ return;
+ }
+ impl.flags_ |= implementation_type::internal_non_blocking;
+ }
+
+ if (flags & socket_base::message_out_of_band)
+ {
+ reactor_.start_except_op(impl.socket_,
+ receive_handler<Mutable_Buffers, Handler>(
+ impl.socket_, io_service(), buffers, flags, handler));
+ }
+ else
+ {
+ reactor_.start_read_op(impl.socket_,
+ receive_handler<Mutable_Buffers, Handler>(
+ impl.socket_, io_service(), buffers, flags, handler));
+ }
+ }
+ }
+
+ // Receive a datagram with the endpoint of the sender. Returns the number of
+ // bytes received.
+ template <typename Mutable_Buffers, typename Error_Handler>
+ size_t receive_from(implementation_type& impl, const Mutable_Buffers& buffers,
+ endpoint_type& sender_endpoint, socket_base::message_flags flags,
+ Error_Handler error_handler)
+ {
+ // Copy buffers into array.
+ socket_ops::buf bufs[max_buffers];
+ typename Mutable_Buffers::const_iterator iter = buffers.begin();
+ typename Mutable_Buffers::const_iterator end = buffers.end();
+ size_t i = 0;
+ for (; iter != end && i < max_buffers; ++iter, ++i)
+ {
+ asio::mutable_buffer buffer(*iter);
+ socket_ops::init_buf(bufs[i],
+ asio::buffer_cast<void*>(buffer),
+ asio::buffer_size(buffer));
+ }
+
+ // Receive some data.
+ for (;;)
+ {
+ // Try to complete the operation without blocking.
+ socket_addr_len_type addr_len = sender_endpoint.capacity();
+ int bytes_recvd = socket_ops::recvfrom(impl.socket_, bufs, i, flags,
+ sender_endpoint.data(), &addr_len);
+ int error = socket_ops::get_error();
+
+ // Check if operation succeeded.
+ if (bytes_recvd > 0)
+ {
+ sender_endpoint.resize(addr_len);
+ error_handler(asio::error(0));
+ return bytes_recvd;
+ }
+
+ // Check for EOF.
+ if (bytes_recvd == 0)
+ {
+ error_handler(asio::error(asio::error::eof));
+ return 0;
+ }
+
+ // Operation failed.
+ if ((impl.flags_ & implementation_type::user_set_non_blocking)
+ || (error != asio::error::would_block
+ && error != asio::error::try_again))
+ {
+ error_handler(asio::error(error));
+ return 0;
+ }
+
+ // Wait for socket to become ready.
+ if (socket_ops::poll_read(impl.socket_) < 0)
+ {
+ error_handler(asio::error(socket_ops::get_error()));
+ return 0;
+ }
+ }
+ }
+
+ template <typename Mutable_Buffers, typename Handler>
+ class receive_from_handler
+ {
+ public:
+ receive_from_handler(socket_type socket,
+ asio::io_service& io_service, const Mutable_Buffers& buffers,
+ endpoint_type& endpoint, socket_base::message_flags flags,
+ Handler handler)
+ : socket_(socket),
+ io_service_(io_service),
+ work_(io_service),
+ buffers_(buffers),
+ sender_endpoint_(endpoint),
+ flags_(flags),
+ handler_(handler)
+ {
+ }
+
+ bool operator()(int result)
+ {
+ // Check whether the operation was successful.
+ if (result != 0)
+ {
+ asio::error error(result);
+ io_service_.post(bind_handler(handler_, error, 0));
+ return true;
+ }
+
+ // Copy buffers into array.
+ socket_ops::buf bufs[max_buffers];
+ typename Mutable_Buffers::const_iterator iter = buffers_.begin();
+ typename Mutable_Buffers::const_iterator end = buffers_.end();
+ size_t i = 0;
+ for (; iter != end && i < max_buffers; ++iter, ++i)
+ {
+ asio::mutable_buffer buffer(*iter);
+ socket_ops::init_buf(bufs[i],
+ asio::buffer_cast<void*>(buffer),
+ asio::buffer_size(buffer));
+ }
+
+ // Receive some data.
+ socket_addr_len_type addr_len = sender_endpoint_.capacity();
+ int bytes = socket_ops::recvfrom(socket_, bufs, i, flags_,
+ sender_endpoint_.data(), &addr_len);
+ int error_code = asio::error::success;
+ if (bytes < 0)
+ error_code = socket_ops::get_error();
+ else if (bytes == 0)
+ error_code = asio::error::eof;
+ asio::error error(error_code);
+
+ // Check if we need to run the operation again.
+ if (error == asio::error::would_block
+ || error == asio::error::try_again)
+ return false;
+
+ sender_endpoint_.resize(addr_len);
+ io_service_.post(bind_handler(handler_, error, bytes < 0 ? 0 : bytes));
+ return true;
+ }
+
+ private:
+ socket_type socket_;
+ asio::io_service& io_service_;
+ asio::io_service::work work_;
+ Mutable_Buffers buffers_;
+ endpoint_type& sender_endpoint_;
+ socket_base::message_flags flags_;
+ Handler handler_;
+ };
+
+ // Start an asynchronous receive. The buffer for the data being received and
+ // the sender_endpoint object must both be valid for the lifetime of the
+ // asynchronous operation.
+ template <typename Mutable_Buffers, typename Handler>
+ void async_receive_from(implementation_type& impl,
+ const Mutable_Buffers& buffers, endpoint_type& sender_endpoint,
+ socket_base::message_flags flags, Handler handler)
+ {
+ if (impl.socket_ == invalid_socket)
+ {
+ asio::error error(asio::error::bad_descriptor);
+ io_service().post(bind_handler(handler, error, 0));
+ }
+ else
+ {
+ // Make socket 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))
+ {
+ asio::error error(socket_ops::get_error());
+ io_service().post(bind_handler(handler, error, 0));
+ return;
+ }
+ impl.flags_ |= implementation_type::internal_non_blocking;
+ }
+
+ reactor_.start_read_op(impl.socket_,
+ receive_from_handler<Mutable_Buffers, Handler>(
+ impl.socket_, io_service(), buffers,
+ sender_endpoint, flags, handler));
+ }
+ }
+
+ // Accept a new connection.
+ template <typename Socket, typename Error_Handler>
+ void accept(implementation_type& impl, Socket& peer,
+ Error_Handler error_handler)
+ {
+ // We cannot accept a socket that is already open.
+ if (peer.native() != invalid_socket)
+ {
+ error_handler(asio::error(asio::error::already_connected));
+ return;
+ }
+
+ // Accept a socket.
+ for (;;)
+ {
+ // Try to complete the operation without blocking.
+ socket_holder new_socket(socket_ops::accept(impl.socket_, 0, 0));
+ int error = socket_ops::get_error();
+
+ // Check if operation succeeded.
+ if (new_socket.get() >= 0)
+ {
+ asio::error temp_error;
+ peer.assign(impl.protocol_, new_socket.get(),
+ asio::assign_error(temp_error));
+ if (temp_error)
+ {
+ error_handler(temp_error);
+ }
+ else
+ {
+ new_socket.release();
+ error_handler(asio::error(0));
+ }
+ return;
+ }
+
+ // Operation failed.
+ if (error == asio::error::would_block
+ || error == asio::error::try_again)
+ {
+ if (impl.flags_ & implementation_type::user_set_non_blocking)
+ {
+ error_handler(asio::error(error));
+ return;
+ }
+ // Fall through to retry operation.
+ }
+ else if (error == asio::error::connection_aborted)
+ {
+ if (impl.flags_ & implementation_type::enable_connection_aborted)
+ {
+ error_handler(asio::error(error));
+ return;
+ }
+ // Fall through to retry operation.
+ }
+ else
+ {
+ error_handler(asio::error(error));
+ return;
+ }
+
+ // Wait for socket to become ready.
+ if (socket_ops::poll_read(impl.socket_) < 0)
+ {
+ error_handler(asio::error(socket_ops::get_error()));
+ return;
+ }
+ }
+ }
+
+ // Accept a new connection.
+ template <typename Socket, typename Error_Handler>
+ void accept_endpoint(implementation_type& impl, Socket& peer,
+ endpoint_type& peer_endpoint, Error_Handler error_handler)
+ {
+ // We cannot accept a socket that is already open.
+ if (peer.native() != invalid_socket)
+ {
+ error_handler(asio::error(asio::error::already_connected));
+ return;
+ }
+
+ // Accept a socket.
+ for (;;)
+ {
+ // Try to complete the operation without blocking.
+ socket_addr_len_type addr_len = peer_endpoint.capacity();
+ socket_holder new_socket(socket_ops::accept(
+ impl.socket_, peer_endpoint.data(), &addr_len));
+ int error = socket_ops::get_error();
+
+ // Check if operation succeeded.
+ if (new_socket.get() >= 0)
+ {
+ peer_endpoint.resize(addr_len);
+ asio::error temp_error;
+ peer.assign(impl.protocol_, new_socket.get(),
+ asio::assign_error(temp_error));
+ if (temp_error)
+ {
+ error_handler(temp_error);
+ }
+ else
+ {
+ new_socket.release();
+ error_handler(asio::error(0));
+ }
+ return;
+ }
+
+ // Operation failed.
+ if (error == asio::error::would_block
+ || error == asio::error::try_again)
+ {
+ if (impl.flags_ & implementation_type::user_set_non_blocking)
+ {
+ error_handler(asio::error(error));
+ return;
+ }
+ // Fall through to retry operation.
+ }
+ else if (error == asio::error::connection_aborted)
+ {
+ if (impl.flags_ & implementation_type::enable_connection_aborted)
+ {
+ error_handler(asio::error(error));
+ return;
+ }
+ // Fall through to retry operation.
+ }
+ else
+ {
+ error_handler(asio::error(error));
+ return;
+ }
+
+ // Wait for socket to become ready.
+ if (socket_ops::poll_read(impl.socket_) < 0)
+ {
+ error_handler(asio::error(socket_ops::get_error()));
+ return;
+ }
+ }
+ }
+
+ template <typename Socket, typename Handler>
+ class accept_handler
+ {
+ public:
+ accept_handler(socket_type socket, asio::io_service& io_service,
+ Socket& peer, const protocol_type& protocol,
+ bool enable_connection_aborted, Handler handler)
+ : socket_(socket),
+ io_service_(io_service),
+ work_(io_service),
+ peer_(peer),
+ protocol_(protocol),
+ enable_connection_aborted_(enable_connection_aborted),
+ handler_(handler)
+ {
+ }
+
+ bool operator()(int result)
+ {
+ // Check whether the operation was successful.
+ if (result != 0)
+ {
+ asio::error error(result);
+ io_service_.post(bind_handler(handler_, error));
+ return true;
+ }
+
+ // Accept the waiting connection.
+ socket_holder new_socket(socket_ops::accept(socket_, 0, 0));
+ asio::error error(new_socket.get() == invalid_socket
+ ? socket_ops::get_error() : asio::error::success);
+
+ // Check if we need to run the operation again.
+ if (error == asio::error::would_block
+ || error == asio::error::try_again)
+ return false;
+ if (error == asio::error::connection_aborted
+ && !enable_connection_aborted_)
+ return false;
+
+ // Transfer ownership of the new socket to the peer object.
+ if (!error)
+ {
+ peer_.assign(protocol_, new_socket.get(),
+ asio::assign_error(error));
+ if (!error)
+ new_socket.release();
+ }
+
+ io_service_.post(bind_handler(handler_, error));
+ return true;
+ }
+
+ private:
+ socket_type socket_;
+ asio::io_service& io_service_;
+ asio::io_service::work work_;
+ Socket& peer_;
+ protocol_type protocol_;
+ bool enable_connection_aborted_;
+ Handler handler_;
+ };
+
+ // Start an asynchronous accept. The peer object must be valid until the
+ // accept's handler is invoked.
+ template <typename Socket, typename Handler>
+ void async_accept(implementation_type& impl, Socket& peer, Handler handler)
+ {
+ if (impl.socket_ == invalid_socket)
+ {
+ asio::error error(asio::error::bad_descriptor);
+ io_service().post(bind_handler(handler, error));
+ }
+ else if (peer.native() != invalid_socket)
+ {
+ asio::error error(asio::error::already_connected);
+ io_service().post(bind_handler(handler, error));
+ }
+ else
+ {
+ // Make socket 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))
+ {
+ asio::error error(socket_ops::get_error());
+ io_service().post(bind_handler(handler, error));
+ return;
+ }
+ impl.flags_ |= implementation_type::internal_non_blocking;
+ }
+
+ reactor_.start_read_op(impl.socket_,
+ accept_handler<Socket, Handler>(
+ impl.socket_, io_service(), peer, impl.protocol_,
+ (impl.flags_ & implementation_type::enable_connection_aborted) != 0,
+ handler));
+ }
+ }
+
+ template <typename Socket, typename Handler>
+ class accept_endp_handler
+ {
+ public:
+ accept_endp_handler(socket_type socket, asio::io_service& io_service,
+ Socket& peer, endpoint_type& peer_endpoint,
+ bool enable_connection_aborted, Handler handler)
+ : socket_(socket),
+ io_service_(io_service),
+ work_(io_service),
+ peer_(peer),
+ peer_endpoint_(peer_endpoint),
+ enable_connection_aborted_(enable_connection_aborted),
+ handler_(handler)
+ {
+ }
+
+ bool operator()(int result)
+ {
+ // Check whether the operation was successful.
+ if (result != 0)
+ {
+ asio::error error(result);
+ io_service_.post(bind_handler(handler_, error));
+ return true;
+ }
+
+ // Accept the waiting connection.
+ socket_addr_len_type addr_len = peer_endpoint_.capacity();
+ socket_holder new_socket(socket_ops::accept(
+ socket_, peer_endpoint_.data(), &addr_len));
+ asio::error error(new_socket.get() == invalid_socket
+ ? socket_ops::get_error() : asio::error::success);
+
+ // Check if we need to run the operation again.
+ if (error == asio::error::would_block
+ || error == asio::error::try_again)
+ return false;
+ if (error == asio::error::connection_aborted
+ && !enable_connection_aborted_)
+ return false;
+
+ // Transfer ownership of the new socket to the peer object.
+ if (!error)
+ {
+ peer_endpoint_.resize(addr_len);
+ peer_.assign(peer_endpoint_.protocol(), new_socket.get(),
+ asio::assign_error(error));
+ if (!error)
+ new_socket.release();
+ }
+
+ io_service_.post(bind_handler(handler_, error));
+ return true;
+ }
+
+ private:
+ socket_type socket_;
+ asio::io_service& io_service_;
+ asio::io_service::work work_;
+ Socket& peer_;
+ endpoint_type& peer_endpoint_;
+ bool enable_connection_aborted_;
+ Handler handler_;
+ };
+
+ // Start an asynchronous accept. The peer and peer_endpoint objects
+ // must be valid until the accept's handler is invoked.
+ template <typename Socket, typename Handler>
+ void async_accept_endpoint(implementation_type& impl, Socket& peer,
+ endpoint_type& peer_endpoint, Handler handler)
+ {
+ if (impl.socket_ == invalid_socket)
+ {
+ asio::error error(asio::error::bad_descriptor);
+ io_service().post(bind_handler(handler, error));
+ }
+ else if (peer.native() != invalid_socket)
+ {
+ asio::error error(asio::error::already_connected);
+ io_service().post(bind_handler(handler, error));
+ }
+ else
+ {
+ // Make socket 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))
+ {
+ asio::error error(socket_ops::get_error());
+ io_service().post(bind_handler(handler, error));
+ return;
+ }
+ impl.flags_ |= implementation_type::internal_non_blocking;
+ }
+
+ reactor_.start_read_op(impl.socket_,
+ accept_endp_handler<Socket, Handler>(
+ impl.socket_, io_service(), peer, peer_endpoint,
+ (impl.flags_ & implementation_type::enable_connection_aborted) != 0,
+ handler));
+ }
+ }
+
+ // Connect the socket to the specified endpoint.
+ template <typename Error_Handler>
+ void connect(implementation_type& impl, const endpoint_type& peer_endpoint,
+ Error_Handler error_handler)
+ {
+ // Open the socket if it is not already open.
+ if (impl.socket_ == invalid_socket)
+ {
+ // Get the flags used to create the new socket.
+ int family = peer_endpoint.protocol().family();
+ int type = peer_endpoint.protocol().type();
+ int proto = peer_endpoint.protocol().protocol();
+
+ // Create a new socket.
+ impl.socket_ = socket_ops::socket(family, type, proto);
+ if (impl.socket_ == invalid_socket)
+ {
+ error_handler(asio::error(socket_ops::get_error()));
+ return;
+ }
+
+ // Register the socket with the reactor.
+ if (int err = reactor_.register_descriptor(impl.socket_))
+ {
+ socket_ops::close(impl.socket_);
+ error_handler(asio::error(err));
+ return;
+ }
+ }
+ else if (impl.flags_ & implementation_type::internal_non_blocking)
+ {
+ // Mark the socket as blocking while we perform the connect.
+ ioctl_arg_type non_blocking = 0;
+ if (socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking))
+ {
+ error_handler(asio::error(socket_ops::get_error()));
+ return;
+ }
+ impl.flags_ &= ~implementation_type::internal_non_blocking;
+ }
+
+ // Perform the connect operation.
+ int result = socket_ops::connect(impl.socket_,
+ peer_endpoint.data(), peer_endpoint.size());
+ if (result == socket_error_retval)
+ error_handler(asio::error(socket_ops::get_error()));
+ else
+ error_handler(asio::error(0));
+ }
+
+ template <typename Handler>
+ class connect_handler
+ {
+ public:
+ connect_handler(socket_type socket, boost::shared_ptr<bool> completed,
+ asio::io_service& io_service, Reactor& reactor, Handler handler)
+ : socket_(socket),
+ completed_(completed),
+ io_service_(io_service),
+ work_(io_service),
+ reactor_(reactor),
+ handler_(handler)
+ {
+ }
+
+ bool operator()(int result)
+ {
+ // Check whether a handler has already been called for the connection.
+ // If it has, then we don't want to do anything in this handler.
+ if (*completed_)
+ return true;
+
+ // Cancel the other reactor operation for the connection.
+ *completed_ = true;
+ reactor_.enqueue_cancel_ops_unlocked(socket_);
+
+ // Check whether the operation was successful.
+ if (result != 0)
+ {
+ asio::error error(result);
+ io_service_.post(bind_handler(handler_, error));
+ return true;
+ }
+
+ // Get the error code from the connect operation.
+ int connect_error = 0;
+ size_t connect_error_len = sizeof(connect_error);
+ if (socket_ops::getsockopt(socket_, SOL_SOCKET, SO_ERROR,
+ &connect_error, &connect_error_len) == socket_error_retval)
+ {
+ asio::error error(socket_ops::get_error());
+ io_service_.post(bind_handler(handler_, error));
+ return true;
+ }
+
+ // If connection failed then post the handler with the error code.
+ if (connect_error)
+ {
+ asio::error error(connect_error);
+ io_service_.post(bind_handler(handler_, error));
+ return true;
+ }
+
+ // Post the result of the successful connection operation.
+ asio::error error(asio::error::success);
+ io_service_.post(bind_handler(handler_, error));
+
+ return true;
+ }
+
+ private:
+ socket_type socket_;
+ boost::shared_ptr<bool> completed_;
+ asio::io_service& io_service_;
+ asio::io_service::work work_;
+ Reactor& reactor_;
+ Handler handler_;
+ };
+
+ // Start an asynchronous connect.
+ template <typename Handler>
+ void async_connect(implementation_type& impl,
+ const endpoint_type& peer_endpoint, Handler handler)
+ {
+ // Open the socket if it is not already open.
+ if (impl.socket_ == invalid_socket)
+ {
+ // Get the flags used to create the new socket.
+ int family = peer_endpoint.protocol().family();
+ int type = peer_endpoint.protocol().type();
+ int proto = peer_endpoint.protocol().protocol();
+
+ // Create a new socket.
+ impl.socket_ = socket_ops::socket(family, type, proto);
+ if (impl.socket_ == invalid_socket)
+ {
+ asio::error error(socket_ops::get_error());
+ io_service().post(bind_handler(handler, error));
+ return;
+ }
+
+ // Register the socket with the reactor.
+ if (int err = reactor_.register_descriptor(impl.socket_))
+ {
+ socket_ops::close(impl.socket_);
+ asio::error error(err);
+ io_service().post(bind_handler(handler, error));
+ return;
+ }
+ }
+
+ // Make socket 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))
+ {
+ asio::error error(socket_ops::get_error());
+ io_service().post(bind_handler(handler, error));
+ return;
+ }
+ impl.flags_ |= implementation_type::internal_non_blocking;
+ }
+
+ // Start the connect operation. The socket is already marked as non-blocking
+ // so the connection will take place asynchronously.
+ if (socket_ops::connect(impl.socket_, peer_endpoint.data(),
+ peer_endpoint.size()) == 0)
+ {
+ // The connect operation has finished successfully so we need to post the
+ // handler immediately.
+ asio::error error(asio::error::success);
+ io_service().post(bind_handler(handler, error));
+ }
+ else if (socket_ops::get_error() == asio::error::in_progress
+ || socket_ops::get_error() == asio::error::would_block)
+ {
+ // The connection is happening in the background, and we need to wait
+ // until the socket becomes writeable.
+ boost::shared_ptr<bool> completed(new bool(false));
+ reactor_.start_write_and_except_ops(impl.socket_,
+ connect_handler<Handler>(
+ impl.socket_, completed, io_service(), reactor_, handler));
+ }
+ else
+ {
+ // The connect operation has failed, so post the handler immediately.
+ asio::error error(socket_ops::get_error());
+ io_service().post(bind_handler(handler, error));
+ }
+ }
+
+private:
+ // The selector that performs event demultiplexing for the provider.
+ Reactor& reactor_;
+};
+
+} // namespace detail
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_DETAIL_REACTIVE_SOCKET_SERVICE_HPP
diff --git a/library/include/libtorrent/asio/detail/reactor_op_queue.hpp b/library/include/libtorrent/asio/detail/reactor_op_queue.hpp
new file mode 100644
index 000000000..dbf86c2e5
--- /dev/null
+++ b/library/include/libtorrent/asio/detail/reactor_op_queue.hpp
@@ -0,0 +1,379 @@
+//
+// reactor_op_queue.hpp
+// ~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_REACTOR_OP_QUEUE_HPP
+#define ASIO_DETAIL_REACTOR_OP_QUEUE_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/detail/push_options.hpp"
+#include <memory>
+#include "asio/detail/pop_options.hpp"
+
+#include "asio/error.hpp"
+#include "asio/detail/hash_map.hpp"
+#include "asio/detail/noncopyable.hpp"
+
+namespace asio {
+namespace detail {
+
+template <typename Descriptor>
+class reactor_op_queue
+ : private noncopyable
+{
+public:
+ // Constructor.
+ reactor_op_queue()
+ : operations_(),
+ cancelled_operations_(0),
+ cleanup_operations_(0)
+ {
+ }
+
+ // Add a new operation to the queue. Returns true if this is the only
+ // operation for the given descriptor, in which case the reactor's event
+ // demultiplexing function call may need to be interrupted and restarted.
+ template <typename Handler>
+ bool enqueue_operation(Descriptor descriptor, Handler handler)
+ {
+ op_base* new_op = new op<Handler>(descriptor, handler);
+
+ typedef typename operation_map::iterator iterator;
+ typedef typename operation_map::value_type value_type;
+ std::pair<iterator, bool> entry =
+ operations_.insert(value_type(descriptor, new_op));
+ if (entry.second)
+ return true;
+
+ op_base* current_op = entry.first->second;
+ while (current_op->next_)
+ current_op = current_op->next_;
+ current_op->next_ = new_op;
+
+ return false;
+ }
+
+ // Cancel all operations associated with the descriptor. Any operations
+ // pending for the descriptor will be notified that they have been cancelled
+ // next time dispatch_cancellations is called. Returns true if any operations
+ // were cancelled, in which case the reactor's event demultiplexing function
+ // may need to be interrupted and restarted.
+ bool cancel_operations(Descriptor descriptor)
+ {
+ typename operation_map::iterator i = operations_.find(descriptor);
+ if (i != operations_.end())
+ {
+ op_base* last_op = i->second;
+ while (last_op->next_)
+ last_op = last_op->next_;
+ last_op->next_ = cancelled_operations_;
+ cancelled_operations_ = i->second;
+ operations_.erase(i);
+ return true;
+ }
+
+ return false;
+ }
+
+ // Whether there are no operations in the queue.
+ bool empty() const
+ {
+ return operations_.empty();
+ }
+
+ // Determine whether there are any operations associated with the descriptor.
+ bool has_operation(Descriptor descriptor) const
+ {
+ return operations_.find(descriptor) != operations_.end();
+ }
+
+ // Dispatch the first operation corresponding to the descriptor. Returns true
+ // if there are more operations queued for the descriptor.
+ bool dispatch_operation(Descriptor descriptor, int result)
+ {
+ typename operation_map::iterator i = operations_.find(descriptor);
+ if (i != operations_.end())
+ {
+ op_base* this_op = i->second;
+ i->second = this_op->next_;
+ this_op->next_ = cleanup_operations_;
+ cleanup_operations_ = this_op;
+ bool done = this_op->invoke(result);
+ if (done)
+ {
+ // Operation has finished.
+ if (i->second)
+ {
+ return true;
+ }
+ else
+ {
+ operations_.erase(i);
+ return false;
+ }
+ }
+ else
+ {
+ // Operation wants to be called again. Leave it at the front of the
+ // queue for this descriptor, and remove from the cleanup list.
+ cleanup_operations_ = this_op->next_;
+ this_op->next_ = i->second;
+ i->second = this_op;
+ return true;
+ }
+ }
+ return false;
+ }
+
+ // Dispatch all operations corresponding to the descriptor.
+ void dispatch_all_operations(Descriptor descriptor, int result)
+ {
+ typename operation_map::iterator i = operations_.find(descriptor);
+ if (i != operations_.end())
+ {
+ while (i->second)
+ {
+ op_base* this_op = i->second;
+ i->second = this_op->next_;
+ this_op->next_ = cleanup_operations_;
+ cleanup_operations_ = this_op;
+ bool done = this_op->invoke(result);
+ if (!done)
+ {
+ // Operation has not finished yet, so leave at front of queue, and
+ // remove from the cleanup list.
+ cleanup_operations_ = this_op->next_;
+ this_op->next_ = i->second;
+ i->second = this_op;
+ return;
+ }
+ operations_.erase(i);
+ }
+ }
+ }
+
+ // Fill a descriptor set with the descriptors corresponding to each active
+ // operation.
+ template <typename Descriptor_Set>
+ void get_descriptors(Descriptor_Set& descriptors)
+ {
+ typename operation_map::iterator i = operations_.begin();
+ while (i != operations_.end())
+ {
+ descriptors.set(i->first);
+ ++i;
+ }
+ }
+
+ // Dispatch the operations corresponding to the ready file descriptors
+ // contained in the given descriptor set.
+ template <typename Descriptor_Set>
+ void dispatch_descriptors(const Descriptor_Set& descriptors, int result)
+ {
+ typename operation_map::iterator i = operations_.begin();
+ while (i != operations_.end())
+ {
+ typename operation_map::iterator op_iter = i++;
+ if (descriptors.is_set(op_iter->first))
+ {
+ op_base* this_op = op_iter->second;
+ op_iter->second = this_op->next_;
+ this_op->next_ = cleanup_operations_;
+ cleanup_operations_ = this_op;
+ bool done = this_op->invoke(result);
+ if (done)
+ {
+ if (!op_iter->second)
+ operations_.erase(op_iter);
+ }
+ else
+ {
+ // Operation has not finished yet, so leave at front of queue, and
+ // remove from the cleanup list.
+ cleanup_operations_ = this_op->next_;
+ this_op->next_ = op_iter->second;
+ op_iter->second = this_op;
+ }
+ }
+ }
+ }
+
+ // Dispatch any pending cancels for operations.
+ void dispatch_cancellations()
+ {
+ while (cancelled_operations_)
+ {
+ op_base* this_op = cancelled_operations_;
+ cancelled_operations_ = this_op->next_;
+ this_op->next_ = cleanup_operations_;
+ cleanup_operations_ = this_op;
+ this_op->invoke(asio::error::operation_aborted);
+ }
+ }
+
+ // Destroy operations that are waiting to be cleaned up.
+ void cleanup_operations()
+ {
+ while (cleanup_operations_)
+ {
+ op_base* next_op = cleanup_operations_->next_;
+ cleanup_operations_->next_ = 0;
+ cleanup_operations_->destroy();
+ cleanup_operations_ = next_op;
+ }
+ }
+
+ // Destroy all operations owned by the queue.
+ void destroy_operations()
+ {
+ while (cancelled_operations_)
+ {
+ op_base* next_op = cancelled_operations_->next_;
+ cancelled_operations_->next_ = 0;
+ cancelled_operations_->destroy();
+ cancelled_operations_ = next_op;
+ }
+
+ while (cleanup_operations_)
+ {
+ op_base* next_op = cleanup_operations_->next_;
+ cleanup_operations_->next_ = 0;
+ cleanup_operations_->destroy();
+ cleanup_operations_ = next_op;
+ }
+
+ typename operation_map::iterator i = operations_.begin();
+ while (i != operations_.end())
+ {
+ typename operation_map::iterator op_iter = i++;
+ op_base* curr_op = op_iter->second;
+ operations_.erase(op_iter);
+ while (curr_op)
+ {
+ op_base* next_op = curr_op->next_;
+ curr_op->next_ = 0;
+ curr_op->destroy();
+ curr_op = next_op;
+ }
+ }
+ }
+
+private:
+ // Base class for reactor operations. A function pointer is used instead of
+ // virtual functions to avoid the associated overhead.
+ class op_base
+ {
+ public:
+ // Get the descriptor associated with the operation.
+ Descriptor descriptor() const
+ {
+ return descriptor_;
+ }
+
+ // Perform the operation.
+ bool invoke(int result)
+ {
+ return invoke_func_(this, result);
+ }
+
+ // Destroy the operation.
+ void destroy()
+ {
+ return destroy_func_(this);
+ }
+
+ protected:
+ typedef bool (*invoke_func_type)(op_base*, int);
+ typedef void (*destroy_func_type)(op_base*);
+
+ // Construct an operation for the given descriptor.
+ op_base(invoke_func_type invoke_func,
+ destroy_func_type destroy_func, Descriptor descriptor)
+ : invoke_func_(invoke_func),
+ destroy_func_(destroy_func),
+ descriptor_(descriptor),
+ next_(0)
+ {
+ }
+
+ // Prevent deletion through this type.
+ ~op_base()
+ {
+ }
+
+ private:
+ friend class reactor_op_queue<Descriptor>;
+
+ // The function to be called to dispatch the handler.
+ invoke_func_type invoke_func_;
+
+ // The function to be called to delete the handler.
+ destroy_func_type destroy_func_;
+
+ // The descriptor associated with the operation.
+ Descriptor descriptor_;
+
+ // The next operation for the same file descriptor.
+ op_base* next_;
+ };
+
+ // Adaptor class template for using handlers in operations.
+ template <typename Handler>
+ class op
+ : public op_base
+ {
+ public:
+ // Constructor.
+ op(Descriptor descriptor, Handler handler)
+ : op_base(&op<Handler>::invoke_handler,
+ &op<Handler>::destroy_handler, descriptor),
+ handler_(handler)
+ {
+ }
+
+ // Invoke the handler.
+ static bool invoke_handler(op_base* base, int result)
+ {
+ return static_cast<op<Handler>*>(base)->handler_(result);
+ }
+
+ // Delete the handler.
+ static void destroy_handler(op_base* base)
+ {
+ delete static_cast<op<Handler>*>(base);
+ }
+
+ private:
+ Handler handler_;
+ };
+
+ // The type for a map of operations.
+ typedef hash_map<Descriptor, op_base*> operation_map;
+
+ // The operations that are currently executing asynchronously.
+ operation_map operations_;
+
+ // The list of operations that have been cancelled.
+ op_base* cancelled_operations_;
+
+ // The list of operations to be destroyed.
+ op_base* cleanup_operations_;
+};
+
+} // namespace detail
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_DETAIL_REACTOR_OP_QUEUE_HPP
diff --git a/library/include/libtorrent/asio/detail/resolver_service.hpp b/library/include/libtorrent/asio/detail/resolver_service.hpp
new file mode 100644
index 000000000..31c87e58e
--- /dev/null
+++ b/library/include/libtorrent/asio/detail/resolver_service.hpp
@@ -0,0 +1,361 @@
+//
+// resolver_service.hpp
+// ~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_RESOLVER_SERVICE_HPP
+#define ASIO_DETAIL_RESOLVER_SERVICE_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/detail/push_options.hpp"
+#include <cstring>
+#include <boost/scoped_ptr.hpp>
+#include <boost/shared_ptr.hpp>
+#include <boost/weak_ptr.hpp>
+#include "asio/detail/pop_options.hpp"
+
+#include "asio/error.hpp"
+#include "asio/io_service.hpp"
+#include "asio/detail/bind_handler.hpp"
+#include "asio/detail/mutex.hpp"
+#include "asio/detail/noncopyable.hpp"
+#include "asio/detail/socket_ops.hpp"
+#include "asio/detail/socket_types.hpp"
+#include "asio/detail/thread.hpp"
+
+namespace asio {
+namespace detail {
+
+template <typename Protocol>
+class resolver_service
+ : public asio::io_service::service
+{
+private:
+ // Helper class to perform exception-safe cleanup of addrinfo objects.
+ class auto_addrinfo
+ : private asio::detail::noncopyable
+ {
+ public:
+ explicit auto_addrinfo(asio::detail::addrinfo_type* ai)
+ : ai_(ai)
+ {
+ }
+
+ ~auto_addrinfo()
+ {
+ if (ai_)
+ socket_ops::freeaddrinfo(ai_);
+ }
+
+ operator asio::detail::addrinfo_type*()
+ {
+ return ai_;
+ }
+
+ private:
+ asio::detail::addrinfo_type* ai_;
+ };
+
+public:
+ // The implementation type of the resolver. The shared pointer is used as a
+ // cancellation token to indicate to the background thread that the operation
+ // has been cancelled.
+ typedef boost::shared_ptr<void> implementation_type;
+ struct noop_deleter { void operator()(void*) {} };
+
+ // The endpoint type.
+ typedef typename Protocol::endpoint endpoint_type;
+
+ // The query type.
+ typedef typename Protocol::resolver_query query_type;
+
+ // The iterator type.
+ typedef typename Protocol::resolver_iterator iterator_type;
+
+ // Constructor.
+ resolver_service(asio::io_service& io_service)
+ : asio::io_service::service(io_service),
+ mutex_(),
+ work_io_service_(new asio::io_service),
+ work_(new asio::io_service::work(*work_io_service_)),
+ work_thread_(0)
+ {
+ }
+
+ // Destructor.
+ ~resolver_service()
+ {
+ shutdown_service();
+ }
+
+ // Destroy all user-defined handler objects owned by the service.
+ void shutdown_service()
+ {
+ work_.reset();
+ if (work_io_service_)
+ {
+ work_io_service_->interrupt();
+ if (work_thread_)
+ {
+ work_thread_->join();
+ work_thread_.reset();
+ }
+ work_io_service_.reset();
+ }
+ }
+
+ // Construct a new resolver implementation.
+ void construct(implementation_type& impl)
+ {
+ impl.reset(static_cast<void*>(0), noop_deleter());
+ }
+
+ // Destroy a resolver implementation.
+ void destroy(implementation_type&)
+ {
+ }
+
+ // Cancel pending asynchronous operations.
+ void cancel(implementation_type& impl)
+ {
+ impl.reset(static_cast<void*>(0), noop_deleter());
+ }
+
+ // Resolve a query to a list of entries.
+ template <typename Error_Handler>
+ iterator_type resolve(implementation_type&, const query_type& query,
+ Error_Handler error_handler)
+ {
+ asio::detail::addrinfo_type* address_info = 0;
+ std::string host_name = query.host_name();
+ std::string service_name = query.service_name();
+ asio::detail::addrinfo_type hints = query.hints();
+
+ int result = socket_ops::getaddrinfo(
+ host_name.length() ? host_name.c_str() : 0,
+ service_name.c_str(), &hints, &address_info);
+ auto_addrinfo auto_address_info(address_info);
+
+ error_handler(asio::error(result));
+ if (result != 0)
+ return iterator_type();
+ return iterator_type::create(address_info, host_name, service_name);
+ }
+
+ template <typename Handler>
+ class resolve_query_handler
+ {
+ public:
+ resolve_query_handler(implementation_type impl, const query_type& query,
+ asio::io_service& io_service, Handler handler)
+ : impl_(impl),
+ query_(query),
+ io_service_(io_service),
+ work_(io_service),
+ handler_(handler)
+ {
+ }
+
+ void operator()()
+ {
+ // Check if the operation has been cancelled.
+ if (impl_.expired())
+ {
+ iterator_type iterator;
+ io_service_.post(asio::detail::bind_handler(handler_,
+ asio::error(asio::error::operation_aborted),
+ iterator));
+ return;
+ }
+
+ // Perform the blocking host resolution operation.
+ asio::detail::addrinfo_type* address_info = 0;
+ std::string host_name = query_.host_name();
+ std::string service_name = query_.service_name();
+ asio::detail::addrinfo_type hints = query_.hints();
+ int result = socket_ops::getaddrinfo(
+ host_name.length() ? host_name.c_str() : 0,
+ service_name.c_str(), &hints, &address_info);
+ auto_addrinfo auto_address_info(address_info);
+
+ // Invoke the handler and pass the result.
+ asio::error e(result);
+ iterator_type iterator;
+ if (result == 0)
+ iterator = iterator_type::create(address_info, host_name, service_name);
+ io_service_.post(asio::detail::bind_handler(
+ handler_, e, iterator));
+ }
+
+ private:
+ boost::weak_ptr<void> impl_;
+ query_type query_;
+ asio::io_service& io_service_;
+ asio::io_service::work work_;
+ Handler handler_;
+ };
+
+ // Asynchronously resolve a query to a list of entries.
+ template <typename Handler>
+ void async_resolve(implementation_type& impl, const query_type& query,
+ Handler handler)
+ {
+ if (work_io_service_)
+ {
+ start_work_thread();
+ work_io_service_->post(
+ resolve_query_handler<Handler>(
+ impl, query, io_service(), handler));
+ }
+ }
+
+ // Resolve an endpoint to a list of entries.
+ template <typename Error_Handler>
+ iterator_type resolve(implementation_type&,
+ const endpoint_type& endpoint, Error_Handler error_handler)
+ {
+ // First try resolving with the service name. If that fails try resolving
+ // but allow the service to be returned as a number.
+ char host_name[NI_MAXHOST];
+ char service_name[NI_MAXSERV];
+ int flags = endpoint.protocol().type() == SOCK_DGRAM ? NI_DGRAM : 0;
+ int result = socket_ops::getnameinfo(endpoint.data(), endpoint.size(),
+ host_name, NI_MAXHOST, service_name, NI_MAXSERV, flags);
+ if (result)
+ {
+ flags |= NI_NUMERICSERV;
+ result = socket_ops::getnameinfo(endpoint.data(), endpoint.size(),
+ host_name, NI_MAXHOST, service_name, NI_MAXSERV, flags);
+ }
+
+ error_handler(asio::error(result));
+ if (result != 0)
+ return iterator_type();
+ return iterator_type::create(endpoint, host_name, service_name);
+ }
+
+ template <typename Handler>
+ class resolve_endpoint_handler
+ {
+ public:
+ resolve_endpoint_handler(implementation_type impl,
+ const endpoint_type& endpoint, asio::io_service& io_service,
+ Handler handler)
+ : impl_(impl),
+ endpoint_(endpoint),
+ io_service_(io_service),
+ work_(io_service),
+ handler_(handler)
+ {
+ }
+
+ void operator()()
+ {
+ // Check if the operation has been cancelled.
+ if (impl_.expired())
+ {
+ iterator_type iterator;
+ io_service_.post(asio::detail::bind_handler(handler_,
+ asio::error(asio::error::operation_aborted),
+ iterator));
+ return;
+ }
+
+
+ // First try resolving with the service name. If that fails try resolving
+ // but allow the service to be returned as a number.
+ char host_name[NI_MAXHOST];
+ char service_name[NI_MAXSERV];
+ int flags = endpoint_.protocol().type() == SOCK_DGRAM ? NI_DGRAM : 0;
+ int result = socket_ops::getnameinfo(endpoint_.data(), endpoint_.size(),
+ host_name, NI_MAXHOST, service_name, NI_MAXSERV, flags);
+ if (result)
+ {
+ flags |= NI_NUMERICSERV;
+ result = socket_ops::getnameinfo(endpoint_.data(), endpoint_.size(),
+ host_name, NI_MAXHOST, service_name, NI_MAXSERV, flags);
+ }
+
+ // Invoke the handler and pass the result.
+ asio::error e(result);
+ iterator_type iterator;
+ if (result == 0)
+ iterator = iterator_type::create(endpoint_, host_name, service_name);
+ io_service_.post(asio::detail::bind_handler(
+ handler_, e, iterator));
+ }
+
+ private:
+ boost::weak_ptr<void> impl_;
+ endpoint_type endpoint_;
+ asio::io_service& io_service_;
+ asio::io_service::work work_;
+ Handler handler_;
+ };
+
+ // Asynchronously resolve an endpoint to a list of entries.
+ template <typename Handler>
+ void async_resolve(implementation_type& impl, const endpoint_type& endpoint,
+ Handler handler)
+ {
+ if (work_io_service_)
+ {
+ start_work_thread();
+ work_io_service_->post(
+ resolve_endpoint_handler<Handler>(
+ impl, endpoint, io_service(), handler));
+ }
+ }
+
+private:
+ // Helper class to run the work io_service in a thread.
+ class work_io_service_runner
+ {
+ public:
+ work_io_service_runner(asio::io_service& io_service)
+ : io_service_(io_service) {}
+ void operator()() { io_service_.run(); }
+ private:
+ asio::io_service& io_service_;
+ };
+
+ // Start the work thread if it's not already running.
+ void start_work_thread()
+ {
+ asio::detail::mutex::scoped_lock lock(mutex_);
+ if (work_thread_ == 0)
+ {
+ work_thread_.reset(new asio::detail::thread(
+ work_io_service_runner(*work_io_service_)));
+ }
+ }
+
+ // Mutex to protect access to internal data.
+ asio::detail::mutex mutex_;
+
+ // Private io_service used for performing asynchronous host resolution.
+ boost::scoped_ptr<asio::io_service> work_io_service_;
+
+ // Work for the private io_service to perform.
+ boost::scoped_ptr<asio::io_service::work> work_;
+
+ // Thread used for running the work io_service's run loop.
+ boost::scoped_ptr<asio::detail::thread> work_thread_;
+};
+
+} // namespace detail
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_DETAIL_RESOLVER_SERVICE_HPP
diff --git a/library/include/libtorrent/asio/detail/scoped_lock.hpp b/library/include/libtorrent/asio/detail/scoped_lock.hpp
new file mode 100644
index 000000000..40ad27d2d
--- /dev/null
+++ b/library/include/libtorrent/asio/detail/scoped_lock.hpp
@@ -0,0 +1,79 @@
+//
+// scoped_lock.hpp
+// ~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_SCOPED_LOCK_HPP
+#define ASIO_DETAIL_SCOPED_LOCK_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/detail/noncopyable.hpp"
+
+namespace asio {
+namespace detail {
+
+// Helper class to lock and unlock a mutex automatically.
+template <typename Mutex>
+class scoped_lock
+ : private noncopyable
+{
+public:
+ // Constructor acquires the lock.
+ scoped_lock(Mutex& m)
+ : mutex_(m)
+ {
+ mutex_.lock();
+ locked_ = true;
+ }
+
+ // Destructor releases the lock.
+ ~scoped_lock()
+ {
+ if (locked_)
+ mutex_.unlock();
+ }
+
+ // Explicitly acquire the lock.
+ void lock()
+ {
+ if (!locked_)
+ {
+ mutex_.lock();
+ locked_ = true;
+ }
+ }
+
+ // Explicitly release the lock.
+ void unlock()
+ {
+ if (locked_)
+ {
+ mutex_.unlock();
+ locked_ = false;
+ }
+ }
+
+private:
+ // The underlying mutex.
+ Mutex& mutex_;
+
+ // Whether the mutex is currently locked or unlocked.
+ bool locked_;
+};
+
+} // namespace detail
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_DETAIL_SCOPED_LOCK_HPP
diff --git a/library/include/libtorrent/asio/detail/select_interrupter.hpp b/library/include/libtorrent/asio/detail/select_interrupter.hpp
new file mode 100644
index 000000000..6b4d51316
--- /dev/null
+++ b/library/include/libtorrent/asio/detail/select_interrupter.hpp
@@ -0,0 +1,41 @@
+//
+// select_interrupter.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_SELECT_INTERRUPTER_HPP
+#define ASIO_DETAIL_SELECT_INTERRUPTER_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/detail/push_options.hpp"
+#include <boost/config.hpp>
+#include "asio/detail/pop_options.hpp"
+
+#include "asio/detail/pipe_select_interrupter.hpp"
+#include "asio/detail/socket_select_interrupter.hpp"
+
+namespace asio {
+namespace detail {
+
+#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+typedef socket_select_interrupter select_interrupter;
+#else
+typedef pipe_select_interrupter select_interrupter;
+#endif
+
+} // namespace detail
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_DETAIL_SELECT_INTERRUPTER_HPP
diff --git a/library/include/libtorrent/asio/detail/select_reactor.hpp b/library/include/libtorrent/asio/detail/select_reactor.hpp
new file mode 100644
index 000000000..e6335aef7
--- /dev/null
+++ b/library/include/libtorrent/asio/detail/select_reactor.hpp
@@ -0,0 +1,435 @@
+//
+// select_reactor.hpp
+// ~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_SELECT_REACTOR_HPP
+#define ASIO_DETAIL_SELECT_REACTOR_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/detail/socket_types.hpp" // Must come before posix_time.
+
+#include "asio/detail/push_options.hpp"
+#include <cstddef>
+#include <boost/config.hpp>
+#include <boost/date_time/posix_time/posix_time_types.hpp>
+#include <vector>
+#include "asio/detail/pop_options.hpp"
+
+#include "asio/io_service.hpp"
+#include "asio/detail/bind_handler.hpp"
+#include "asio/detail/fd_set_adapter.hpp"
+#include "asio/detail/mutex.hpp"
+#include "asio/detail/noncopyable.hpp"
+#include "asio/detail/task_io_service.hpp"
+#include "asio/detail/thread.hpp"
+#include "asio/detail/reactor_op_queue.hpp"
+#include "asio/detail/select_interrupter.hpp"
+#include "asio/detail/select_reactor_fwd.hpp"
+#include "asio/detail/signal_blocker.hpp"
+#include "asio/detail/socket_ops.hpp"
+#include "asio/detail/socket_types.hpp"
+#include "asio/detail/timer_queue.hpp"
+
+namespace asio {
+namespace detail {
+
+template <bool Own_Thread>
+class select_reactor
+ : public asio::io_service::service
+{
+public:
+ // Constructor.
+ select_reactor(asio::io_service& io_service)
+ : asio::io_service::service(io_service),
+ mutex_(),
+ select_in_progress_(false),
+ interrupter_(),
+ read_op_queue_(),
+ write_op_queue_(),
+ except_op_queue_(),
+ pending_cancellations_(),
+ stop_thread_(false),
+ thread_(0),
+ shutdown_(false)
+ {
+ if (Own_Thread)
+ {
+ asio::detail::signal_blocker sb;
+ thread_ = new asio::detail::thread(
+ bind_handler(&select_reactor::call_run_thread, this));
+ }
+ }
+
+ // Destructor.
+ ~select_reactor()
+ {
+ shutdown_service();
+ }
+
+ // Destroy all user-defined handler objects owned by the service.
+ void shutdown_service()
+ {
+ asio::detail::mutex::scoped_lock lock(mutex_);
+ shutdown_ = true;
+ stop_thread_ = true;
+ lock.unlock();
+
+ if (thread_)
+ {
+ interrupter_.interrupt();
+ thread_->join();
+ delete thread_;
+ thread_ = 0;
+ }
+
+ read_op_queue_.destroy_operations();
+ write_op_queue_.destroy_operations();
+ except_op_queue_.destroy_operations();
+
+ for (std::size_t i = 0; i < timer_queues_.size(); ++i)
+ timer_queues_[i]->destroy_timers();
+ timer_queues_.clear();
+ }
+
+ // Register a socket with the reactor. Returns 0 on success, system error
+ // code on failure.
+ int register_descriptor(socket_type descriptor)
+ {
+ return 0;
+ }
+
+ // Start a new read operation. The handler object will be invoked when the
+ // given descriptor is ready to be read, or an error has occurred.
+ template <typename Handler>
+ void start_read_op(socket_type descriptor, Handler handler)
+ {
+ asio::detail::mutex::scoped_lock lock(mutex_);
+ if (!shutdown_)
+ if (read_op_queue_.enqueue_operation(descriptor, handler))
+ interrupter_.interrupt();
+ }
+
+ // Start a new write operation. The handler object will be invoked when the
+ // given descriptor is ready to be written, or an error has occurred.
+ template <typename Handler>
+ void start_write_op(socket_type descriptor, Handler handler)
+ {
+ asio::detail::mutex::scoped_lock lock(mutex_);
+ if (!shutdown_)
+ if (write_op_queue_.enqueue_operation(descriptor, handler))
+ interrupter_.interrupt();
+ }
+
+ // Start a new exception operation. The handler object will be invoked when
+ // the given descriptor has exception information, or an error has occurred.
+ template <typename Handler>
+ void start_except_op(socket_type descriptor, Handler handler)
+ {
+ asio::detail::mutex::scoped_lock lock(mutex_);
+ if (!shutdown_)
+ if (except_op_queue_.enqueue_operation(descriptor, handler))
+ interrupter_.interrupt();
+ }
+
+ // Start new write and exception operations. The handler object will be
+ // invoked when the given descriptor is ready for writing or has exception
+ // information available, or an error has occurred.
+ template <typename Handler>
+ void start_write_and_except_ops(socket_type descriptor, Handler handler)
+ {
+ asio::detail::mutex::scoped_lock lock(mutex_);
+ if (!shutdown_)
+ {
+ bool interrupt = write_op_queue_.enqueue_operation(descriptor, handler);
+ interrupt = except_op_queue_.enqueue_operation(descriptor, handler)
+ || interrupt;
+ if (interrupt)
+ interrupter_.interrupt();
+ }
+ }
+
+ // Cancel all operations associated with the given descriptor. The
+ // handlers associated with the descriptor will be invoked with the
+ // operation_aborted error.
+ void cancel_ops(socket_type descriptor)
+ {
+ asio::detail::mutex::scoped_lock lock(mutex_);
+ cancel_ops_unlocked(descriptor);
+ }
+
+ // Enqueue cancellation of all operations associated with the given
+ // descriptor. The handlers associated with the descriptor will be invoked
+ // with the operation_aborted error. This function does not acquire the
+ // select_reactor's mutex, and so should only be used from within a reactor
+ // handler.
+ void enqueue_cancel_ops_unlocked(socket_type descriptor)
+ {
+ pending_cancellations_.push_back(descriptor);
+ }
+
+ // Cancel any operations that are running against the descriptor and remove
+ // its registration from the reactor.
+ void close_descriptor(socket_type descriptor)
+ {
+ asio::detail::mutex::scoped_lock lock(mutex_);
+ cancel_ops_unlocked(descriptor);
+ }
+
+ // Add a new timer queue to the reactor.
+ template <typename Time_Traits>
+ void add_timer_queue(timer_queue<Time_Traits>& timer_queue)
+ {
+ asio::detail::mutex::scoped_lock lock(mutex_);
+ timer_queues_.push_back(&timer_queue);
+ }
+
+ // Schedule a timer in the given timer queue to expire at the specified
+ // absolute time. The handler object will be invoked when the timer expires.
+ template <typename Time_Traits, typename Handler>
+ void schedule_timer(timer_queue<Time_Traits>& timer_queue,
+ const typename Time_Traits::time_type& time, Handler handler, void* token)
+ {
+ asio::detail::mutex::scoped_lock lock(mutex_);
+ if (!shutdown_)
+ if (timer_queue.enqueue_timer(time, handler, token))
+ interrupter_.interrupt();
+ }
+
+ // Cancel the timer associated with the given token. Returns the number of
+ // handlers that have been posted or dispatched.
+ template <typename Time_Traits>
+ std::size_t cancel_timer(timer_queue<Time_Traits>& timer_queue, void* token)
+ {
+ asio::detail::mutex::scoped_lock lock(mutex_);
+ return timer_queue.cancel_timer(token);
+ }
+
+private:
+ friend class task_io_service<select_reactor<Own_Thread> >;
+
+ // Run select once until interrupted or events are ready to be dispatched.
+ void run(bool block)
+ {
+ asio::detail::mutex::scoped_lock lock(mutex_);
+
+ // Dispatch any operation cancellations that were made while the select
+ // loop was not running.
+ read_op_queue_.dispatch_cancellations();
+ write_op_queue_.dispatch_cancellations();
+ except_op_queue_.dispatch_cancellations();
+
+ // Check if the thread is supposed to stop.
+ if (stop_thread_)
+ {
+ // Clean up operations. We must not hold the lock since the operations may
+ // make calls back into this reactor.
+ lock.unlock();
+ read_op_queue_.cleanup_operations();
+ write_op_queue_.cleanup_operations();
+ except_op_queue_.cleanup_operations();
+ return;
+ }
+
+ // We can return immediately if there's no work to do and the reactor is
+ // not supposed to block.
+ if (!block && read_op_queue_.empty() && write_op_queue_.empty()
+ && except_op_queue_.empty() && all_timer_queues_are_empty())
+ {
+ // Clean up operations. We must not hold the lock since the operations may
+ // make calls back into this reactor.
+ lock.unlock();
+ read_op_queue_.cleanup_operations();
+ write_op_queue_.cleanup_operations();
+ except_op_queue_.cleanup_operations();
+ return;
+ }
+
+ // Set up the descriptor sets.
+ fd_set_adapter read_fds;
+ read_fds.set(interrupter_.read_descriptor());
+ read_op_queue_.get_descriptors(read_fds);
+ fd_set_adapter write_fds;
+ write_op_queue_.get_descriptors(write_fds);
+ fd_set_adapter except_fds;
+ except_op_queue_.get_descriptors(except_fds);
+ socket_type max_fd = read_fds.max_descriptor();
+ if (write_fds.max_descriptor() > max_fd)
+ max_fd = write_fds.max_descriptor();
+ if (except_fds.max_descriptor() > max_fd)
+ max_fd = except_fds.max_descriptor();
+
+ // Block on the select call without holding the lock so that new
+ // operations can be started while the call is executing.
+ timeval tv_buf = { 0, 0 };
+ timeval* tv = block ? get_timeout(tv_buf) : &tv_buf;
+ select_in_progress_ = true;
+ lock.unlock();
+ int retval = socket_ops::select(static_cast<int>(max_fd + 1),
+ read_fds, write_fds, except_fds, tv);
+ lock.lock();
+ select_in_progress_ = false;
+
+ // Block signals while dispatching operations.
+ asio::detail::signal_blocker sb;
+
+ // Reset the interrupter.
+ if (retval > 0 && read_fds.is_set(interrupter_.read_descriptor()))
+ interrupter_.reset();
+
+ // Dispatch all ready operations.
+ if (retval > 0)
+ {
+ // Exception operations must be processed first to ensure that any
+ // out-of-band data is read before normal data.
+ except_op_queue_.dispatch_descriptors(except_fds, 0);
+ read_op_queue_.dispatch_descriptors(read_fds, 0);
+ write_op_queue_.dispatch_descriptors(write_fds, 0);
+ except_op_queue_.dispatch_cancellations();
+ read_op_queue_.dispatch_cancellations();
+ write_op_queue_.dispatch_cancellations();
+ }
+ for (std::size_t i = 0; i < timer_queues_.size(); ++i)
+ timer_queues_[i]->dispatch_timers();
+
+ // Issue any pending cancellations.
+ for (size_t i = 0; i < pending_cancellations_.size(); ++i)
+ cancel_ops_unlocked(pending_cancellations_[i]);
+ pending_cancellations_.clear();
+
+ // Clean up operations. We must not hold the lock since the operations may
+ // make calls back into this reactor.
+ lock.unlock();
+ read_op_queue_.cleanup_operations();
+ write_op_queue_.cleanup_operations();
+ except_op_queue_.cleanup_operations();
+ }
+
+ // Run the select loop in the thread.
+ void run_thread()
+ {
+ asio::detail::mutex::scoped_lock lock(mutex_);
+ while (!stop_thread_)
+ {
+ lock.unlock();
+ run(true);
+ lock.lock();
+ }
+ }
+
+ // Entry point for the select loop thread.
+ static void call_run_thread(select_reactor* reactor)
+ {
+ reactor->run_thread();
+ }
+
+ // Interrupt the select loop.
+ void interrupt()
+ {
+ interrupter_.interrupt();
+ }
+
+ // Check if all timer queues are empty.
+ bool all_timer_queues_are_empty() const
+ {
+ for (std::size_t i = 0; i < timer_queues_.size(); ++i)
+ if (!timer_queues_[i]->empty())
+ return false;
+ return true;
+ }
+
+ // Get the timeout value for the select call.
+ timeval* get_timeout(timeval& tv)
+ {
+ if (all_timer_queues_are_empty())
+ return 0;
+
+ // By default we will wait no longer than 5 minutes. This will ensure that
+ // any changes to the system clock are detected after no longer than this.
+ boost::posix_time::time_duration minimum_wait_duration
+ = boost::posix_time::minutes(5);
+
+ for (std::size_t i = 0; i < timer_queues_.size(); ++i)
+ {
+ boost::posix_time::time_duration wait_duration
+ = timer_queues_[i]->wait_duration();
+ if (wait_duration < minimum_wait_duration)
+ minimum_wait_duration = wait_duration;
+ }
+
+ if (minimum_wait_duration > boost::posix_time::time_duration())
+ {
+ tv.tv_sec = minimum_wait_duration.total_seconds();
+ tv.tv_usec = minimum_wait_duration.total_microseconds() % 1000000;
+ }
+ else
+ {
+ tv.tv_sec = 0;
+ tv.tv_usec = 0;
+ }
+
+ return &tv;
+ }
+
+ // Cancel all operations associated with the given descriptor. The do_cancel
+ // function of the handler objects will be invoked. This function does not
+ // acquire the select_reactor's mutex.
+ void cancel_ops_unlocked(socket_type descriptor)
+ {
+ bool interrupt = read_op_queue_.cancel_operations(descriptor);
+ interrupt = write_op_queue_.cancel_operations(descriptor) || interrupt;
+ interrupt = except_op_queue_.cancel_operations(descriptor) || interrupt;
+ if (interrupt)
+ interrupter_.interrupt();
+ }
+
+ // Mutex to protect access to internal data.
+ asio::detail::mutex mutex_;
+
+ // Whether the select loop is currently running or not.
+ bool select_in_progress_;
+
+ // The interrupter is used to break a blocking select call.
+ select_interrupter interrupter_;
+
+ // The queue of read operations.
+ reactor_op_queue<socket_type> read_op_queue_;
+
+ // The queue of write operations.
+ reactor_op_queue<socket_type> write_op_queue_;
+
+ // The queue of exception operations.
+ reactor_op_queue<socket_type> except_op_queue_;
+
+ // The timer queues.
+ std::vector<timer_queue_base*> timer_queues_;
+
+ // The descriptors that are pending cancellation.
+ std::vector<socket_type> pending_cancellations_;
+
+ // Does the reactor loop thread need to stop.
+ bool stop_thread_;
+
+ // The thread that is running the reactor loop.
+ asio::detail::thread* thread_;
+
+ // Whether the service has been shut down.
+ bool shutdown_;
+};
+
+} // namespace detail
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_DETAIL_SELECT_REACTOR_HPP
diff --git a/library/include/libtorrent/asio/detail/select_reactor_fwd.hpp b/library/include/libtorrent/asio/detail/select_reactor_fwd.hpp
new file mode 100644
index 000000000..35c524c7e
--- /dev/null
+++ b/library/include/libtorrent/asio/detail/select_reactor_fwd.hpp
@@ -0,0 +1,31 @@
+//
+// select_reactor_fwd.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_SELECT_REACTOR_FWD_HPP
+#define ASIO_DETAIL_SELECT_REACTOR_FWD_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+namespace asio {
+namespace detail {
+
+template <bool Own_Thread>
+class select_reactor;
+
+} // namespace detail
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_DETAIL_SELECT_REACTOR_FWD_HPP
diff --git a/library/include/libtorrent/asio/detail/service_registry.hpp b/library/include/libtorrent/asio/detail/service_registry.hpp
new file mode 100644
index 000000000..d7164d088
--- /dev/null
+++ b/library/include/libtorrent/asio/detail/service_registry.hpp
@@ -0,0 +1,163 @@
+//
+// service_registry.hpp
+// ~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_SERVICE_REGISTRY_HPP
+#define ASIO_DETAIL_SERVICE_REGISTRY_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/detail/push_options.hpp"
+#include <memory>
+#include <typeinfo>
+#include "asio/detail/pop_options.hpp"
+
+#include "asio/detail/mutex.hpp"
+#include "asio/detail/noncopyable.hpp"
+
+namespace asio {
+namespace detail {
+
+template <typename Owner>
+class service_registry
+ : private noncopyable
+{
+public:
+ // Constructor.
+ service_registry(Owner& o)
+ : owner_(o),
+ first_service_(0)
+ {
+ }
+
+ // Destructor.
+ ~service_registry()
+ {
+ // Shutdown all services. This must be done in a separate loop before the
+ // services are destroyed since the destructors of user-defined handler
+ // objects may try to access other service objects.
+ typename Owner::service* service = first_service_;
+ while (service)
+ {
+ service->shutdown_service();
+ service = service->next_;
+ }
+
+ // Destroy all services.
+ while (first_service_)
+ {
+ typename Owner::service* next_service = first_service_->next_;
+ delete first_service_;
+ first_service_ = next_service;
+ }
+ }
+
+ // Get the service object corresponding to the specified service type. Will
+ // create a new service object automatically if no such object already
+ // exists. Ownership of the service object is not transferred to the caller.
+ template <typename Service>
+ Service& use_service()
+ {
+ asio::detail::mutex::scoped_lock lock(mutex_);
+
+ // First see if there is an existing service object for the given type.
+ typename Owner::service* service = first_service_;
+ while (service)
+ {
+ if (*service->type_info_ == typeid(Service))
+ return *static_cast<Service*>(service);
+ service = service->next_;
+ }
+
+ // Create a new service object. The service registry's mutex is not locked
+ // at this time to allow for nested calls into this function from the new
+ // service's constructor.
+ lock.unlock();
+ std::auto_ptr<Service> new_service(new Service(owner_));
+ new_service->type_info_ = &typeid(Service);
+ Service& new_service_ref = *new_service;
+ lock.lock();
+
+ // Check that nobody else created another service object of the same type
+ // while the lock was released.
+ service = first_service_;
+ while (service)
+ {
+ if (*service->type_info_ == typeid(Service))
+ return *static_cast<Service*>(service);
+ service = service->next_;
+ }
+
+ // Service was successfully initialised, pass ownership to registry.
+ new_service->next_ = first_service_;
+ first_service_ = new_service.release();
+
+ return new_service_ref;
+ }
+
+ // Add a service object. Returns false on error, in which case ownership of
+ // the object is retained by the caller.
+ template <typename Service>
+ bool add_service(Service* new_service)
+ {
+ asio::detail::mutex::scoped_lock lock(mutex_);
+
+ // Check if there is an existing service object for the given type.
+ typename Owner::service* service = first_service_;
+ while (service)
+ {
+ if (*service->type_info_ == typeid(Service))
+ return false;
+ service = service->next_;
+ }
+
+ // Take ownership of the service object.
+ new_service->type_info_ = &typeid(Service);
+ new_service->next_ = first_service_;
+ first_service_ = new_service;
+ }
+
+ // Check whether a service object of the specified type already exists.
+ template <typename Service>
+ bool has_service() const
+ {
+ asio::detail::mutex::scoped_lock lock(mutex_);
+
+ typename Owner::service* service = first_service_;
+ while (service)
+ {
+ if (*service->type_info_ == typeid(Service))
+ return true;
+ service = service->next_;
+ }
+
+ return false;
+ }
+
+private:
+ // Mutex to protect access to internal data.
+ mutable asio::detail::mutex mutex_;
+
+ // The owner of this service registry and the services it contains.
+ Owner& owner_;
+
+ // The first service in the list of contained services.
+ typename Owner::service* first_service_;
+};
+
+} // namespace detail
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_DETAIL_SERVICE_REGISTRY_HPP
diff --git a/library/include/libtorrent/asio/detail/signal_blocker.hpp b/library/include/libtorrent/asio/detail/signal_blocker.hpp
new file mode 100644
index 000000000..92299e3e7
--- /dev/null
+++ b/library/include/libtorrent/asio/detail/signal_blocker.hpp
@@ -0,0 +1,50 @@
+//
+// signal_blocker.hpp
+// ~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_SIGNAL_BLOCKER_HPP
+#define ASIO_DETAIL_SIGNAL_BLOCKER_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/detail/push_options.hpp"
+#include <boost/config.hpp>
+#include "asio/detail/pop_options.hpp"
+
+#if !defined(BOOST_HAS_THREADS)
+# include "asio/detail/null_signal_blocker.hpp"
+#elif defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+# include "asio/detail/win_signal_blocker.hpp"
+#elif defined(BOOST_HAS_PTHREADS)
+# include "asio/detail/posix_signal_blocker.hpp"
+#else
+# error Only Windows and POSIX are supported!
+#endif
+
+namespace asio {
+namespace detail {
+
+#if !defined(BOOST_HAS_THREADS)
+typedef null_signal_blocker signal_blocker;
+#elif defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+typedef win_signal_blocker signal_blocker;
+#elif defined(BOOST_HAS_PTHREADS)
+typedef posix_signal_blocker signal_blocker;
+#endif
+
+} // namespace detail
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_DETAIL_SIGNAL_BLOCKER_HPP
diff --git a/library/include/libtorrent/asio/detail/signal_init.hpp b/library/include/libtorrent/asio/detail/signal_init.hpp
new file mode 100644
index 000000000..95c05e852
--- /dev/null
+++ b/library/include/libtorrent/asio/detail/signal_init.hpp
@@ -0,0 +1,51 @@
+//
+// signal_init.hpp
+// ~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_SIGNAL_INIT_HPP
+#define ASIO_DETAIL_SIGNAL_INIT_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/detail/push_options.hpp"
+#include <boost/config.hpp>
+#include "asio/detail/pop_options.hpp"
+
+#if !defined(BOOST_WINDOWS) && !defined(__CYGWIN__)
+
+#include "asio/detail/push_options.hpp"
+#include <csignal>
+#include "asio/detail/pop_options.hpp"
+
+namespace asio {
+namespace detail {
+
+template <int Signal = SIGPIPE>
+class signal_init
+{
+public:
+ // Constructor.
+ signal_init()
+ {
+ std::signal(Signal, SIG_IGN);
+ }
+};
+
+} // namespace detail
+} // namespace asio
+
+#endif // !defined(BOOST_WINDOWS) && !defined(__CYGWIN__)
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_DETAIL_SIGNAL_INIT_HPP
diff --git a/library/include/libtorrent/asio/detail/socket_holder.hpp b/library/include/libtorrent/asio/detail/socket_holder.hpp
new file mode 100644
index 000000000..a94e01e2c
--- /dev/null
+++ b/library/include/libtorrent/asio/detail/socket_holder.hpp
@@ -0,0 +1,91 @@
+//
+// socket_holder.hpp
+// ~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_SOCKET_HOLDER_HPP
+#define ASIO_DETAIL_SOCKET_HOLDER_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/detail/noncopyable.hpp"
+#include "asio/detail/socket_ops.hpp"
+
+namespace asio {
+namespace detail {
+
+// Implement the resource acquisition is initialisation idiom for sockets.
+class socket_holder
+ : private noncopyable
+{
+public:
+ // Construct as an uninitialised socket.
+ socket_holder()
+ : socket_(invalid_socket)
+ {
+ }
+
+ // Construct to take ownership of the specified socket.
+ explicit socket_holder(socket_type s)
+ : socket_(s)
+ {
+ }
+
+ // Destructor.
+ ~socket_holder()
+ {
+ if (socket_ != invalid_socket)
+ socket_ops::close(socket_);
+ }
+
+ // Get the underlying socket.
+ socket_type get() const
+ {
+ return socket_;
+ }
+
+ // Reset to an uninitialised socket.
+ void reset()
+ {
+ if (socket_ != invalid_socket)
+ {
+ socket_ops::close(socket_);
+ socket_ = invalid_socket;
+ }
+ }
+
+ // Reset to take ownership of the specified socket.
+ void reset(socket_type s)
+ {
+ reset();
+ socket_ = s;
+ }
+
+ // Release ownership of the socket.
+ socket_type release()
+ {
+ socket_type tmp = socket_;
+ socket_ = invalid_socket;
+ return tmp;
+ }
+
+private:
+ // The underlying socket.
+ socket_type socket_;
+};
+
+} // namespace detail
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_DETAIL_SOCKET_HOLDER_HPP
diff --git a/library/include/libtorrent/asio/detail/socket_ops.hpp b/library/include/libtorrent/asio/detail/socket_ops.hpp
new file mode 100644
index 000000000..c3f4a59b2
--- /dev/null
+++ b/library/include/libtorrent/asio/detail/socket_ops.hpp
@@ -0,0 +1,1534 @@
+//
+// socket_ops.hpp
+// ~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_SOCKET_OPS_HPP
+#define ASIO_DETAIL_SOCKET_OPS_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/detail/push_options.hpp"
+#include <boost/config.hpp>
+#include <cstdlib>
+#include <cstring>
+#include <cerrno>
+#include <boost/detail/workaround.hpp>
+#include <new>
+#if defined(__MACH__) && defined(__APPLE__)
+# include <AvailabilityMacros.h>
+#endif // defined(__MACH__) && defined(__APPLE__)
+#include "asio/detail/pop_options.hpp"
+
+#include "asio/error.hpp"
+#include "asio/detail/socket_types.hpp"
+
+namespace asio {
+namespace detail {
+namespace socket_ops {
+
+inline int get_error()
+{
+#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+ return WSAGetLastError();
+#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+ return errno;
+#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+}
+
+inline void set_error(int error)
+{
+ errno = error;
+#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+ WSASetLastError(error);
+#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+}
+
+template <typename ReturnType>
+inline ReturnType error_wrapper(ReturnType return_value)
+{
+#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+ errno = WSAGetLastError();
+#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+ return return_value;
+}
+
+inline socket_type accept(socket_type s, socket_addr_type* addr,
+ socket_addr_len_type* addrlen)
+{
+ set_error(0);
+#if defined(__MACH__) && defined(__APPLE__)
+ socket_type new_s = error_wrapper(::accept(s, addr, addrlen));
+ if (new_s == invalid_socket)
+ return new_s;
+
+ int optval = 1;
+ int result = error_wrapper(::setsockopt(new_s,
+ SOL_SOCKET, SO_NOSIGPIPE, &optval, sizeof(optval)));
+ if (result != 0)
+ {
+ ::close(new_s);
+ return invalid_socket;
+ }
+
+ return new_s;
+#else
+ return error_wrapper(::accept(s, addr, addrlen));
+#endif
+}
+
+inline int bind(socket_type s, const socket_addr_type* addr,
+ socket_addr_len_type addrlen)
+{
+ set_error(0);
+ return error_wrapper(::bind(s, addr, addrlen));
+}
+
+inline int close(socket_type s)
+{
+ set_error(0);
+#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+ return error_wrapper(::closesocket(s));
+#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+ return error_wrapper(::close(s));
+#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+}
+
+inline int shutdown(socket_type s, int what)
+{
+ set_error(0);
+ return error_wrapper(::shutdown(s, what));
+}
+
+inline int connect(socket_type s, const socket_addr_type* addr,
+ socket_addr_len_type addrlen)
+{
+ set_error(0);
+ return error_wrapper(::connect(s, addr, addrlen));
+}
+
+inline int listen(socket_type s, int backlog)
+{
+ set_error(0);
+ return error_wrapper(::listen(s, backlog));
+}
+
+#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+typedef WSABUF buf;
+#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+typedef iovec buf;
+#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+
+inline void init_buf(buf& b, void* data, size_t size)
+{
+#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+ b.buf = static_cast<char*>(data);
+ b.len = static_cast<u_long>(size);
+#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+ b.iov_base = data;
+ b.iov_len = size;
+#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+}
+
+inline void init_buf(buf& b, const void* data, size_t size)
+{
+#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+ b.buf = static_cast<char*>(const_cast<void*>(data));
+ b.len = static_cast<u_long>(size);
+#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+ b.iov_base = const_cast<void*>(data);
+ b.iov_len = size;
+#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+}
+
+inline int recv(socket_type s, buf* bufs, size_t count, int flags)
+{
+ set_error(0);
+#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+ // Receive some data.
+ DWORD recv_buf_count = static_cast<DWORD>(count);
+ DWORD bytes_transferred = 0;
+ DWORD recv_flags = flags;
+ int result = error_wrapper(::WSARecv(s, bufs,
+ recv_buf_count, &bytes_transferred, &recv_flags, 0, 0));
+ if (result != 0)
+ return -1;
+ return bytes_transferred;
+#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+ msghdr msg;
+ msg.msg_name = 0;
+ msg.msg_namelen = 0;
+ msg.msg_iov = bufs;
+ msg.msg_iovlen = count;
+ msg.msg_control = 0;
+ msg.msg_controllen = 0;
+ msg.msg_flags = 0;
+ return error_wrapper(::recvmsg(s, &msg, flags));
+#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+}
+
+inline int recvfrom(socket_type s, buf* bufs, size_t count, int flags,
+ socket_addr_type* addr, socket_addr_len_type* addrlen)
+{
+ set_error(0);
+#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+ // Receive some data.
+ DWORD recv_buf_count = static_cast<DWORD>(count);
+ DWORD bytes_transferred = 0;
+ DWORD recv_flags = flags;
+ int result = error_wrapper(::WSARecvFrom(s, bufs, recv_buf_count,
+ &bytes_transferred, &recv_flags, addr, addrlen, 0, 0));
+ if (result != 0)
+ return -1;
+ return bytes_transferred;
+#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+ msghdr msg;
+#if defined(__MACH__) && defined(__APPLE__) \
+ && (MAC_OS_X_VERSION_MAX_ALLOWED < 1040)
+ msg.msg_name = reinterpret_cast<char*>(addr);
+#else
+ msg.msg_name = addr;
+#endif
+ msg.msg_namelen = *addrlen;
+ msg.msg_iov = bufs;
+ msg.msg_iovlen = count;
+ msg.msg_control = 0;
+ msg.msg_controllen = 0;
+ msg.msg_flags = 0;
+ int result = error_wrapper(::recvmsg(s, &msg, flags));
+ *addrlen = msg.msg_namelen;
+ return result;
+#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+}
+
+inline int send(socket_type s, const buf* bufs, size_t count, int flags)
+{
+ set_error(0);
+#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+ // Send the data.
+ DWORD send_buf_count = static_cast<DWORD>(count);
+ DWORD bytes_transferred = 0;
+ DWORD send_flags = flags;
+ int result = error_wrapper(::WSASend(s, const_cast<buf*>(bufs),
+ send_buf_count, &bytes_transferred, send_flags, 0, 0));
+ if (result != 0)
+ return -1;
+ return bytes_transferred;
+#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+ msghdr msg;
+ msg.msg_name = 0;
+ msg.msg_namelen = 0;
+ msg.msg_iov = const_cast<buf*>(bufs);
+ msg.msg_iovlen = count;
+ msg.msg_control = 0;
+ msg.msg_controllen = 0;
+ msg.msg_flags = 0;
+#if defined(__linux__)
+ flags |= MSG_NOSIGNAL;
+#endif // defined(__linux__)
+ return error_wrapper(::sendmsg(s, &msg, flags));
+#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+}
+
+inline int sendto(socket_type s, const buf* bufs, size_t count, int flags,
+ const socket_addr_type* addr, socket_addr_len_type addrlen)
+{
+ set_error(0);
+#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+ // Send the data.
+ DWORD send_buf_count = static_cast<DWORD>(count);
+ DWORD bytes_transferred = 0;
+ int result = ::WSASendTo(s, const_cast<buf*>(bufs), send_buf_count,
+ &bytes_transferred, flags, addr, addrlen, 0, 0);
+ if (result != 0)
+ return -1;
+ return bytes_transferred;
+#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+ msghdr msg;
+#if defined(__MACH__) && defined(__APPLE__) \
+ && (MAC_OS_X_VERSION_MAX_ALLOWED < 1040)
+ msg.msg_name = reinterpret_cast<char*>(const_cast<socket_addr_type*>(addr));
+#else
+ msg.msg_name = const_cast<socket_addr_type*>(addr);
+#endif
+ msg.msg_namelen = addrlen;
+ msg.msg_iov = const_cast<buf*>(bufs);
+ msg.msg_iovlen = count;
+ msg.msg_control = 0;
+ msg.msg_controllen = 0;
+ msg.msg_flags = 0;
+#if defined(__linux__)
+ flags |= MSG_NOSIGNAL;
+#endif // defined(__linux__)
+ return error_wrapper(::sendmsg(s, &msg, flags));
+#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+}
+
+inline socket_type socket(int af, int type, int protocol)
+{
+ set_error(0);
+#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+ return error_wrapper(::WSASocket(af, type, protocol, 0, 0,
+ WSA_FLAG_OVERLAPPED));
+#elif defined(__MACH__) && defined(__APPLE__)
+ socket_type s = error_wrapper(::socket(af, type, protocol));
+ if (s == invalid_socket)
+ return s;
+
+ int optval = 1;
+ int result = error_wrapper(::setsockopt(s,
+ SOL_SOCKET, SO_NOSIGPIPE, &optval, sizeof(optval)));
+ if (result != 0)
+ {
+ ::close(s);
+ return invalid_socket;
+ }
+
+ return s;
+#else
+ return error_wrapper(::socket(af, type, protocol));
+#endif
+}
+
+inline int setsockopt(socket_type s, int level, int optname,
+ const void* optval, size_t optlen)
+{
+ set_error(0);
+#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+ return error_wrapper(::setsockopt(s, level, optname,
+ reinterpret_cast<const char*>(optval), static_cast<int>(optlen)));
+#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+ return error_wrapper(::setsockopt(s, level, optname, optval,
+ static_cast<socklen_t>(optlen)));
+#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+}
+
+inline int getsockopt(socket_type s, int level, int optname, void* optval,
+ size_t* optlen)
+{
+ set_error(0);
+#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));
+ *optlen = static_cast<size_t>(tmp_optlen);
+ return result;
+#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+ socklen_t tmp_optlen = static_cast<socklen_t>(*optlen);
+ int result = error_wrapper(::getsockopt(s, level, optname,
+ optval, &tmp_optlen));
+ *optlen = static_cast<size_t>(tmp_optlen);
+ return result;
+#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+}
+
+inline int getpeername(socket_type s, socket_addr_type* addr,
+ socket_addr_len_type* addrlen)
+{
+ set_error(0);
+ return error_wrapper(::getpeername(s, addr, addrlen));
+}
+
+inline int getsockname(socket_type s, socket_addr_type* addr,
+ socket_addr_len_type* addrlen)
+{
+ set_error(0);
+ return error_wrapper(::getsockname(s, addr, addrlen));
+}
+
+inline int ioctl(socket_type s, long cmd, ioctl_arg_type* arg)
+{
+ set_error(0);
+#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+ return error_wrapper(::ioctlsocket(s, cmd, arg));
+#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+ return error_wrapper(::ioctl(s, cmd, arg));
+#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+}
+
+inline int select(int nfds, fd_set* readfds, fd_set* writefds,
+ fd_set* exceptfds, timeval* timeout)
+{
+ set_error(0);
+#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+ if (!readfds && !writefds && !exceptfds && timeout)
+ {
+ DWORD milliseconds = timeout->tv_sec * 1000 + timeout->tv_usec / 1000;
+ if (milliseconds == 0)
+ milliseconds = 1; // Force context switch.
+ ::Sleep(milliseconds);
+ return 0;
+ }
+
+ // The select() call allows timeout values measured in microseconds, but the
+ // system clock (as wrapped by boost::posix_time::microsec_clock) typically
+ // has a resolution of 10 milliseconds. This can lead to a spinning select
+ // reactor, meaning increased CPU usage, when waiting for the earliest
+ // scheduled timeout if it's less than 10 milliseconds away. To avoid a tight
+ // spin we'll use a minimum timeout of 1 millisecond.
+ if (timeout && timeout->tv_sec == 0
+ && timeout->tv_usec > 0 && timeout->tv_usec < 1000)
+ timeout->tv_usec = 1000;
+#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+ return error_wrapper(::select(nfds, readfds, writefds, exceptfds, timeout));
+}
+
+inline int poll_read(socket_type s)
+{
+#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+ FD_SET fds;
+ FD_ZERO(&fds);
+ FD_SET(s, &fds);
+ set_error(0);
+ return error_wrapper(::select(s, &fds, 0, 0, 0));
+#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+ pollfd fds;
+ fds.fd = s;
+ fds.events = POLLIN;
+ fds.revents = 0;
+ set_error(0);
+ return error_wrapper(::poll(&fds, 1, -1));
+#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+}
+
+inline int poll_write(socket_type s)
+{
+#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+ FD_SET fds;
+ FD_ZERO(&fds);
+ FD_SET(s, &fds);
+ set_error(0);
+ return error_wrapper(::select(s, 0, &fds, 0, 0));
+#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+ pollfd fds;
+ fds.fd = s;
+ fds.events = POLLOUT;
+ fds.revents = 0;
+ set_error(0);
+ return error_wrapper(::poll(&fds, 1, -1));
+#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+}
+
+inline const char* inet_ntop(int af, const void* src, char* dest, size_t length,
+ unsigned long scope_id = 0)
+{
+ set_error(0);
+#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+ using namespace std; // For memcpy.
+
+ if (af != AF_INET && af != AF_INET6)
+ {
+ set_error(asio::error::address_family_not_supported);
+ return 0;
+ }
+
+ sockaddr_storage_type address;
+ DWORD address_length;
+ if (af == AF_INET)
+ {
+ address_length = sizeof(sockaddr_in4_type);
+ sockaddr_in4_type* ipv4_address =
+ reinterpret_cast<sockaddr_in4_type*>(&address);
+ ipv4_address->sin_family = AF_INET;
+ ipv4_address->sin_port = 0;
+ memcpy(&ipv4_address->sin_addr, src, sizeof(in4_addr_type));
+ }
+ else // AF_INET6
+ {
+ address_length = sizeof(sockaddr_in6_type);
+ sockaddr_in6_type* ipv6_address =
+ reinterpret_cast<sockaddr_in6_type*>(&address);
+ ipv6_address->sin6_family = AF_INET6;
+ ipv6_address->sin6_port = 0;
+ ipv6_address->sin6_flowinfo = 0;
+ ipv6_address->sin6_scope_id = scope_id;
+ memcpy(&ipv6_address->sin6_addr, src, sizeof(in6_addr_type));
+ }
+
+ DWORD string_length = static_cast<DWORD>(length);
+ int result = error_wrapper(::WSAAddressToStringA(
+ reinterpret_cast<sockaddr*>(&address),
+ address_length, 0, dest, &string_length));
+
+ // Windows may not set an error code on failure.
+ if (result == socket_error_retval && get_error() == 0)
+ set_error(asio::error::invalid_argument);
+
+ return result == socket_error_retval ? 0 : dest;
+#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+ const char* result = error_wrapper(::inet_ntop(af, src, dest, length));
+ if (result == 0 && get_error() == 0)
+ set_error(asio::error::invalid_argument);
+ if (result != 0 && af == AF_INET6 && scope_id != 0)
+ {
+ using namespace std; // For strcat and sprintf.
+ char if_name[IF_NAMESIZE + 1] = "%";
+ const in6_addr_type* ipv6_address = static_cast<const in6_addr_type*>(src);
+ bool is_link_local = IN6_IS_ADDR_LINKLOCAL(ipv6_address);
+ if (!is_link_local || if_indextoname(scope_id, if_name + 1) == 0)
+ sprintf(if_name + 1, "%lu", scope_id);
+ strcat(dest, if_name);
+ }
+ return result;
+#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+}
+
+inline int inet_pton(int af, const char* src, void* dest,
+ unsigned long* scope_id = 0)
+{
+ set_error(0);
+#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+ using namespace std; // For memcpy and strcmp.
+
+ if (af != AF_INET && af != AF_INET6)
+ {
+ set_error(asio::error::address_family_not_supported);
+ return -1;
+ }
+
+ sockaddr_storage_type address;
+ int address_length = sizeof(sockaddr_storage_type);
+ int result = error_wrapper(::WSAStringToAddressA(
+ const_cast<char*>(src), af, 0,
+ reinterpret_cast<sockaddr*>(&address),
+ &address_length));
+
+ if (af == AF_INET)
+ {
+ if (result != socket_error_retval)
+ {
+ sockaddr_in4_type* ipv4_address =
+ reinterpret_cast<sockaddr_in4_type*>(&address);
+ memcpy(dest, &ipv4_address->sin_addr, sizeof(in4_addr_type));
+ }
+ else if (strcmp(src, "255.255.255.255") == 0)
+ {
+ static_cast<in4_addr_type*>(dest)->s_addr = INADDR_NONE;
+ }
+ }
+ else // AF_INET6
+ {
+ if (result != socket_error_retval)
+ {
+ sockaddr_in6_type* ipv6_address =
+ reinterpret_cast<sockaddr_in6_type*>(&address);
+ memcpy(dest, &ipv6_address->sin6_addr, sizeof(in6_addr_type));
+ if (scope_id)
+ *scope_id = ipv6_address->sin6_scope_id;
+ }
+ }
+
+ // Windows may not set an error code on failure.
+ if (result == socket_error_retval && get_error() == 0)
+ set_error(asio::error::invalid_argument);
+
+ return result == socket_error_retval ? -1 : 1;
+#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+ int result = error_wrapper(::inet_pton(af, src, dest));
+ if (result <= 0 && get_error() == 0)
+ set_error(asio::error::invalid_argument);
+ if (result > 0 && af == AF_INET6 && scope_id)
+ {
+ using namespace std; // For strchr and atoi.
+ *scope_id = 0;
+ if (const char* if_name = strchr(src, '%'))
+ {
+ in6_addr_type* ipv6_address = static_cast<in6_addr_type*>(dest);
+ bool is_link_local = IN6_IS_ADDR_LINKLOCAL(ipv6_address);
+ if (is_link_local)
+ *scope_id = if_nametoindex(if_name + 1);
+ if (*scope_id == 0)
+ *scope_id = atoi(if_name + 1);
+ }
+ }
+ return result;
+#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+}
+
+inline int gethostname(char* name, int namelen)
+{
+ set_error(0);
+ return error_wrapper(::gethostname(name, namelen));
+}
+
+#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) \
+ || defined(__MACH__) && defined(__APPLE__)
+
+// The following functions are only needed for emulation of getaddrinfo and
+// getnameinfo.
+
+inline int translate_netdb_error(int error)
+{
+ switch (error)
+ {
+ case 0:
+ return asio::error::success;
+ case HOST_NOT_FOUND:
+ return asio::error::host_not_found;
+ case TRY_AGAIN:
+ return asio::error::host_not_found_try_again;
+ case NO_RECOVERY:
+ return asio::error::no_recovery;
+ case NO_DATA:
+ return asio::error::no_data;
+ default:
+ BOOST_ASSERT(false);
+ return get_error();
+ }
+}
+
+inline hostent* gethostbyaddr(const char* addr, int length, int af,
+ hostent* result, char* buffer, int buflength, int* error)
+{
+ set_error(0);
+#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+ (void)(buffer);
+ (void)(buflength);
+ hostent* retval = error_wrapper(::gethostbyaddr(addr, length, af));
+ *error = get_error();
+ if (!retval)
+ return 0;
+ *result = *retval;
+ return retval;
+#elif defined(__sun) || defined(__QNX__)
+ hostent* retval = error_wrapper(::gethostbyaddr_r(addr, length, af, result,
+ buffer, buflength, error));
+ *error = translate_netdb_error(*error);
+ return retval;
+#elif defined(__MACH__) && defined(__APPLE__)
+ (void)(buffer);
+ (void)(buflength);
+ hostent* retval = error_wrapper(::getipnodebyaddr(addr, length, af, error));
+ *error = translate_netdb_error(*error);
+ if (!retval)
+ return 0;
+ *result = *retval;
+ return retval;
+#else
+ hostent* retval = 0;
+ error_wrapper(::gethostbyaddr_r(addr, length, af, result, buffer,
+ buflength, &retval, error));
+ *error = translate_netdb_error(*error);
+ return retval;
+#endif
+}
+
+inline hostent* gethostbyname(const char* name, int af, struct hostent* result,
+ char* buffer, int buflength, int* error, int ai_flags = 0)
+{
+ set_error(0);
+#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+ (void)(buffer);
+ (void)(buflength);
+ (void)(ai_flags);
+ if (af != AF_INET)
+ {
+ *error = asio::error::address_family_not_supported;
+ return 0;
+ }
+ hostent* retval = error_wrapper(::gethostbyname(name));
+ *error = get_error();
+ if (!retval)
+ return 0;
+ *result = *retval;
+ return result;
+#elif defined(__sun) || defined(__QNX__)
+ (void)(ai_flags);
+ if (af != AF_INET)
+ {
+ *error = asio::error::address_family_not_supported;
+ return 0;
+ }
+ hostent* retval = error_wrapper(::gethostbyname_r(name, result, buffer,
+ buflength, error));
+ *error = translate_netdb_error(*error);
+ return retval;
+#elif defined(__MACH__) && defined(__APPLE__)
+ (void)(buffer);
+ (void)(buflength);
+ hostent* retval = error_wrapper(::getipnodebyname(
+ name, af, ai_flags, error));
+ *error = translate_netdb_error(*error);
+ if (!retval)
+ return 0;
+ *result = *retval;
+ return retval;
+#else
+ (void)(ai_flags);
+ if (af != AF_INET)
+ {
+ *error = asio::error::address_family_not_supported;
+ return 0;
+ }
+ hostent* retval = 0;
+ error_wrapper(::gethostbyname_r(name, result, buffer, buflength, &retval,
+ error));
+ *error = translate_netdb_error(*error);
+ return retval;
+#endif
+}
+
+inline void freehostent(hostent* h)
+{
+#if defined(__MACH__) && defined(__APPLE__)
+ if (h)
+ ::freehostent(h);
+#else
+ (void)(h);
+#endif
+}
+
+// Emulation of getaddrinfo based on implementation in:
+// Stevens, W. R., UNIX Network Programming Vol. 1, 2nd Ed., Prentice-Hall 1998.
+
+struct gai_search
+{
+ const char* host;
+ int family;
+};
+
+inline int gai_nsearch(const char* host,
+ const addrinfo_type* hints, gai_search (&search)[2])
+{
+ int search_count = 0;
+ if (host == 0 || host[0] == '\0')
+ {
+ if (hints->ai_flags & AI_PASSIVE)
+ {
+ // No host and AI_PASSIVE implies wildcard bind.
+ switch (hints->ai_family)
+ {
+ case AF_INET:
+ search[search_count].host = "0.0.0.0";
+ search[search_count].family = AF_INET;
+ ++search_count;
+ break;
+ case AF_INET6:
+ search[search_count].host = "0::0";
+ search[search_count].family = AF_INET6;
+ ++search_count;
+ break;
+ case AF_UNSPEC:
+ search[search_count].host = "0::0";
+ search[search_count].family = AF_INET6;
+ ++search_count;
+ search[search_count].host = "0.0.0.0";
+ search[search_count].family = AF_INET;
+ ++search_count;
+ break;
+ default:
+ break;
+ }
+ }
+ else
+ {
+ // No host and not AI_PASSIVE means connect to local host.
+ switch (hints->ai_family)
+ {
+ case AF_INET:
+ search[search_count].host = "localhost";
+ search[search_count].family = AF_INET;
+ ++search_count;
+ break;
+ case AF_INET6:
+ search[search_count].host = "localhost";
+ search[search_count].family = AF_INET6;
+ ++search_count;
+ break;
+ case AF_UNSPEC:
+ search[search_count].host = "localhost";
+ search[search_count].family = AF_INET6;
+ ++search_count;
+ search[search_count].host = "localhost";
+ search[search_count].family = AF_INET;
+ ++search_count;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ else
+ {
+ // Host is specified.
+ switch (hints->ai_family)
+ {
+ case AF_INET:
+ search[search_count].host = host;
+ search[search_count].family = AF_INET;
+ ++search_count;
+ break;
+ case AF_INET6:
+ search[search_count].host = host;
+ search[search_count].family = AF_INET6;
+ ++search_count;
+ break;
+ case AF_UNSPEC:
+ search[search_count].host = host;
+ search[search_count].family = AF_INET6;
+ ++search_count;
+ search[search_count].host = host;
+ search[search_count].family = AF_INET;
+ ++search_count;
+ break;
+ default:
+ break;
+ }
+ }
+ return search_count;
+}
+
+template <typename T>
+inline T* gai_alloc(std::size_t size = sizeof(T))
+{
+ using namespace std;
+ T* p = static_cast<T*>(::operator new(size, std::nothrow));
+ if (p)
+ memset(p, 0, size);
+ return p;
+}
+
+inline void gai_free(void* p)
+{
+ ::operator delete(p);
+}
+
+enum { gai_clone_flag = 1 << 30 };
+
+inline int gai_aistruct(addrinfo_type*** next, const addrinfo_type* hints,
+ const void* addr, int family)
+{
+ using namespace std;
+
+ addrinfo_type* ai = gai_alloc<addrinfo_type>();
+ if (ai == 0)
+ return EAI_MEMORY;
+
+ ai->ai_next = 0;
+ **next = ai;
+ *next = &ai->ai_next;
+
+ ai->ai_canonname = 0;
+ ai->ai_socktype = hints->ai_socktype;
+ if (ai->ai_socktype == 0)
+ ai->ai_flags |= gai_clone_flag;
+ ai->ai_protocol = hints->ai_protocol;
+ ai->ai_family = family;
+
+ switch (ai->ai_family)
+ {
+ case AF_INET:
+ {
+ sockaddr_in4_type* sinptr = gai_alloc<sockaddr_in4_type>();
+ if (sinptr == 0)
+ return EAI_MEMORY;
+ sinptr->sin_family = AF_INET;
+ memcpy(&sinptr->sin_addr, addr, sizeof(in4_addr_type));
+ ai->ai_addr = reinterpret_cast<sockaddr*>(sinptr);
+ ai->ai_addrlen = sizeof(sockaddr_in4_type);
+ break;
+ }
+ case AF_INET6:
+ {
+ sockaddr_in6_type* sin6ptr = gai_alloc<sockaddr_in6_type>();
+ if (sin6ptr == 0)
+ return EAI_MEMORY;
+ sin6ptr->sin6_family = AF_INET6;
+ memcpy(&sin6ptr->sin6_addr, addr, sizeof(in6_addr_type));
+ ai->ai_addr = reinterpret_cast<sockaddr*>(sin6ptr);
+ ai->ai_addrlen = sizeof(sockaddr_in6_type);
+ break;
+ }
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+inline addrinfo_type* gai_clone(addrinfo_type* ai)
+{
+ using namespace std;
+
+ addrinfo_type* new_ai = gai_alloc<addrinfo_type>();
+ if (new_ai == 0)
+ return new_ai;
+
+ new_ai->ai_next = ai->ai_next;
+ ai->ai_next = new_ai;
+
+ new_ai->ai_flags = 0;
+ new_ai->ai_family = ai->ai_family;
+ new_ai->ai_socktype = ai->ai_socktype;
+ new_ai->ai_protocol = ai->ai_protocol;
+ new_ai->ai_canonname = 0;
+ new_ai->ai_addrlen = ai->ai_addrlen;
+ new_ai->ai_addr = gai_alloc<sockaddr>(ai->ai_addrlen);
+ memcpy(new_ai->ai_addr, ai->ai_addr, ai->ai_addrlen);
+
+ return new_ai;
+}
+
+inline int gai_port(addrinfo_type* aihead, int port, int socktype)
+{
+ int num_found = 0;
+
+ for (addrinfo_type* ai = aihead; ai; ai = ai->ai_next)
+ {
+ if (ai->ai_flags & gai_clone_flag)
+ {
+ if (ai->ai_socktype != 0)
+ {
+ ai = gai_clone(ai);
+ if (ai == 0)
+ return -1;
+ // ai now points to newly cloned entry.
+ }
+ }
+ else if (ai->ai_socktype != socktype)
+ {
+ // Ignore if mismatch on socket type.
+ continue;
+ }
+
+ ai->ai_socktype = socktype;
+
+ switch (ai->ai_family)
+ {
+ case AF_INET:
+ {
+ sockaddr_in4_type* sinptr =
+ reinterpret_cast<sockaddr_in4_type*>(ai->ai_addr);
+ sinptr->sin_port = port;
+ ++num_found;
+ break;
+ }
+ case AF_INET6:
+ {
+ sockaddr_in6_type* sin6ptr =
+ reinterpret_cast<sockaddr_in6_type*>(ai->ai_addr);
+ sin6ptr->sin6_port = port;
+ ++num_found;
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ return num_found;
+}
+
+inline int gai_serv(addrinfo_type* aihead,
+ const addrinfo_type* hints, const char* serv)
+{
+ using namespace std;
+
+ int num_found = 0;
+
+ if (
+#if defined(AI_NUMERICSERV)
+ (hints->ai_flags & AI_NUMERICSERV) ||
+#endif
+ isdigit(serv[0]))
+ {
+ int port = htons(atoi(serv));
+ if (hints->ai_socktype)
+ {
+ // Caller specifies socket type.
+ int rc = gai_port(aihead, port, hints->ai_socktype);
+ if (rc < 0)
+ return EAI_MEMORY;
+ num_found += rc;
+ }
+ else
+ {
+ // Caller does not specify socket type.
+ int rc = gai_port(aihead, port, SOCK_STREAM);
+ if (rc < 0)
+ return EAI_MEMORY;
+ num_found += rc;
+ rc = gai_port(aihead, port, SOCK_DGRAM);
+ if (rc < 0)
+ return EAI_MEMORY;
+ num_found += rc;
+ }
+ }
+ else
+ {
+ // Try service name with TCP first, then UDP.
+ if (hints->ai_socktype == 0 || hints->ai_socktype == SOCK_STREAM)
+ {
+ servent* sptr = getservbyname(serv, "tcp");
+ if (sptr != 0)
+ {
+ int rc = gai_port(aihead, sptr->s_port, SOCK_STREAM);
+ if (rc < 0)
+ return EAI_MEMORY;
+ num_found += rc;
+ }
+ }
+ if (hints->ai_socktype == 0 || hints->ai_socktype == SOCK_DGRAM)
+ {
+ servent* sptr = getservbyname(serv, "udp");
+ if (sptr != 0)
+ {
+ int rc = gai_port(aihead, sptr->s_port, SOCK_DGRAM);
+ if (rc < 0)
+ return EAI_MEMORY;
+ num_found += rc;
+ }
+ }
+ }
+
+ if (num_found == 0)
+ {
+ if (hints->ai_socktype == 0)
+ {
+ // All calls to getservbyname() failed.
+ return EAI_NONAME;
+ }
+ else
+ {
+ // Service not supported for socket type.
+ return EAI_SERVICE;
+ }
+ }
+
+ return 0;
+}
+
+inline int gai_echeck(const char* host, const char* service,
+ int flags, int family, int socktype, int protocol)
+{
+ (void)(flags);
+ (void)(protocol);
+
+ // Host or service must be specified.
+ if (host == 0 || host[0] == '\0')
+ if (service == 0 || service[0] == '\0')
+ return EAI_NONAME;
+
+ // Check combination of family and socket type.
+ switch (family)
+ {
+ case AF_UNSPEC:
+ break;
+ case AF_INET:
+ case AF_INET6:
+ if (socktype != 0 && socktype != SOCK_STREAM && socktype != SOCK_DGRAM)
+ return EAI_SOCKTYPE;
+ break;
+ default:
+ return EAI_FAMILY;
+ }
+
+ return 0;
+}
+
+inline void freeaddrinfo_emulation(addrinfo_type* aihead)
+{
+ addrinfo_type* ai = aihead;
+ while (ai)
+ {
+ gai_free(ai->ai_addr);
+ gai_free(ai->ai_canonname);
+ addrinfo_type* ainext = ai->ai_next;
+ gai_free(ai);
+ ai = ainext;
+ }
+}
+
+inline int getaddrinfo_emulation(const char* host, const char* service,
+ const addrinfo_type* hintsp, addrinfo_type** result)
+{
+ // Set up linked list of addrinfo structures.
+ addrinfo_type* aihead = 0;
+ addrinfo_type** ainext = &aihead;
+ char* canon = 0;
+
+ // Supply default hints if not specified by caller.
+ addrinfo_type hints = addrinfo_type();
+ hints.ai_family = AF_UNSPEC;
+ if (hintsp)
+ hints = *hintsp;
+
+ // If the resolution is not specifically for AF_INET6, remove the AI_V4MAPPED
+ // and AI_ALL flags.
+#if defined(AI_V4MAPPED)
+ if (hints.ai_family != AF_INET6)
+ hints.ai_flags &= ~AI_V4MAPPED;
+#endif
+#if defined(AI_ALL)
+ if (hints.ai_family != AF_INET6)
+ hints.ai_flags &= ~AI_ALL;
+#endif
+
+ // Basic error checking.
+ int rc = gai_echeck(host, service, hints.ai_flags, hints.ai_family,
+ hints.ai_socktype, hints.ai_protocol);
+ if (rc != 0)
+ {
+ freeaddrinfo_emulation(aihead);
+ return rc;
+ }
+
+ gai_search search[2];
+ int search_count = gai_nsearch(host, &hints, search);
+ for (gai_search* sptr = search; sptr < search + search_count; ++sptr)
+ {
+ // Check for IPv4 dotted decimal string.
+ in4_addr_type inaddr;
+ if (socket_ops::inet_pton(AF_INET, sptr->host, &inaddr) == 1)
+ {
+ if (hints.ai_family != AF_UNSPEC && hints.ai_family != AF_INET)
+ {
+ freeaddrinfo_emulation(aihead);
+ gai_free(canon);
+ return EAI_FAMILY;
+ }
+ if (sptr->family == AF_INET)
+ {
+ rc = gai_aistruct(&ainext, &hints, &inaddr, AF_INET);
+ if (rc != 0)
+ {
+ freeaddrinfo_emulation(aihead);
+ gai_free(canon);
+ return rc;
+ }
+ }
+ continue;
+ }
+
+ // Check for IPv6 hex string.
+ in6_addr_type in6addr;
+ if (socket_ops::inet_pton(AF_INET6, sptr->host, &in6addr) == 1)
+ {
+ if (hints.ai_family != AF_UNSPEC && hints.ai_family != AF_INET6)
+ {
+ freeaddrinfo_emulation(aihead);
+ gai_free(canon);
+ return EAI_FAMILY;
+ }
+ if (sptr->family == AF_INET6)
+ {
+ rc = gai_aistruct(&ainext, &hints, &in6addr, AF_INET6);
+ if (rc != 0)
+ {
+ freeaddrinfo_emulation(aihead);
+ gai_free(canon);
+ return rc;
+ }
+ }
+ continue;
+ }
+
+ // Look up hostname.
+ hostent hent;
+ char hbuf[8192] = "";
+ int herr = 0;
+ hostent* hptr = socket_ops::gethostbyname(sptr->host,
+ sptr->family, &hent, hbuf, sizeof(hbuf), &herr, hints.ai_flags);
+ if (hptr == 0)
+ {
+ if (search_count == 2)
+ {
+ // Failure is OK if there are multiple searches.
+ continue;
+ }
+ freeaddrinfo_emulation(aihead);
+ gai_free(canon);
+ switch (herr)
+ {
+ case HOST_NOT_FOUND:
+ return EAI_NONAME;
+ case TRY_AGAIN:
+ return EAI_AGAIN;
+ case NO_RECOVERY:
+ return EAI_FAIL;
+ case NO_DATA:
+ default:
+ return EAI_NONAME;
+ }
+ }
+
+ // Check for address family mismatch if one was specified.
+ if (hints.ai_family != AF_UNSPEC && hints.ai_family != hptr->h_addrtype)
+ {
+ freeaddrinfo_emulation(aihead);
+ gai_free(canon);
+ socket_ops::freehostent(hptr);
+ return EAI_FAMILY;
+ }
+
+ // Save canonical name first time.
+ if (host != 0 && host[0] != '\0' && hptr->h_name && hptr->h_name[0]
+ && (hints.ai_flags & AI_CANONNAME) && canon == 0)
+ {
+ canon = gai_alloc<char>(strlen(hptr->h_name) + 1);
+ if (canon == 0)
+ {
+ freeaddrinfo_emulation(aihead);
+ socket_ops::freehostent(hptr);
+ return EAI_MEMORY;
+ }
+ strcpy(canon, hptr->h_name);
+ }
+
+ // Create an addrinfo structure for each returned address.
+ for (char** ap = hptr->h_addr_list; *ap; ++ap)
+ {
+ rc = gai_aistruct(&ainext, &hints, *ap, hptr->h_addrtype);
+ if (rc != 0)
+ {
+ freeaddrinfo_emulation(aihead);
+ gai_free(canon);
+ socket_ops::freehostent(hptr);
+ return EAI_FAMILY;
+ }
+ }
+
+ socket_ops::freehostent(hptr);
+ }
+
+ // Check if we found anything.
+ if (aihead == 0)
+ {
+ gai_free(canon);
+ return EAI_NONAME;
+ }
+
+ // Return canonical name in first entry.
+ if (host != 0 && host[0] != '\0' && (hints.ai_flags & AI_CANONNAME))
+ {
+ if (canon)
+ {
+ aihead->ai_canonname = canon;
+ canon = 0;
+ }
+ else
+ {
+ aihead->ai_canonname = gai_alloc<char>(strlen(search[0].host) + 1);
+ if (aihead->ai_canonname == 0)
+ {
+ freeaddrinfo_emulation(aihead);
+ return EAI_MEMORY;
+ }
+ strcpy(aihead->ai_canonname, search[0].host);
+ }
+ }
+ gai_free(canon);
+
+ // Process the service name.
+ if (service != 0 && service[0] != '\0')
+ {
+ rc = gai_serv(aihead, &hints, service);
+ if (rc != 0)
+ {
+ freeaddrinfo_emulation(aihead);
+ return rc;
+ }
+ }
+
+ // Return result to caller.
+ *result = aihead;
+ return 0;
+}
+
+inline int getnameinfo_emulation(const socket_addr_type* sa,
+ socket_addr_len_type salen, char* host, std::size_t hostlen,
+ char* serv, std::size_t servlen, int flags)
+{
+ using namespace std;
+
+ const char* addr;
+ size_t addr_len;
+ unsigned short port;
+ switch (sa->sa_family)
+ {
+ case AF_INET:
+ if (salen != sizeof(sockaddr_in4_type))
+ {
+ set_error(asio::error::invalid_argument);
+ return 1;
+ }
+ addr = reinterpret_cast<const char*>(
+ &reinterpret_cast<const sockaddr_in4_type*>(sa)->sin_addr);
+ addr_len = sizeof(in4_addr_type);
+ port = reinterpret_cast<const sockaddr_in4_type*>(sa)->sin_port;
+ break;
+ case AF_INET6:
+ if (salen != sizeof(sockaddr_in6_type))
+ {
+ set_error(asio::error::invalid_argument);
+ return 1;
+ }
+ addr = reinterpret_cast<const char*>(
+ &reinterpret_cast<const sockaddr_in6_type*>(sa)->sin6_addr);
+ addr_len = sizeof(in6_addr_type);
+ port = reinterpret_cast<const sockaddr_in6_type*>(sa)->sin6_port;
+ break;
+ default:
+ set_error(asio::error::address_family_not_supported);
+ return 1;
+ }
+
+ if (host && hostlen > 0)
+ {
+ if (flags & NI_NUMERICHOST)
+ {
+ if (socket_ops::inet_ntop(sa->sa_family, addr, host, hostlen) == 0)
+ {
+ return 1;
+ }
+ }
+ else
+ {
+ hostent hent;
+ char hbuf[8192] = "";
+ int herr = 0;
+ hostent* hptr = socket_ops::gethostbyaddr(addr,
+ static_cast<int>(addr_len), sa->sa_family,
+ &hent, hbuf, sizeof(hbuf), &herr);
+ if (hptr && hptr->h_name && hptr->h_name[0] != '\0')
+ {
+ if (flags & NI_NOFQDN)
+ {
+ char* dot = strchr(hptr->h_name, '.');
+ if (dot)
+ {
+ *dot = 0;
+ }
+ }
+ *host = '\0';
+ strncat(host, hptr->h_name, hostlen);
+ socket_ops::freehostent(hptr);
+ }
+ else
+ {
+ socket_ops::freehostent(hptr);
+ if (flags & NI_NAMEREQD)
+ {
+ set_error(asio::error::host_not_found);
+ return 1;
+ }
+ if (socket_ops::inet_ntop(sa->sa_family, addr, host, hostlen) == 0)
+ {
+ return 1;
+ }
+ }
+ }
+ }
+
+ if (serv && servlen > 0)
+ {
+ if (flags & NI_NUMERICSERV)
+ {
+ if (servlen < 6)
+ {
+ set_error(asio::error::no_buffer_space);
+ return 1;
+ }
+ sprintf(serv, "%u", ntohs(port));
+ }
+ else
+ {
+#if defined(BOOST_HAS_THREADS) && defined(BOOST_HAS_PTHREADS)
+ static ::pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
+ ::pthread_mutex_lock(&mutex);
+#endif // defined(BOOST_HAS_THREADS) && defined(BOOST_HAS_PTHREADS)
+ servent* sptr = ::getservbyport(port, (flags & NI_DGRAM) ? "udp" : 0);
+ if (sptr && sptr->s_name && sptr->s_name[0] != '\0')
+ {
+ *serv = '\0';
+ strncat(serv, sptr->s_name, servlen);
+ }
+ else
+ {
+ if (servlen < 6)
+ {
+ set_error(asio::error::no_buffer_space);
+ return 1;
+ }
+ sprintf(serv, "%u", ntohs(port));
+ }
+#if defined(BOOST_HAS_THREADS) && defined(BOOST_HAS_PTHREADS)
+ ::pthread_mutex_unlock(&mutex);
+#endif // defined(BOOST_HAS_THREADS) && defined(BOOST_HAS_PTHREADS)
+ }
+ }
+
+ set_error(0);
+ return 0;
+}
+
+#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+ // || defined(__MACH__) && defined(__APPLE__)
+
+inline int translate_addrinfo_error(int error)
+{
+ switch (error)
+ {
+ case 0:
+ return asio::error::success;
+ case EAI_AGAIN:
+ return asio::error::host_not_found_try_again;
+ case EAI_BADFLAGS:
+ return asio::error::invalid_argument;
+ case EAI_FAIL:
+ return asio::error::no_recovery;
+ case EAI_FAMILY:
+ return asio::error::address_family_not_supported;
+ case EAI_MEMORY:
+ return asio::error::no_memory;
+ case EAI_NONAME:
+ return asio::error::host_not_found;
+ case EAI_SERVICE:
+ return asio::error::service_not_found;
+ case EAI_SOCKTYPE:
+ return asio::error::socket_type_not_supported;
+ default: // Possibly the non-portable EAI_SYSTEM.
+ return get_error();
+ }
+}
+
+inline int getaddrinfo(const char* host, const char* service,
+ const addrinfo_type* hints, addrinfo_type** result)
+{
+ set_error(0);
+#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+# if defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0501)
+ // Building for Windows XP, Windows Server 2003, or later.
+ int error = ::getaddrinfo(host, service, hints, result);
+ return translate_addrinfo_error(error);
+# else
+ // Building for Windows 2000 or earlier.
+ typedef int (WSAAPI *gai_t)(const char*,
+ const char*, const addrinfo_type*, addrinfo_type**);
+ if (HMODULE winsock_module = ::GetModuleHandleA("ws2_32"))
+ {
+ if (gai_t gai = (gai_t)::GetProcAddress(winsock_module, "getaddrinfo"))
+ {
+ int error = gai(host, service, hints, result);
+ return translate_addrinfo_error(error);
+ }
+ }
+ int error = getaddrinfo_emulation(host, service, hints, result);
+ return translate_addrinfo_error(error);
+# endif
+#elif defined(__MACH__) && defined(__APPLE__)
+ int error = getaddrinfo_emulation(host, service, hints, result);
+ return translate_addrinfo_error(error);
+#else
+ int error = ::getaddrinfo(host, service, hints, result);
+ return translate_addrinfo_error(error);
+#endif
+}
+
+inline void freeaddrinfo(addrinfo_type* ai)
+{
+#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+# if defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0501)
+ // Building for Windows XP, Windows Server 2003, or later.
+ ::freeaddrinfo(ai);
+# else
+ // Building for Windows 2000 or earlier.
+ typedef int (WSAAPI *fai_t)(addrinfo_type*);
+ if (HMODULE winsock_module = ::GetModuleHandleA("ws2_32"))
+ {
+ if (fai_t fai = (fai_t)::GetProcAddress(winsock_module, "freeaddrinfo"))
+ {
+ fai(ai);
+ return;
+ }
+ }
+ freeaddrinfo_emulation(ai);
+# endif
+#elif defined(__MACH__) && defined(__APPLE__)
+ freeaddrinfo_emulation(ai);
+#else
+ ::freeaddrinfo(ai);
+#endif
+}
+
+inline int getnameinfo(const socket_addr_type* addr,
+ socket_addr_len_type addrlen, char* host, std::size_t hostlen,
+ char* serv, std::size_t servlen, int flags)
+{
+#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+# if defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0501)
+ // Building for Windows XP, Windows Server 2003, or later.
+ set_error(0);
+ int error = ::getnameinfo(addr, addrlen, host, hostlen, serv, servlen, flags);
+ return translate_addrinfo_error(error);
+# else
+ // Building for Windows 2000 or earlier.
+ typedef int (WSAAPI *gni_t)(const socket_addr_type*,
+ socket_addr_len_type, char*, std::size_t, char*, std::size_t, int);
+ if (HMODULE winsock_module = ::GetModuleHandleA("ws2_32"))
+ {
+ if (gni_t gni = (gni_t)::GetProcAddress(winsock_module, "getnameinfo"))
+ {
+ set_error(0);
+ int error = gni(addr, addrlen, host, hostlen, serv, servlen, flags);
+ return translate_addrinfo_error(error);
+ }
+ }
+ set_error(0);
+ int error = getnameinfo_emulation(addr, addrlen,
+ host, hostlen, serv, servlen, flags);
+ return translate_addrinfo_error(error);
+# endif
+#elif defined(__MACH__) && defined(__APPLE__)
+ using namespace std; // For memcpy.
+ sockaddr_storage_type tmp_addr;
+ memcpy(&tmp_addr, addr, addrlen);
+ tmp_addr.ss_len = addrlen;
+ addr = reinterpret_cast<socket_addr_type*>(&tmp_addr);
+ set_error(0);
+ int error = getnameinfo_emulation(addr, addrlen,
+ host, hostlen, serv, servlen, flags);
+ return translate_addrinfo_error(error);
+#else
+ set_error(0);
+ int error = ::getnameinfo(addr, addrlen, host, hostlen, serv, servlen, flags);
+ return translate_addrinfo_error(error);
+#endif
+}
+
+inline u_long_type network_to_host_long(u_long_type value)
+{
+ return ntohl(value);
+}
+
+inline u_long_type host_to_network_long(u_long_type value)
+{
+ return htonl(value);
+}
+
+inline u_short_type network_to_host_short(u_short_type value)
+{
+ return ntohs(value);
+}
+
+inline u_short_type host_to_network_short(u_short_type value)
+{
+ return htons(value);
+}
+
+} // namespace socket_ops
+} // namespace detail
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_DETAIL_SOCKET_OPS_HPP
diff --git a/library/include/libtorrent/asio/detail/socket_option.hpp b/library/include/libtorrent/asio/detail/socket_option.hpp
new file mode 100644
index 000000000..c8e680e14
--- /dev/null
+++ b/library/include/libtorrent/asio/detail/socket_option.hpp
@@ -0,0 +1,323 @@
+//
+// socket_option.hpp
+// ~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_SOCKET_OPTION_HPP
+#define ASIO_DETAIL_SOCKET_OPTION_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/detail/push_options.hpp"
+#include <cstddef>
+#include <boost/config.hpp>
+#include "asio/detail/pop_options.hpp"
+
+#include "asio/detail/socket_types.hpp"
+
+namespace asio {
+namespace detail {
+namespace socket_option {
+
+// Helper template for implementing boolean-based options.
+template <int Level, int Name>
+class boolean
+{
+public:
+ // Default constructor.
+ boolean()
+ : value_(0)
+ {
+ }
+
+ // Construct with a specific option value.
+ boolean(bool value)
+ : value_(value ? 1 : 0)
+ {
+ }
+
+ // Set the value of the boolean.
+ void set(bool value)
+ {
+ value_ = value ? 1 : 0;
+ }
+
+ // Get the current value of the boolean.
+ bool get() const
+ {
+ return value_;
+ }
+
+ // Get the level of the socket option.
+ template <typename Protocol>
+ int level(const Protocol&) const
+ {
+ return Level;
+ }
+
+ // Get the name of the socket option.
+ template <typename Protocol>
+ int name(const Protocol&) const
+ {
+ return Name;
+ }
+
+ // Get the address of the boolean data.
+ template <typename Protocol>
+ int* data(const Protocol&)
+ {
+ return &value_;
+ }
+
+ // Get the address of the boolean data.
+ template <typename Protocol>
+ const int* data(const Protocol&) const
+ {
+ return &value_;
+ }
+
+ // Get the size of the boolean data.
+ template <typename Protocol>
+ std::size_t size(const Protocol&) const
+ {
+ return sizeof(value_);
+ }
+
+private:
+ int value_;
+};
+
+// Helper template for implementing integer options.
+template <int Level, int Name>
+class integer
+{
+public:
+ // Default constructor.
+ integer()
+ : value_(0)
+ {
+ }
+
+ // Construct with a specific option value.
+ integer(int value)
+ : value_(value)
+ {
+ }
+
+ // Set the value of the int option.
+ void set(int value)
+ {
+ value_ = value;
+ }
+
+ // Get the current value of the int option.
+ int get() const
+ {
+ return value_;
+ }
+
+ // Get the level of the socket option.
+ template <typename Protocol>
+ int level(const Protocol&) const
+ {
+ return Level;
+ }
+
+ // Get the name of the socket option.
+ template <typename Protocol>
+ int name(const Protocol&) const
+ {
+ return Name;
+ }
+
+ // Get the address of the int data.
+ template <typename Protocol>
+ int* data(const Protocol&)
+ {
+ return &value_;
+ }
+
+ // Get the address of the int data.
+ template <typename Protocol>
+ const int* data(const Protocol&) const
+ {
+ return &value_;
+ }
+
+ // Get the size of the int data.
+ template <typename Protocol>
+ std::size_t size(const Protocol&) const
+ {
+ return sizeof(value_);
+ }
+
+private:
+ int value_;
+};
+
+// Helper template for implementing unsigned integer options.
+template <int Level, int Name>
+class unsigned_integer
+{
+public:
+ // Default constructor.
+ unsigned_integer()
+ : value_(0)
+ {
+ }
+
+ // Construct with a specific option value.
+ unsigned_integer(unsigned int value)
+ : value_(value)
+ {
+ }
+
+ // Set the value of the int option.
+ void set(unsigned int value)
+ {
+ value_ = value;
+ }
+
+ // Get the current value of the int option.
+ unsigned int get() const
+ {
+ return value_;
+ }
+
+ // Get the level of the socket option.
+ template <typename Protocol>
+ int level(const Protocol&) const
+ {
+ return Level;
+ }
+
+ // Get the name of the socket option.
+ template <typename Protocol>
+ int name(const Protocol&) const
+ {
+ return Name;
+ }
+
+ // Get the address of the int data.
+ template <typename Protocol>
+ unsigned int* data(const Protocol&)
+ {
+ return &value_;
+ }
+
+ // Get the address of the int data.
+ template <typename Protocol>
+ const unsigned int* data(const Protocol&) const
+ {
+ return &value_;
+ }
+
+ // Get the size of the int data.
+ template <typename Protocol>
+ std::size_t size(const Protocol&) const
+ {
+ return sizeof(value_);
+ }
+
+private:
+ unsigned int value_;
+};
+
+// Helper template for implementing linger options.
+template <int Level, int Name>
+class linger
+{
+public:
+ // Default constructor.
+ linger()
+ {
+ value_.l_onoff = 0;
+ value_.l_linger = 0;
+ }
+
+ // Construct with specific option values.
+ linger(bool value, unsigned short timeout)
+ {
+ value_.l_onoff = value ? 1 : 0;
+ value_.l_linger = timeout;
+ }
+
+ // Set the value for whether linger is enabled.
+ void enabled(bool value)
+ {
+ value_.l_onoff = value ? 1 : 0;
+ }
+
+ // Get the value for whether linger is enabled.
+ bool enabled() const
+ {
+ return value_.l_onoff != 0;
+ }
+
+ // Set the value for the linger timeout.
+ void timeout(unsigned short value)
+ {
+ value_.l_linger = value;
+ }
+
+ // Get the value for the linger timeout.
+ unsigned short timeout() const
+ {
+ return value_.l_linger;
+ }
+
+ // Get the level of the socket option.
+ template <typename Protocol>
+ int level(const Protocol&) const
+ {
+ return Level;
+ }
+
+ // Get the name of the socket option.
+ template <typename Protocol>
+ int name(const Protocol&) const
+ {
+ return Name;
+ }
+
+ // Get the address of the linger data.
+ template <typename Protocol>
+ ::linger* data(const Protocol&)
+ {
+ return &value_;
+ }
+
+ // Get the address of the linger data.
+ template <typename Protocol>
+ const ::linger* data(const Protocol&) const
+ {
+ return &value_;
+ }
+
+ // Get the size of the linger data.
+ template <typename Protocol>
+ std::size_t size(const Protocol&) const
+ {
+ return sizeof(value_);
+ }
+
+private:
+ ::linger value_;
+};
+
+} // namespace socket_option
+} // namespace detail
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_DETAIL_SOCKET_OPTION_HPP
diff --git a/library/include/libtorrent/asio/detail/socket_select_interrupter.hpp b/library/include/libtorrent/asio/detail/socket_select_interrupter.hpp
new file mode 100644
index 000000000..e833213de
--- /dev/null
+++ b/library/include/libtorrent/asio/detail/socket_select_interrupter.hpp
@@ -0,0 +1,177 @@
+//
+// socket_select_interrupter.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_SOCKET_SELECT_INTERRUPTER_HPP
+#define ASIO_DETAIL_SOCKET_SELECT_INTERRUPTER_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/detail/push_options.hpp"
+#include <boost/throw_exception.hpp>
+#include "asio/detail/pop_options.hpp"
+
+#include "asio/error.hpp"
+#include "asio/detail/socket_holder.hpp"
+#include "asio/detail/socket_ops.hpp"
+#include "asio/detail/socket_types.hpp"
+
+namespace asio {
+namespace detail {
+
+class socket_select_interrupter
+{
+public:
+ // Constructor.
+ socket_select_interrupter()
+ {
+ socket_holder acceptor(socket_ops::socket(AF_INET, SOCK_STREAM,
+ IPPROTO_TCP));
+ if (acceptor.get() == invalid_socket)
+ {
+ asio::error e(socket_ops::get_error());
+ boost::throw_exception(e);
+ }
+
+ int opt = 1;
+ socket_ops::setsockopt(acceptor.get(),
+ SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
+
+ sockaddr_in4_type addr;
+ socket_addr_len_type addr_len = sizeof(addr);
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = inet_addr("127.0.0.1");
+ addr.sin_port = 0;
+ if (socket_ops::bind(acceptor.get(), (const socket_addr_type*)&addr,
+ addr_len) == socket_error_retval)
+ {
+ asio::error e(socket_ops::get_error());
+ boost::throw_exception(e);
+ }
+
+ if (getsockname(acceptor.get(), (socket_addr_type*)&addr, &addr_len)
+ == socket_error_retval)
+ {
+ asio::error e(socket_ops::get_error());
+ boost::throw_exception(e);
+ }
+
+ if (socket_ops::listen(acceptor.get(), SOMAXCONN) == socket_error_retval)
+ {
+ asio::error e(socket_ops::get_error());
+ boost::throw_exception(e);
+ }
+
+ socket_holder client(socket_ops::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP));
+ if (client.get() == invalid_socket)
+ {
+ asio::error e(socket_ops::get_error());
+ boost::throw_exception(e);
+ }
+
+ if (socket_ops::connect(client.get(), (const socket_addr_type*)&addr,
+ addr_len) == socket_error_retval)
+ {
+ asio::error e(socket_ops::get_error());
+ boost::throw_exception(e);
+ }
+
+ socket_holder server(socket_ops::accept(acceptor.get(), 0, 0));
+ if (server.get() == invalid_socket)
+ {
+ asio::error e(socket_ops::get_error());
+ boost::throw_exception(e);
+ }
+
+ ioctl_arg_type non_blocking = 1;
+ if (socket_ops::ioctl(client.get(), FIONBIO, &non_blocking))
+ {
+ asio::error e(socket_ops::get_error());
+ boost::throw_exception(e);
+ }
+
+ opt = 1;
+ socket_ops::setsockopt(client.get(),
+ IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(opt));
+
+ non_blocking = 1;
+ if (socket_ops::ioctl(server.get(), FIONBIO, &non_blocking))
+ {
+ asio::error e(socket_ops::get_error());
+ boost::throw_exception(e);
+ }
+
+ opt = 1;
+ socket_ops::setsockopt(server.get(),
+ IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(opt));
+
+ read_descriptor_ = server.release();
+ write_descriptor_ = client.release();
+ }
+
+ // Destructor.
+ ~socket_select_interrupter()
+ {
+ if (read_descriptor_ != invalid_socket)
+ socket_ops::close(read_descriptor_);
+ if (write_descriptor_ != invalid_socket)
+ socket_ops::close(write_descriptor_);
+ }
+
+ // Interrupt the select call.
+ void interrupt()
+ {
+ char byte = 0;
+ socket_ops::buf b;
+ socket_ops::init_buf(b, &byte, 1);
+ socket_ops::send(write_descriptor_, &b, 1, 0);
+ }
+
+ // Reset the select interrupt. Returns true if the call was interrupted.
+ bool reset()
+ {
+ char data[1024];
+ socket_ops::buf b;
+ socket_ops::init_buf(b, data, sizeof(data));
+ int bytes_read = socket_ops::recv(read_descriptor_, &b, 1, 0);
+ bool was_interrupted = (bytes_read > 0);
+ while (bytes_read == sizeof(data))
+ bytes_read = socket_ops::recv(read_descriptor_, &b, 1, 0);
+ return was_interrupted;
+ }
+
+ // Get the read descriptor to be passed to select.
+ socket_type read_descriptor() const
+ {
+ return read_descriptor_;
+ }
+
+private:
+ // The read end of a connection used to interrupt the select call. This file
+ // descriptor is passed to select such that when it is time to stop, a single
+ // byte will be written on the other end of the connection and this
+ // descriptor will become readable.
+ socket_type read_descriptor_;
+
+ // The write end of a connection used to interrupt the select call. A single
+ // byte may be written to this to wake up the select which is waiting for the
+ // other end to become readable.
+ socket_type write_descriptor_;
+};
+
+} // namespace detail
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_DETAIL_SOCKET_SELECT_INTERRUPTER_HPP
diff --git a/library/include/libtorrent/asio/detail/socket_types.hpp b/library/include/libtorrent/asio/detail/socket_types.hpp
new file mode 100644
index 000000000..631edbe1c
--- /dev/null
+++ b/library/include/libtorrent/asio/detail/socket_types.hpp
@@ -0,0 +1,184 @@
+//
+// socket_types.hpp
+// ~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_SOCKET_TYPES_HPP
+#define ASIO_DETAIL_SOCKET_TYPES_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/detail/push_options.hpp"
+#include <boost/config.hpp>
+#include "asio/detail/pop_options.hpp"
+
+#include "asio/detail/push_options.hpp"
+#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+# if defined(_WINSOCKAPI_) && !defined(_WINSOCK2API_)
+# error WinSock.h has already been included
+# endif // defined(_WINSOCKAPI_) && !defined(_WINSOCK2API_)
+# if !defined(_WIN32_WINNT) && !defined(_WIN32_WINDOWS)
+# if defined(_MSC_VER) || defined(__BORLANDC__)
+# pragma message("Please define _WIN32_WINNT or _WIN32_WINDOWS appropriately")
+# pragma message("Assuming _WIN32_WINNT=0x0500 (i.e. Windows 2000 target)")
+# else // defined(_MSC_VER) || defined(__BORLANDC__)
+# warning Please define _WIN32_WINNT or _WIN32_WINDOWS appropriately
+# warning Assuming _WIN32_WINNT=0x0500 (i.e. Windows 2000 target)
+# endif // defined(_MSC_VER) || defined(__BORLANDC__)
+# define _WIN32_WINNT 0x0500
+# endif // !defined(_WIN32_WINNT) && !defined(_WIN32_WINDOWS)
+# if defined(_MSC_VER)
+# if defined(_WIN32) && !defined(WIN32)
+# if !defined(_WINSOCK2API_)
+# define WIN32 // Needed for correct types in winsock2.h
+# else // !defined(_WINSOCK2API_)
+# error Please define the macro WIN32 in your compiler options
+# endif // !defined(_WINSOCK2API_)
+# endif // defined(_WIN32) && !defined(WIN32)
+# endif // defined(_MSC_VER)
+# if defined(__BORLANDC__)
+# include <stdlib.h> // Needed for __errno
+# if defined(__WIN32__) && !defined(WIN32)
+# if !defined(_WINSOCK2API_)
+# define WIN32 // Needed for correct types in winsock2.h
+# else // !defined(_WINSOCK2API_)
+# error Please define the macro WIN32 in your compiler options
+# endif // !defined(_WINSOCK2API_)
+# endif // defined(__WIN32__) && !defined(WIN32)
+# if !defined(_WSPIAPI_H_)
+# define _WSPIAPI_H_
+# define ASIO_WSPIAPI_H_DEFINED
+# endif // !defined(_WSPIAPI_H_)
+# endif // defined(__BORLANDC__)
+# if !defined(ASIO_NO_WIN32_LEAN_AND_MEAN)
+# if !defined(WIN32_LEAN_AND_MEAN)
+# define WIN32_LEAN_AND_MEAN
+# endif // !defined(WIN32_LEAN_AND_MEAN)
+# endif // !defined(ASIO_NO_WIN32_LEAN_AND_MEAN)
+# if defined(__CYGWIN__)
+# if !defined(__USE_W32_SOCKETS)
+# error You must add -D__USE_W32_SOCKETS to your compiler options.
+# endif // !defined(__USE_W32_SOCKETS)
+# if !defined(NOMINMAX)
+# define NOMINMAX 1
+# endif // !defined(NOMINMAX)
+# endif // defined(__CYGWIN__)
+# include <winsock2.h>
+# include <ws2tcpip.h>
+# include <mswsock.h>
+# if defined(ASIO_WSPIAPI_H_DEFINED)
+# undef _WSPIAPI_H_
+# undef ASIO_WSPIAPI_H_DEFINED
+# endif // defined(ASIO_WSPIAPI_H_DEFINED)
+# if !defined(ASIO_NO_DEFAULT_LINKED_LIBS)
+# if defined(_MSC_VER) || defined(__BORLANDC__)
+# pragma comment(lib, "ws2_32.lib")
+# pragma comment(lib, "mswsock.lib")
+# endif // defined(_MSC_VER) || defined(__BORLANDC__)
+# endif // !defined(ASIO_NO_DEFAULT_LINKED_LIBS)
+# include "asio/detail/old_win_sdk_compat.hpp"
+#else
+# include <sys/ioctl.h>
+# include <sys/poll.h>
+# include <sys/types.h>
+# include <sys/select.h>
+# include <sys/socket.h>
+# include <sys/uio.h>
+# include <netinet/in.h>
+# include <netinet/tcp.h>
+# include <arpa/inet.h>
+# include <netdb.h>
+# include <net/if.h>
+# if defined(__sun)
+# include <sys/filio.h>
+# endif
+#endif
+#include "asio/detail/pop_options.hpp"
+
+namespace asio {
+namespace detail {
+
+#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+typedef SOCKET socket_type;
+const SOCKET invalid_socket = INVALID_SOCKET;
+const int socket_error_retval = SOCKET_ERROR;
+const int max_addr_v4_str_len = 256;
+const int max_addr_v6_str_len = 256;
+typedef sockaddr socket_addr_type;
+typedef int socket_addr_len_type;
+typedef in_addr in4_addr_type;
+typedef ip_mreq in4_mreq_type;
+typedef sockaddr_in sockaddr_in4_type;
+# if defined(ASIO_HAS_OLD_WIN_SDK)
+typedef in6_addr_emulation in6_addr_type;
+typedef ipv6_mreq_emulation in6_mreq_type;
+typedef sockaddr_in6_emulation sockaddr_in6_type;
+typedef sockaddr_storage_emulation sockaddr_storage_type;
+typedef addrinfo_emulation addrinfo_type;
+# else
+typedef in6_addr in6_addr_type;
+typedef ipv6_mreq in6_mreq_type;
+typedef sockaddr_in6 sockaddr_in6_type;
+typedef sockaddr_storage sockaddr_storage_type;
+typedef addrinfo addrinfo_type;
+# endif
+typedef unsigned long ioctl_arg_type;
+typedef u_long u_long_type;
+typedef u_short u_short_type;
+const int shutdown_receive = SD_RECEIVE;
+const int shutdown_send = SD_SEND;
+const int shutdown_both = SD_BOTH;
+const int message_peek = MSG_PEEK;
+const int message_out_of_band = MSG_OOB;
+const int message_do_not_route = MSG_DONTROUTE;
+#else
+typedef int socket_type;
+const int invalid_socket = -1;
+const int socket_error_retval = -1;
+const int max_addr_v4_str_len = INET_ADDRSTRLEN;
+const int max_addr_v6_str_len = INET6_ADDRSTRLEN + 1 + IF_NAMESIZE;
+typedef sockaddr socket_addr_type;
+typedef socklen_t socket_addr_len_type;
+typedef in_addr in4_addr_type;
+typedef ip_mreq in4_mreq_type;
+typedef sockaddr_in sockaddr_in4_type;
+typedef in6_addr in6_addr_type;
+typedef ipv6_mreq in6_mreq_type;
+typedef sockaddr_in6 sockaddr_in6_type;
+typedef sockaddr_storage sockaddr_storage_type;
+typedef addrinfo addrinfo_type;
+typedef int ioctl_arg_type;
+typedef uint32_t u_long_type;
+typedef uint16_t u_short_type;
+const int shutdown_receive = SHUT_RD;
+const int shutdown_send = SHUT_WR;
+const int shutdown_both = SHUT_RDWR;
+const int message_peek = MSG_PEEK;
+const int message_out_of_band = MSG_OOB;
+const int message_do_not_route = MSG_DONTROUTE;
+#endif
+const int custom_socket_option_level = 0xA5100000;
+const int enable_connection_aborted_option = 1;
+
+#if defined(_WIN64)
+std::size_t hash_value(SOCKET s)
+{
+ return static_cast<std::size_t>(s);
+}
+#endif // defined(_WIN64)
+
+} // namespace detail
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_DETAIL_SOCKET_TYPES_HPP
diff --git a/library/include/libtorrent/asio/detail/strand_service.hpp b/library/include/libtorrent/asio/detail/strand_service.hpp
new file mode 100644
index 000000000..778cff66f
--- /dev/null
+++ b/library/include/libtorrent/asio/detail/strand_service.hpp
@@ -0,0 +1,528 @@
+//
+// strand_service.hpp
+// ~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_STRAND_SERVICE_HPP
+#define ASIO_DETAIL_STRAND_SERVICE_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/detail/push_options.hpp"
+#include <boost/aligned_storage.hpp>
+#include <boost/assert.hpp>
+#include <boost/intrusive_ptr.hpp>
+#include "asio/detail/pop_options.hpp"
+
+#include "asio/io_service.hpp"
+#include "asio/detail/bind_handler.hpp"
+#include "asio/detail/call_stack.hpp"
+#include "asio/detail/handler_alloc_helpers.hpp"
+#include "asio/detail/handler_invoke_helpers.hpp"
+#include "asio/detail/mutex.hpp"
+#include "asio/detail/noncopyable.hpp"
+
+namespace asio {
+namespace detail {
+
+// Default service implementation for a strand.
+class strand_service
+ : public asio::io_service::service
+{
+public:
+ class handler_base;
+ class invoke_current_handler;
+ class post_next_waiter_on_exit;
+
+ // The underlying implementation of a strand.
+ class strand_impl
+ {
+#if defined (__BORLANDC__)
+ public:
+#else
+ private:
+#endif
+ void add_ref()
+ {
+ asio::detail::mutex::scoped_lock lock(mutex_);
+ ++ref_count_;
+ }
+
+ void release()
+ {
+ asio::detail::mutex::scoped_lock lock(mutex_);
+ --ref_count_;
+ if (ref_count_ == 0)
+ {
+ lock.unlock();
+ delete this;
+ }
+ }
+
+ private:
+ // Only this service will have access to the internal values.
+ friend class strand_service;
+ friend class post_next_waiter_on_exit;
+ friend class invoke_current_handler;
+
+ strand_impl(strand_service& owner)
+ : owner_(owner),
+ current_handler_(0),
+ first_waiter_(0),
+ last_waiter_(0),
+ ref_count_(0)
+ {
+ // Insert implementation into linked list of all implementations.
+ asio::detail::mutex::scoped_lock lock(owner_.mutex_);
+ next_ = owner_.impl_list_;
+ prev_ = 0;
+ if (owner_.impl_list_)
+ owner_.impl_list_->prev_ = this;
+ owner_.impl_list_ = this;
+ }
+
+ ~strand_impl()
+ {
+ // Remove implementation from linked list of all implementations.
+ asio::detail::mutex::scoped_lock lock(owner_.mutex_);
+ if (owner_.impl_list_ == this)
+ owner_.impl_list_ = next_;
+ if (prev_)
+ prev_->next_ = next_;
+ if (next_)
+ next_->prev_= prev_;
+ next_ = 0;
+ prev_ = 0;
+ lock.unlock();
+
+ if (current_handler_)
+ {
+ current_handler_->destroy();
+ }
+
+ while (first_waiter_)
+ {
+ handler_base* next = first_waiter_->next_;
+ first_waiter_->destroy();
+ first_waiter_ = next;
+ }
+ }
+
+ // Mutex to protect access to internal data.
+ asio::detail::mutex mutex_;
+
+ // The service that owns this implementation.
+ strand_service& owner_;
+
+ // The handler that is ready to execute. If this pointer is non-null then it
+ // indicates that a handler holds the lock.
+ handler_base* current_handler_;
+
+ // The start of the list of waiting handlers for the strand.
+ handler_base* first_waiter_;
+
+ // The end of the list of waiting handlers for the strand.
+ handler_base* last_waiter_;
+
+ // Storage for posted handlers.
+ typedef boost::aligned_storage<64> handler_storage_type;
+#if defined(__BORLANDC__)
+ boost::aligned_storage<64> handler_storage_;
+#else
+ handler_storage_type handler_storage_;
+#endif
+
+ // Pointers to adjacent socket implementations in linked list.
+ strand_impl* next_;
+ strand_impl* prev_;
+
+ // The reference count on the strand implementation.
+ size_t ref_count_;
+
+#if !defined(__BORLANDC__)
+ friend void intrusive_ptr_add_ref(strand_impl* p)
+ {
+ p->add_ref();
+ }
+
+ friend void intrusive_ptr_release(strand_impl* p)
+ {
+ p->release();
+ }
+#endif
+ };
+
+ friend class strand_impl;
+
+ typedef boost::intrusive_ptr<strand_impl> implementation_type;
+
+ // Base class for all handler types.
+ class handler_base
+ {
+ public:
+ typedef void (*invoke_func_type)(handler_base*,
+ strand_service&, implementation_type&);
+ typedef void (*destroy_func_type)(handler_base*);
+
+ handler_base(invoke_func_type invoke_func, destroy_func_type destroy_func)
+ : next_(0),
+ invoke_func_(invoke_func),
+ destroy_func_(destroy_func)
+ {
+ }
+
+ void invoke(strand_service& service_impl, implementation_type& impl)
+ {
+ invoke_func_(this, service_impl, impl);
+ }
+
+ void destroy()
+ {
+ destroy_func_(this);
+ }
+
+ protected:
+ ~handler_base()
+ {
+ }
+
+ private:
+ friend class strand_service;
+ friend class strand_impl;
+ friend class post_next_waiter_on_exit;
+ handler_base* next_;
+ invoke_func_type invoke_func_;
+ destroy_func_type destroy_func_;
+ };
+
+ // Helper class to allow handlers to be dispatched.
+ class invoke_current_handler
+ {
+ public:
+ invoke_current_handler(strand_service& service_impl,
+ const implementation_type& impl)
+ : service_impl_(service_impl),
+ impl_(impl)
+ {
+ }
+
+ void operator()()
+ {
+ impl_->current_handler_->invoke(service_impl_, impl_);
+ }
+
+ friend void* asio_handler_allocate(std::size_t size,
+ invoke_current_handler* this_handler)
+ {
+ return this_handler->do_handler_allocate(size);
+ }
+
+ friend void asio_handler_deallocate(void*, std::size_t,
+ invoke_current_handler*)
+ {
+ }
+
+ void* do_handler_allocate(std::size_t size)
+ {
+#if defined(__BORLANDC__)
+ BOOST_ASSERT(size <= boost::aligned_storage<64>::size);
+#else
+ BOOST_ASSERT(size <= strand_impl::handler_storage_type::size);
+#endif
+ return impl_->handler_storage_.address();
+ }
+
+ template <typename Function>
+ friend void asio_handler_invoke(Function function,
+ invoke_current_handler*)
+ {
+ function();
+ }
+
+ private:
+ strand_service& service_impl_;
+ implementation_type impl_;
+ };
+
+ // Helper class to automatically enqueue next waiter on block exit.
+ class post_next_waiter_on_exit
+ {
+ public:
+ post_next_waiter_on_exit(strand_service& service_impl,
+ implementation_type& impl)
+ : service_impl_(service_impl),
+ impl_(impl),
+ cancelled_(false)
+ {
+ }
+
+ ~post_next_waiter_on_exit()
+ {
+ if (!cancelled_)
+ {
+ asio::detail::mutex::scoped_lock lock(impl_->mutex_);
+ impl_->current_handler_ = impl_->first_waiter_;
+ if (impl_->current_handler_)
+ {
+ impl_->first_waiter_ = impl_->first_waiter_->next_;
+ if (impl_->first_waiter_ == 0)
+ impl_->last_waiter_ = 0;
+ lock.unlock();
+ service_impl_.io_service().post(
+ invoke_current_handler(service_impl_, impl_));
+ }
+ }
+ }
+
+ void cancel()
+ {
+ cancelled_ = true;
+ }
+
+ private:
+ strand_service& service_impl_;
+ implementation_type& impl_;
+ bool cancelled_;
+ };
+
+ // Class template for a waiter.
+ template <typename Handler>
+ class handler_wrapper
+ : public handler_base
+ {
+ public:
+ handler_wrapper(Handler handler)
+ : handler_base(&handler_wrapper<Handler>::do_invoke,
+ &handler_wrapper<Handler>::do_destroy),
+ handler_(handler)
+ {
+ }
+
+ static void do_invoke(handler_base* base,
+ strand_service& service_impl, implementation_type& impl)
+ {
+ // Take ownership of the handler object.
+ typedef handler_wrapper<Handler> this_type;
+ this_type* h(static_cast<this_type*>(base));
+ typedef handler_alloc_traits<Handler, this_type> alloc_traits;
+ handler_ptr<alloc_traits> ptr(h->handler_, h);
+
+ post_next_waiter_on_exit p1(service_impl, impl);
+
+ // Make a copy of the handler so that the memory can be deallocated before
+ // the upcall is made.
+ Handler handler(h->handler_);
+
+ // A handler object must still be valid when the next waiter is posted
+ // since destroying the last handler might cause the strand object to be
+ // destroyed. Therefore we create a second post_next_waiter_on_exit object
+ // that will be destroyed before the handler object.
+ p1.cancel();
+ post_next_waiter_on_exit p2(service_impl, impl);
+
+ // Free the memory associated with the handler.
+ ptr.reset();
+
+ // Indicate that this strand is executing on the current thread.
+ call_stack<strand_impl>::context ctx(impl.get());
+
+ // Make the upcall.
+ asio_handler_invoke_helpers::invoke(handler, &handler);
+ }
+
+ static void do_destroy(handler_base* base)
+ {
+ // Take ownership of the handler object.
+ typedef handler_wrapper<Handler> this_type;
+ this_type* h(static_cast<this_type*>(base));
+ typedef handler_alloc_traits<Handler, this_type> alloc_traits;
+ handler_ptr<alloc_traits> ptr(h->handler_, h);
+ }
+
+ private:
+ Handler handler_;
+ };
+
+ // Construct a new strand service for the specified io_service.
+ explicit strand_service(asio::io_service& io_service)
+ : asio::io_service::service(io_service),
+ mutex_(),
+ impl_list_(0)
+ {
+ }
+
+ // Destroy all user-defined handler objects owned by the service.
+ void shutdown_service()
+ {
+ // Construct a list of all handlers to be destroyed.
+ asio::detail::mutex::scoped_lock lock(mutex_);
+ strand_impl* impl = impl_list_;
+ handler_base* first_handler = 0;
+ while (impl)
+ {
+ if (impl->current_handler_)
+ {
+ impl->current_handler_->next_ = first_handler;
+ first_handler = impl->current_handler_;
+ impl->current_handler_ = 0;
+ }
+ if (impl->first_waiter_)
+ {
+ impl->last_waiter_->next_ = first_handler;
+ first_handler = impl->first_waiter_;
+ impl->first_waiter_ = 0;
+ impl->last_waiter_ = 0;
+ }
+ impl = impl->next_;
+ }
+
+ // Destroy all handlers without holding the lock.
+ lock.unlock();
+ while (first_handler)
+ {
+ handler_base* next = first_handler->next_;
+ first_handler->destroy();
+ first_handler = next;
+ }
+ }
+
+ // Construct a new strand implementation.
+ void construct(implementation_type& impl)
+ {
+ impl = implementation_type(new strand_impl(*this));
+ }
+
+ // Destroy a strand implementation.
+ void destroy(implementation_type& impl)
+ {
+ implementation_type().swap(impl);
+ }
+
+ // Request the io_service to invoke the given handler.
+ template <typename Handler>
+ void dispatch(implementation_type& impl, Handler handler)
+ {
+ if (call_stack<strand_impl>::contains(impl.get()))
+ {
+ asio_handler_invoke_helpers::invoke(handler, &handler);
+ }
+ else
+ {
+ asio::detail::mutex::scoped_lock lock(impl->mutex_);
+
+ // Allocate and construct an object to wrap the handler.
+ typedef handler_wrapper<Handler> value_type;
+ typedef handler_alloc_traits<Handler, value_type> alloc_traits;
+ raw_handler_ptr<alloc_traits> raw_ptr(handler);
+ handler_ptr<alloc_traits> ptr(raw_ptr, handler);
+
+ if (impl->current_handler_ == 0)
+ {
+ // This handler now has the lock, so can be dispatched immediately.
+ impl->current_handler_ = ptr.get();
+ lock.unlock();
+ io_service().dispatch(invoke_current_handler(*this, impl));
+ ptr.release();
+ }
+ else
+ {
+ // Another handler already holds the lock, so this handler must join
+ // the list of waiters. The handler will be posted automatically when
+ // its turn comes.
+ if (impl->last_waiter_)
+ {
+ impl->last_waiter_->next_ = ptr.get();
+ impl->last_waiter_ = impl->last_waiter_->next_;
+ }
+ else
+ {
+ impl->first_waiter_ = ptr.get();
+ impl->last_waiter_ = ptr.get();
+ }
+ ptr.release();
+ }
+ }
+ }
+
+ // Request the io_service to invoke the given handler and return immediately.
+ template <typename Handler>
+ void post(implementation_type& impl, Handler handler)
+ {
+ asio::detail::mutex::scoped_lock lock(impl->mutex_);
+
+ // Allocate and construct an object to wrap the handler.
+ typedef handler_wrapper<Handler> value_type;
+ typedef handler_alloc_traits<Handler, value_type> alloc_traits;
+ raw_handler_ptr<alloc_traits> raw_ptr(handler);
+ handler_ptr<alloc_traits> ptr(raw_ptr, handler);
+
+ if (impl->current_handler_ == 0)
+ {
+ // This handler now has the lock, so can be dispatched immediately.
+ impl->current_handler_ = ptr.get();
+ lock.unlock();
+ io_service().post(invoke_current_handler(*this, impl));
+ ptr.release();
+ }
+ else
+ {
+ // Another handler already holds the lock, so this handler must join the
+ // list of waiters. The handler will be posted automatically when its turn
+ // comes.
+ if (impl->last_waiter_)
+ {
+ impl->last_waiter_->next_ = ptr.get();
+ impl->last_waiter_ = impl->last_waiter_->next_;
+ }
+ else
+ {
+ impl->first_waiter_ = ptr.get();
+ impl->last_waiter_ = ptr.get();
+ }
+ ptr.release();
+ }
+ }
+
+private:
+ // Mutex to protect access to the linked list of implementations.
+ asio::detail::mutex mutex_;
+
+ // The head of a linked list of all implementations.
+ strand_impl* impl_list_;
+};
+
+} // namespace detail
+} // namespace asio
+
+#if defined(__BORLANDC__)
+
+namespace boost {
+
+inline void intrusive_ptr_add_ref(
+ asio::detail::strand_service::strand_impl* p)
+{
+ p->add_ref();
+}
+
+inline void intrusive_ptr_release(
+ asio::detail::strand_service::strand_impl* p)
+{
+ p->release();
+}
+
+} // namespace boost
+
+#endif // defined(__BORLANDC__)
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_DETAIL_STRAND_SERVICE_HPP
diff --git a/library/include/libtorrent/asio/detail/task_io_service.hpp b/library/include/libtorrent/asio/detail/task_io_service.hpp
new file mode 100644
index 000000000..fbe56cbc3
--- /dev/null
+++ b/library/include/libtorrent/asio/detail/task_io_service.hpp
@@ -0,0 +1,526 @@
+//
+// task_io_service.hpp
+// ~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_TASK_IO_SERVICE_HPP
+#define ASIO_DETAIL_TASK_IO_SERVICE_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/io_service.hpp"
+#include "asio/detail/call_stack.hpp"
+#include "asio/detail/event.hpp"
+#include "asio/detail/handler_alloc_helpers.hpp"
+#include "asio/detail/handler_invoke_helpers.hpp"
+#include "asio/detail/mutex.hpp"
+#include "asio/detail/task_io_service_fwd.hpp"
+
+namespace asio {
+namespace detail {
+
+template <typename Task>
+class task_io_service
+ : public asio::io_service::service
+{
+public:
+ // Constructor.
+ task_io_service(asio::io_service& io_service)
+ : asio::io_service::service(io_service),
+ mutex_(),
+ task_(use_service<Task>(io_service)),
+ outstanding_work_(0),
+ handler_queue_(&task_handler_),
+ handler_queue_end_(&task_handler_),
+ interrupted_(false),
+ shutdown_(false),
+ first_idle_thread_(0)
+ {
+ }
+
+ // Destroy all user-defined handler objects owned by the service.
+ void shutdown_service()
+ {
+ asio::detail::mutex::scoped_lock lock(mutex_);
+ shutdown_ = true;
+ lock.unlock();
+
+ // Destroy handler objects.
+ while (handler_queue_)
+ {
+ handler_base* h = handler_queue_;
+ handler_queue_ = h->next_;
+ if (h != &task_handler_)
+ h->destroy();
+ }
+
+ // Reset handler queue to initial state.
+ handler_queue_ = &task_handler_;
+ handler_queue_end_ = &task_handler_;
+ }
+
+ // Run the event loop until interrupted or no more work.
+ size_t run()
+ {
+ typename call_stack<task_io_service>::context ctx(this);
+
+ idle_thread_info this_idle_thread;
+ this_idle_thread.prev = &this_idle_thread;
+ this_idle_thread.next = &this_idle_thread;
+
+ asio::detail::mutex::scoped_lock lock(mutex_);
+
+ size_t n = 0;
+ while (do_one(lock, &this_idle_thread))
+ if (n != (std::numeric_limits<size_t>::max)())
+ ++n;
+ return n;
+ }
+
+ // Run until interrupted or one operation is performed.
+ size_t run_one()
+ {
+ typename call_stack<task_io_service>::context ctx(this);
+
+ idle_thread_info this_idle_thread;
+ this_idle_thread.prev = &this_idle_thread;
+ this_idle_thread.next = &this_idle_thread;
+
+ asio::detail::mutex::scoped_lock lock(mutex_);
+
+ return do_one(lock, &this_idle_thread);
+ }
+
+ // Poll for operations without blocking.
+ size_t poll()
+ {
+ typename call_stack<task_io_service>::context ctx(this);
+
+ asio::detail::mutex::scoped_lock lock(mutex_);
+
+ size_t n = 0;
+ while (do_one(lock, 0))
+ if (n != (std::numeric_limits<size_t>::max)())
+ ++n;
+ return n;
+ }
+
+ // Poll for one operation without blocking.
+ size_t poll_one()
+ {
+ typename call_stack<task_io_service>::context ctx(this);
+
+ asio::detail::mutex::scoped_lock lock(mutex_);
+
+ return do_one(lock, 0);
+ }
+
+ // Interrupt the event processing loop.
+ void interrupt()
+ {
+ asio::detail::mutex::scoped_lock lock(mutex_);
+ interrupt_all_threads();
+ }
+
+ // Reset in preparation for a subsequent run invocation.
+ void reset()
+ {
+ asio::detail::mutex::scoped_lock lock(mutex_);
+ interrupted_ = false;
+ }
+
+ // Notify that some work has started.
+ void work_started()
+ {
+ asio::detail::mutex::scoped_lock lock(mutex_);
+ ++outstanding_work_;
+ }
+
+ // Notify that some work has finished.
+ void work_finished()
+ {
+ asio::detail::mutex::scoped_lock lock(mutex_);
+ if (--outstanding_work_ == 0)
+ interrupt_all_threads();
+ }
+
+ // Request invocation of the given handler.
+ template <typename Handler>
+ void dispatch(Handler handler)
+ {
+ if (call_stack<task_io_service>::contains(this))
+ asio_handler_invoke_helpers::invoke(handler, &handler);
+ else
+ post(handler);
+ }
+
+ // Request invocation of the given handler and return immediately.
+ template <typename Handler>
+ void post(Handler handler)
+ {
+ // Allocate and construct an operation to wrap the handler.
+ typedef handler_wrapper<Handler> value_type;
+ typedef handler_alloc_traits<Handler, value_type> alloc_traits;
+ raw_handler_ptr<alloc_traits> raw_ptr(handler);
+ handler_ptr<alloc_traits> ptr(raw_ptr, handler);
+
+ asio::detail::mutex::scoped_lock lock(mutex_);
+
+ // If the service has been shut down we silently discard the handler.
+ if (shutdown_)
+ return;
+
+ // Add the handler to the end of the queue.
+ if (handler_queue_end_)
+ {
+ handler_queue_end_->next_ = ptr.get();
+ handler_queue_end_ = ptr.get();
+ }
+ else
+ {
+ handler_queue_ = handler_queue_end_ = ptr.get();
+ }
+ ptr.release();
+
+ // An undelivered handler is treated as unfinished work.
+ ++outstanding_work_;
+
+ // Wake up a thread to execute the handler.
+ if (!interrupt_one_idle_thread())
+ if (task_handler_.next_ == 0 && handler_queue_end_ != &task_handler_)
+ task_.interrupt();
+ }
+
+private:
+ struct idle_thread_info;
+
+ size_t do_one(asio::detail::mutex::scoped_lock& lock,
+ idle_thread_info* this_idle_thread)
+ {
+ if (outstanding_work_ == 0 && !interrupted_)
+ {
+ interrupt_all_threads();
+ return 0;
+ }
+
+ bool polling = !this_idle_thread;
+ bool task_has_run = false;
+ while (!interrupted_)
+ {
+ if (handler_queue_)
+ {
+ // Prepare to execute first handler from queue.
+ handler_base* h = handler_queue_;
+ handler_queue_ = h->next_;
+ if (handler_queue_ == 0)
+ handler_queue_end_ = 0;
+ bool more_handlers = (handler_queue_ != 0);
+ lock.unlock();
+
+ if (h == &task_handler_)
+ {
+ // If the task has already run and we're polling then we're done.
+ if (task_has_run && polling)
+ return 0;
+ task_has_run = true;
+
+ task_cleanup c(lock, *this);
+
+ // Run the task. May throw an exception. Only block if the handler
+ // queue is empty and we have an idle_thread_info object, otherwise
+ // we want to return as soon as possible.
+ task_.run(!more_handlers && !polling);
+ }
+ else
+ {
+ handler_cleanup c(lock, *this);
+
+ // Invoke the handler. May throw an exception.
+ h->call(); // call() deletes the handler object
+
+ return 1;
+ }
+ }
+ else if (this_idle_thread)
+ {
+ // Nothing to run right now, so just wait for work to do.
+ if (first_idle_thread_)
+ {
+ this_idle_thread->next = first_idle_thread_;
+ this_idle_thread->prev = first_idle_thread_->prev;
+ first_idle_thread_->prev->next = this_idle_thread;
+ first_idle_thread_->prev = this_idle_thread;
+ }
+ first_idle_thread_ = this_idle_thread;
+ this_idle_thread->wakeup_event.clear();
+ lock.unlock();
+ this_idle_thread->wakeup_event.wait();
+ lock.lock();
+ if (this_idle_thread->next == this_idle_thread)
+ {
+ first_idle_thread_ = 0;
+ }
+ else
+ {
+ if (first_idle_thread_ == this_idle_thread)
+ first_idle_thread_ = this_idle_thread->next;
+ this_idle_thread->next->prev = this_idle_thread->prev;
+ this_idle_thread->prev->next = this_idle_thread->next;
+ this_idle_thread->next = this_idle_thread;
+ this_idle_thread->prev = this_idle_thread;
+ }
+ }
+ else
+ {
+ return 0;
+ }
+ }
+
+ return 0;
+ }
+
+ // Interrupt the task and all idle threads.
+ void interrupt_all_threads()
+ {
+ interrupted_ = true;
+ interrupt_all_idle_threads();
+ if (task_handler_.next_ == 0 && handler_queue_end_ != &task_handler_)
+ task_.interrupt();
+ }
+
+ // Interrupt a single idle thread. Returns true if a thread was interrupted,
+ // false if no running thread could be found to interrupt.
+ bool interrupt_one_idle_thread()
+ {
+ if (first_idle_thread_)
+ {
+ first_idle_thread_->wakeup_event.signal();
+ first_idle_thread_ = first_idle_thread_->next;
+ return true;
+ }
+ return false;
+ }
+
+ // Interrupt all idle threads.
+ void interrupt_all_idle_threads()
+ {
+ if (first_idle_thread_)
+ {
+ first_idle_thread_->wakeup_event.signal();
+ idle_thread_info* current_idle_thread = first_idle_thread_->next;
+ while (current_idle_thread != first_idle_thread_)
+ {
+ current_idle_thread->wakeup_event.signal();
+ current_idle_thread = current_idle_thread->next;
+ }
+ }
+ }
+
+ class task_cleanup;
+
+ // The base class for all handler wrappers. A function pointer is used
+ // instead of virtual functions to avoid the associated overhead.
+ class handler_base
+ {
+ public:
+ typedef void (*call_func_type)(handler_base*);
+ typedef void (*destroy_func_type)(handler_base*);
+
+ handler_base(call_func_type call_func, destroy_func_type destroy_func)
+ : next_(0),
+ call_func_(call_func),
+ destroy_func_(destroy_func)
+ {
+ }
+
+ void call()
+ {
+ call_func_(this);
+ }
+
+ void destroy()
+ {
+ destroy_func_(this);
+ }
+
+ protected:
+ // Prevent deletion through this type.
+ ~handler_base()
+ {
+ }
+
+ private:
+ friend class task_io_service<Task>;
+ friend class task_cleanup;
+ handler_base* next_;
+ call_func_type call_func_;
+ destroy_func_type destroy_func_;
+ };
+
+ // Template wrapper for handlers.
+ template <typename Handler>
+ class handler_wrapper
+ : public handler_base
+ {
+ public:
+ handler_wrapper(Handler handler)
+ : handler_base(&handler_wrapper<Handler>::do_call,
+ &handler_wrapper<Handler>::do_destroy),
+ handler_(handler)
+ {
+ }
+
+ static void do_call(handler_base* base)
+ {
+ // Take ownership of the handler object.
+ typedef handler_wrapper<Handler> this_type;
+ this_type* h(static_cast<this_type*>(base));
+ typedef handler_alloc_traits<Handler, this_type> alloc_traits;
+ handler_ptr<alloc_traits> ptr(h->handler_, h);
+
+ // Make a copy of the handler so that the memory can be deallocated before
+ // the upcall is made.
+ Handler handler(h->handler_);
+
+ // Free the memory associated with the handler.
+ ptr.reset();
+
+ // Make the upcall.
+ asio_handler_invoke_helpers::invoke(handler, &handler);
+ }
+
+ static void do_destroy(handler_base* base)
+ {
+ // Take ownership of the handler object.
+ typedef handler_wrapper<Handler> this_type;
+ this_type* h(static_cast<this_type*>(base));
+ typedef handler_alloc_traits<Handler, this_type> alloc_traits;
+ handler_ptr<alloc_traits> ptr(h->handler_, h);
+ }
+
+ private:
+ Handler handler_;
+ };
+
+ // Helper class to perform task-related operations on block exit.
+ class task_cleanup;
+ friend class task_cleanup;
+ class task_cleanup
+ {
+ public:
+ task_cleanup(asio::detail::mutex::scoped_lock& lock,
+ task_io_service& task_io_svc)
+ : lock_(lock),
+ task_io_service_(task_io_svc)
+ {
+ }
+
+ ~task_cleanup()
+ {
+ // Reinsert the task at the end of the handler queue.
+ lock_.lock();
+ task_io_service_.task_handler_.next_ = 0;
+ if (task_io_service_.handler_queue_end_)
+ {
+ task_io_service_.handler_queue_end_->next_
+ = &task_io_service_.task_handler_;
+ task_io_service_.handler_queue_end_
+ = &task_io_service_.task_handler_;
+ }
+ else
+ {
+ task_io_service_.handler_queue_
+ = task_io_service_.handler_queue_end_
+ = &task_io_service_.task_handler_;
+ }
+ }
+
+ private:
+ asio::detail::mutex::scoped_lock& lock_;
+ task_io_service& task_io_service_;
+ };
+
+ // Helper class to perform handler-related operations on block exit.
+ class handler_cleanup;
+ friend class handler_cleanup;
+ class handler_cleanup
+ {
+ public:
+ handler_cleanup(asio::detail::mutex::scoped_lock& lock,
+ task_io_service& task_io_svc)
+ : lock_(lock),
+ task_io_service_(task_io_svc)
+ {
+ }
+
+ ~handler_cleanup()
+ {
+ lock_.lock();
+ if (--task_io_service_.outstanding_work_ == 0)
+ task_io_service_.interrupt_all_threads();
+ }
+
+ private:
+ asio::detail::mutex::scoped_lock& lock_;
+ task_io_service& task_io_service_;
+ };
+
+ // Mutex to protect access to internal data.
+ asio::detail::mutex mutex_;
+
+ // The task to be run by this service.
+ Task& task_;
+
+ // Handler object to represent the position of the task in the queue.
+ class task_handler
+ : public handler_base
+ {
+ public:
+ task_handler()
+ : handler_base(0, 0)
+ {
+ }
+ } task_handler_;
+
+ // The count of unfinished work.
+ int outstanding_work_;
+
+ // The start of a linked list of handlers that are ready to be delivered.
+ handler_base* handler_queue_;
+
+ // The end of a linked list of handlers that are ready to be delivered.
+ handler_base* handler_queue_end_;
+
+ // Flag to indicate that the dispatcher has been interrupted.
+ bool interrupted_;
+
+ // Flag to indicate that the dispatcher has been shut down.
+ bool shutdown_;
+
+ // Structure containing information about an idle thread.
+ struct idle_thread_info
+ {
+ event wakeup_event;
+ idle_thread_info* prev;
+ idle_thread_info* next;
+ };
+
+ // The number of threads that are currently idle.
+ idle_thread_info* first_idle_thread_;
+};
+
+} // namespace detail
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_DETAIL_TASK_IO_SERVICE_HPP
diff --git a/library/include/libtorrent/asio/detail/task_io_service_fwd.hpp b/library/include/libtorrent/asio/detail/task_io_service_fwd.hpp
new file mode 100644
index 000000000..8100beac7
--- /dev/null
+++ b/library/include/libtorrent/asio/detail/task_io_service_fwd.hpp
@@ -0,0 +1,31 @@
+//
+// task_io_service_fwd.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_TASK_IO_SERVICE_FWD_HPP
+#define ASIO_DETAIL_TASK_IO_SERVICE_FWD_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+namespace asio {
+namespace detail {
+
+template <typename Task>
+class task_io_service;
+
+} // namespace detail
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_DETAIL_TASK_IO_SERVICE_FWD_HPP
diff --git a/library/include/libtorrent/asio/detail/thread.hpp b/library/include/libtorrent/asio/detail/thread.hpp
new file mode 100644
index 000000000..9d7452b8a
--- /dev/null
+++ b/library/include/libtorrent/asio/detail/thread.hpp
@@ -0,0 +1,50 @@
+//
+// thread.hpp
+// ~~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_THREAD_HPP
+#define ASIO_DETAIL_THREAD_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/detail/push_options.hpp"
+#include <boost/config.hpp>
+#include "asio/detail/pop_options.hpp"
+
+#if !defined(BOOST_HAS_THREADS)
+# include "asio/detail/null_thread.hpp"
+#elif defined(BOOST_WINDOWS)
+# include "asio/detail/win_thread.hpp"
+#elif defined(BOOST_HAS_PTHREADS)
+# include "asio/detail/posix_thread.hpp"
+#else
+# error Only Windows and POSIX are supported!
+#endif
+
+namespace asio {
+namespace detail {
+
+#if !defined(BOOST_HAS_THREADS)
+typedef null_thread thread;
+#elif defined(BOOST_WINDOWS)
+typedef win_thread thread;
+#elif defined(BOOST_HAS_PTHREADS)
+typedef posix_thread thread;
+#endif
+
+} // namespace detail
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_DETAIL_THREAD_HPP
diff --git a/library/include/libtorrent/asio/detail/timer_queue.hpp b/library/include/libtorrent/asio/detail/timer_queue.hpp
new file mode 100644
index 000000000..2526da948
--- /dev/null
+++ b/library/include/libtorrent/asio/detail/timer_queue.hpp
@@ -0,0 +1,345 @@
+//
+// timer_queue.hpp
+// ~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_TIMER_QUEUE_HPP
+#define ASIO_DETAIL_TIMER_QUEUE_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/detail/push_options.hpp"
+#include <cstddef>
+#include <functional>
+#include <limits>
+#include <memory>
+#include <vector>
+#include <boost/config.hpp>
+#include "asio/detail/pop_options.hpp"
+
+#include "asio/error.hpp"
+#include "asio/detail/hash_map.hpp"
+#include "asio/detail/noncopyable.hpp"
+#include "asio/detail/timer_queue_base.hpp"
+
+namespace asio {
+namespace detail {
+
+template <typename Time_Traits>
+class timer_queue
+ : public timer_queue_base
+{
+public:
+ // The time type.
+ typedef typename Time_Traits::time_type time_type;
+
+ // The duration type.
+ typedef typename Time_Traits::duration_type duration_type;
+
+ // Constructor.
+ timer_queue()
+ : timers_(),
+ heap_()
+ {
+ }
+
+ // Add a new timer to the queue. Returns true if this is the timer that is
+ // earliest in the queue, in which case the reactor's event demultiplexing
+ // function call may need to be interrupted and restarted.
+ template <typename Handler>
+ bool enqueue_timer(const time_type& time, Handler handler, void* token)
+ {
+ // Ensure that there is space for the timer in the heap. We reserve here so
+ // that the push_back below will not throw due to a reallocation failure.
+ heap_.reserve(heap_.size() + 1);
+
+ // Create a new timer object.
+ std::auto_ptr<timer<Handler> > new_timer(
+ new timer<Handler>(time, handler, token));
+
+ // Insert the new timer into the hash.
+ typedef typename hash_map<void*, timer_base*>::iterator iterator;
+ typedef typename hash_map<void*, timer_base*>::value_type value_type;
+ std::pair<iterator, bool> result =
+ timers_.insert(value_type(token, new_timer.get()));
+ if (!result.second)
+ {
+ result.first->second->prev_ = new_timer.get();
+ new_timer->next_ = result.first->second;
+ result.first->second = new_timer.get();
+ }
+
+ // Put the timer at the correct position in the heap.
+ new_timer->heap_index_ = heap_.size();
+ heap_.push_back(new_timer.get());
+ up_heap(heap_.size() - 1);
+ bool is_first = (heap_[0] == new_timer.get());
+
+ // Ownership of the timer is transferred to the timer queue.
+ new_timer.release();
+
+ return is_first;
+ }
+
+ // Whether there are no timers in the queue.
+ virtual bool empty() const
+ {
+ return heap_.empty();
+ }
+
+ // Get the time for the timer that is earliest in the queue.
+ virtual boost::posix_time::time_duration wait_duration() const
+ {
+ return Time_Traits::to_posix_duration(
+ Time_Traits::subtract(heap_[0]->time_, Time_Traits::now()));
+ }
+
+ // Dispatch the timers that are earlier than the specified time.
+ virtual void dispatch_timers()
+ {
+ const time_type now = Time_Traits::now();
+ while (!heap_.empty() && !Time_Traits::less_than(now, heap_[0]->time_))
+ {
+ timer_base* t = heap_[0];
+ remove_timer(t);
+ t->invoke(0);
+ }
+ }
+
+ // Cancel the timer with the given token. The handler will be invoked
+ // immediately with the result operation_aborted.
+ std::size_t cancel_timer(void* timer_token)
+ {
+ std::size_t num_cancelled = 0;
+ typedef typename hash_map<void*, timer_base*>::iterator iterator;
+ iterator it = timers_.find(timer_token);
+ if (it != timers_.end())
+ {
+ timer_base* t = it->second;
+ while (t)
+ {
+ timer_base* next = t->next_;
+ remove_timer(t);
+ t->invoke(asio::error::operation_aborted);
+ t = next;
+ ++num_cancelled;
+ }
+ }
+ return num_cancelled;
+ }
+
+ // Destroy all timers.
+ virtual void destroy_timers()
+ {
+ typename hash_map<void*, timer_base*>::iterator i = timers_.begin();
+ typename hash_map<void*, timer_base*>::iterator end = timers_.end();
+ while (i != end)
+ {
+ timer_base* t = i->second;
+ typename hash_map<void*, timer_base*>::iterator old_i = i++;
+ timers_.erase(old_i);
+ t->destroy();
+ }
+ heap_.clear();
+ timers_.clear();
+ }
+
+private:
+ // Base class for timer operations. Function pointers are used instead of
+ // virtual functions to avoid the associated overhead.
+ class timer_base
+ {
+ public:
+ // Perform the timer operation and then destroy.
+ void invoke(int result)
+ {
+ invoke_func_(this, result);
+ }
+
+ // Destroy the timer operation.
+ void destroy()
+ {
+ destroy_func_(this);
+ }
+
+ protected:
+ typedef void (*invoke_func_type)(timer_base*, int);
+ typedef void (*destroy_func_type)(timer_base*);
+
+ // Constructor.
+ timer_base(invoke_func_type invoke_func, destroy_func_type destroy_func,
+ const time_type& time, void* token)
+ : invoke_func_(invoke_func),
+ destroy_func_(destroy_func),
+ time_(time),
+ token_(token),
+ next_(0),
+ prev_(0),
+ heap_index_(
+ std::numeric_limits<size_t>::max BOOST_PREVENT_MACRO_SUBSTITUTION())
+ {
+ }
+
+ // Prevent deletion through this type.
+ ~timer_base()
+ {
+ }
+
+ private:
+ friend class timer_queue<Time_Traits>;
+
+ // The function to be called to dispatch the handler.
+ invoke_func_type invoke_func_;
+
+ // The function to be called to destroy the handler.
+ destroy_func_type destroy_func_;
+
+ // The time when the operation should fire.
+ time_type time_;
+
+ // The token associated with the timer.
+ void* token_;
+
+ // The next timer known to the queue.
+ timer_base* next_;
+
+ // The previous timer known to the queue.
+ timer_base* prev_;
+
+ // The index of the timer in the heap.
+ size_t heap_index_;
+ };
+
+ // Adaptor class template for using handlers in timers.
+ template <typename Handler>
+ class timer
+ : public timer_base
+ {
+ public:
+ // Constructor.
+ timer(const time_type& time, Handler handler, void* token)
+ : timer_base(&timer<Handler>::invoke_handler,
+ &timer<Handler>::destroy_handler, time, token),
+ handler_(handler)
+ {
+ }
+
+ // Invoke the handler and then destroy it.
+ static void invoke_handler(timer_base* base, int result)
+ {
+ std::auto_ptr<timer<Handler> > t(static_cast<timer<Handler>*>(base));
+ t->handler_(result);
+ }
+
+ // Destroy the handler.
+ static void destroy_handler(timer_base* base)
+ {
+ delete static_cast<timer<Handler>*>(base);
+ }
+
+ private:
+ Handler handler_;
+ };
+
+ // Move the item at the given index up the heap to its correct position.
+ void up_heap(size_t index)
+ {
+ size_t parent = (index - 1) / 2;
+ while (index > 0
+ && Time_Traits::less_than(heap_[index]->time_, heap_[parent]->time_))
+ {
+ swap_heap(index, parent);
+ index = parent;
+ parent = (index - 1) / 2;
+ }
+ }
+
+ // Move the item at the given index down the heap to its correct position.
+ void down_heap(size_t index)
+ {
+ size_t child = index * 2 + 1;
+ while (child < heap_.size())
+ {
+ size_t min_child = (child + 1 == heap_.size()
+ || Time_Traits::less_than(
+ heap_[child]->time_, heap_[child + 1]->time_))
+ ? child : child + 1;
+ if (Time_Traits::less_than(heap_[index]->time_, heap_[min_child]->time_))
+ break;
+ swap_heap(index, min_child);
+ index = min_child;
+ child = index * 2 + 1;
+ }
+ }
+
+ // Swap two entries in the heap.
+ void swap_heap(size_t index1, size_t index2)
+ {
+ timer_base* tmp = heap_[index1];
+ heap_[index1] = heap_[index2];
+ heap_[index2] = tmp;
+ heap_[index1]->heap_index_ = index1;
+ heap_[index2]->heap_index_ = index2;
+ }
+
+ // Remove a timer from the heap and list of timers.
+ void remove_timer(timer_base* t)
+ {
+ // Remove the timer from the heap.
+ size_t index = t->heap_index_;
+ if (!heap_.empty() && index < heap_.size())
+ {
+ if (index == heap_.size() - 1)
+ {
+ heap_.pop_back();
+ }
+ else
+ {
+ 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_))
+ up_heap(index);
+ else
+ down_heap(index);
+ }
+ }
+
+ // Remove the timer from the hash.
+ typedef typename hash_map<void*, timer_base*>::iterator iterator;
+ iterator it = timers_.find(t->token_);
+ if (it != timers_.end())
+ {
+ if (it->second == t)
+ it->second = t->next_;
+ if (t->prev_)
+ t->prev_->next_ = t->next_;
+ if (t->next_)
+ t->next_->prev_ = t->prev_;
+ if (it->second == 0)
+ timers_.erase(it);
+ }
+ }
+
+ // A hash of timer token to linked lists of timers.
+ hash_map<void*, timer_base*> timers_;
+
+ // The heap of timers, with the earliest timer at the front.
+ std::vector<timer_base*> heap_;
+};
+
+} // namespace detail
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_DETAIL_TIMER_QUEUE_HPP
diff --git a/library/include/libtorrent/asio/detail/timer_queue_base.hpp b/library/include/libtorrent/asio/detail/timer_queue_base.hpp
new file mode 100644
index 000000000..22f3bb1e5
--- /dev/null
+++ b/library/include/libtorrent/asio/detail/timer_queue_base.hpp
@@ -0,0 +1,56 @@
+//
+// timer_queue_base.hpp
+// ~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_TIMER_QUEUE_BASE_HPP
+#define ASIO_DETAIL_TIMER_QUEUE_BASE_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/detail/socket_types.hpp" // Must come before posix_time.
+
+#include "asio/detail/push_options.hpp"
+#include <boost/date_time/posix_time/posix_time_types.hpp>
+#include "asio/detail/pop_options.hpp"
+
+#include "asio/detail/noncopyable.hpp"
+
+namespace asio {
+namespace detail {
+
+class timer_queue_base
+ : private noncopyable
+{
+public:
+ // Destructor.
+ virtual ~timer_queue_base() {}
+
+ // Whether there are no timers in the queue.
+ virtual bool empty() const = 0;
+
+ // Get the time to wait until the next timer.
+ virtual boost::posix_time::time_duration wait_duration() const = 0;
+
+ // Dispatch all ready timers.
+ virtual void dispatch_timers() = 0;
+
+ // Destroy all timers.
+ virtual void destroy_timers() = 0;
+};
+
+} // namespace detail
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_DETAIL_TIMER_QUEUE_BASE_HPP
diff --git a/library/include/libtorrent/asio/detail/tss_ptr.hpp b/library/include/libtorrent/asio/detail/tss_ptr.hpp
new file mode 100644
index 000000000..24ce434c2
--- /dev/null
+++ b/library/include/libtorrent/asio/detail/tss_ptr.hpp
@@ -0,0 +1,65 @@
+//
+// tss_ptr.hpp
+// ~~~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_TSS_PTR_HPP
+#define ASIO_DETAIL_TSS_PTR_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/detail/push_options.hpp"
+#include <boost/config.hpp>
+#include "asio/detail/pop_options.hpp"
+
+#if !defined(BOOST_HAS_THREADS)
+# include "asio/detail/null_tss_ptr.hpp"
+#elif defined(BOOST_WINDOWS)
+# include "asio/detail/win_tss_ptr.hpp"
+#elif defined(BOOST_HAS_PTHREADS)
+# include "asio/detail/posix_tss_ptr.hpp"
+#else
+# error Only Windows and POSIX are supported!
+#endif
+
+namespace asio {
+namespace detail {
+
+template <typename T>
+class tss_ptr
+#if !defined(BOOST_HAS_THREADS)
+ : public null_tss_ptr<T>
+#elif defined(BOOST_WINDOWS)
+ : public win_tss_ptr<T>
+#elif defined(BOOST_HAS_PTHREADS)
+ : public posix_tss_ptr<T>
+#endif
+{
+public:
+ void operator=(T* value)
+ {
+#if !defined(BOOST_HAS_THREADS)
+ null_tss_ptr<T>::operator=(value);
+#elif defined(BOOST_WINDOWS)
+ win_tss_ptr<T>::operator=(value);
+#elif defined(BOOST_HAS_PTHREADS)
+ posix_tss_ptr<T>::operator=(value);
+#endif
+ }
+};
+
+} // namespace detail
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_DETAIL_TSS_PTR_HPP
diff --git a/library/include/libtorrent/asio/detail/win_event.hpp b/library/include/libtorrent/asio/detail/win_event.hpp
new file mode 100644
index 000000000..b08cf8a08
--- /dev/null
+++ b/library/include/libtorrent/asio/detail/win_event.hpp
@@ -0,0 +1,88 @@
+//
+// win_event.hpp
+// ~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_WIN_EVENT_HPP
+#define ASIO_DETAIL_WIN_EVENT_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/detail/push_options.hpp"
+#include <boost/config.hpp>
+#include "asio/detail/pop_options.hpp"
+
+#if defined(BOOST_WINDOWS)
+
+#include "asio/system_exception.hpp"
+#include "asio/detail/noncopyable.hpp"
+#include "asio/detail/socket_types.hpp"
+
+#include "asio/detail/push_options.hpp"
+#include <boost/throw_exception.hpp>
+#include "asio/detail/pop_options.hpp"
+
+namespace asio {
+namespace detail {
+
+class win_event
+ : private noncopyable
+{
+public:
+ // Constructor.
+ win_event()
+ : event_(::CreateEvent(0, true, false, 0))
+ {
+ if (!event_)
+ {
+ DWORD last_error = ::GetLastError();
+ system_exception e("event", last_error);
+ boost::throw_exception(e);
+ }
+ }
+
+ // Destructor.
+ ~win_event()
+ {
+ ::CloseHandle(event_);
+ }
+
+ // Signal the event.
+ void signal()
+ {
+ ::SetEvent(event_);
+ }
+
+ // Reset the event.
+ void clear()
+ {
+ ::ResetEvent(event_);
+ }
+
+ // Wait for the event to become signalled.
+ void wait()
+ {
+ ::WaitForSingleObject(event_, INFINITE);
+ }
+
+private:
+ HANDLE event_;
+};
+
+} // namespace detail
+} // namespace asio
+
+#endif // defined(BOOST_WINDOWS)
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_DETAIL_WIN_EVENT_HPP
diff --git a/library/include/libtorrent/asio/detail/win_fd_set_adapter.hpp b/library/include/libtorrent/asio/detail/win_fd_set_adapter.hpp
new file mode 100644
index 000000000..1f393dc11
--- /dev/null
+++ b/library/include/libtorrent/asio/detail/win_fd_set_adapter.hpp
@@ -0,0 +1,83 @@
+//
+// win_fd_set_adapter.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_WIN_FD_SET_ADAPTER_HPP
+#define ASIO_DETAIL_WIN_FD_SET_ADAPTER_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/detail/socket_types.hpp"
+
+#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+
+namespace asio {
+namespace detail {
+
+// Adapts the FD_SET type to meet the Descriptor_Set concept's requirements.
+class win_fd_set_adapter
+{
+public:
+ win_fd_set_adapter()
+ : max_descriptor_(invalid_socket)
+ {
+ fd_set_.fd_count = 0;
+ }
+
+ void set(socket_type descriptor)
+ {
+ for (u_int i = 0; i < fd_set_.fd_count; ++i)
+ if (fd_set_.fd_array[i] == descriptor)
+ return;
+ if (fd_set_.fd_count < win_fd_set_size)
+ fd_set_.fd_array[fd_set_.fd_count++] = descriptor;
+ }
+
+ bool is_set(socket_type descriptor) const
+ {
+ return !!__WSAFDIsSet(descriptor,
+ const_cast<fd_set*>(reinterpret_cast<const fd_set*>(&fd_set_)));
+ }
+
+ operator fd_set*()
+ {
+ return reinterpret_cast<fd_set*>(&fd_set_);
+ }
+
+ socket_type max_descriptor() const
+ {
+ return max_descriptor_;
+ }
+
+private:
+ // This structure is defined to be compatible with the Windows API fd_set
+ // structure, but without being dependent on the value of FD_SETSIZE.
+ enum { win_fd_set_size = 1024 };
+ struct win_fd_set
+ {
+ u_int fd_count;
+ SOCKET fd_array[win_fd_set_size];
+ };
+
+ win_fd_set fd_set_;
+ socket_type max_descriptor_;
+};
+
+} // namespace detail
+} // namespace asio
+
+#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_DETAIL_WIN_FD_SET_ADAPTER_HPP
diff --git a/library/include/libtorrent/asio/detail/win_iocp_io_service.hpp b/library/include/libtorrent/asio/detail/win_iocp_io_service.hpp
new file mode 100644
index 000000000..e39fd33a6
--- /dev/null
+++ b/library/include/libtorrent/asio/detail/win_iocp_io_service.hpp
@@ -0,0 +1,389 @@
+//
+// win_iocp_io_service.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_WIN_IOCP_IO_SERVICE_HPP
+#define ASIO_DETAIL_WIN_IOCP_IO_SERVICE_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/detail/win_iocp_io_service_fwd.hpp"
+
+#if defined(ASIO_HAS_IOCP)
+
+#include "asio/detail/push_options.hpp"
+#include <limits>
+#include <boost/throw_exception.hpp>
+#include "asio/detail/pop_options.hpp"
+
+#include "asio/io_service.hpp"
+#include "asio/system_exception.hpp"
+#include "asio/detail/call_stack.hpp"
+#include "asio/detail/handler_alloc_helpers.hpp"
+#include "asio/detail/handler_invoke_helpers.hpp"
+#include "asio/detail/socket_types.hpp"
+#include "asio/detail/win_iocp_operation.hpp"
+
+namespace asio {
+namespace detail {
+
+class win_iocp_io_service
+ : public asio::io_service::service
+{
+public:
+ // Base class for all operations.
+ typedef win_iocp_operation operation;
+
+ // Constructor.
+ win_iocp_io_service(asio::io_service& io_service)
+ : asio::io_service::service(io_service),
+ iocp_(::CreateIoCompletionPort(INVALID_HANDLE_VALUE, 0, 0, 0)),
+ outstanding_work_(0),
+ interrupted_(0),
+ shutdown_(0)
+ {
+ if (!iocp_.handle)
+ {
+ DWORD last_error = ::GetLastError();
+ system_exception e("iocp", last_error);
+ boost::throw_exception(e);
+ }
+ }
+
+ // Destroy all user-defined handler objects owned by the service.
+ void shutdown_service()
+ {
+ ::InterlockedExchange(&shutdown_, 1);
+
+ for (;;)
+ {
+ DWORD bytes_transferred = 0;
+#if (WINVER < 0x0500)
+ DWORD completion_key = 0;
+#else
+ DWORD_PTR completion_key = 0;
+#endif
+ LPOVERLAPPED overlapped = 0;
+ ::GetQueuedCompletionStatus(iocp_.handle,
+ &bytes_transferred, &completion_key, &overlapped, 0);
+ DWORD last_error = ::GetLastError();
+ if (last_error == WAIT_TIMEOUT)
+ break;
+ if (overlapped)
+ static_cast<operation*>(overlapped)->destroy();
+ }
+ }
+
+ // Register a socket with the IO completion port.
+ void register_socket(socket_type sock)
+ {
+ HANDLE sock_as_handle = reinterpret_cast<HANDLE>(sock);
+ ::CreateIoCompletionPort(sock_as_handle, iocp_.handle, 0, 0);
+ }
+
+ // Run the event loop until interrupted or no more work.
+ size_t run()
+ {
+ if (::InterlockedExchangeAdd(&outstanding_work_, 0) == 0)
+ return 0;
+
+ call_stack<win_iocp_io_service>::context ctx(this);
+
+ size_t n = 0;
+ while (do_one(true))
+ if (n != (std::numeric_limits<size_t>::max)())
+ ++n;
+ return n;
+ }
+
+ // Run until interrupted or one operation is performed.
+ size_t run_one()
+ {
+ if (::InterlockedExchangeAdd(&outstanding_work_, 0) == 0)
+ return 0;
+
+ call_stack<win_iocp_io_service>::context ctx(this);
+
+ return do_one(true);
+ }
+
+ // Poll for operations without blocking.
+ size_t poll()
+ {
+ if (::InterlockedExchangeAdd(&outstanding_work_, 0) == 0)
+ return 0;
+
+ call_stack<win_iocp_io_service>::context ctx(this);
+
+ size_t n = 0;
+ while (do_one(false))
+ if (n != (std::numeric_limits<size_t>::max)())
+ ++n;
+ return n;
+ }
+
+ // Poll for one operation without blocking.
+ size_t poll_one()
+ {
+ if (::InterlockedExchangeAdd(&outstanding_work_, 0) == 0)
+ return 0;
+
+ call_stack<win_iocp_io_service>::context ctx(this);
+
+ return do_one(false);
+ }
+
+ // Interrupt the event processing loop.
+ void interrupt()
+ {
+ if (::InterlockedExchange(&interrupted_, 1) == 0)
+ {
+ if (!::PostQueuedCompletionStatus(iocp_.handle, 0, 0, 0))
+ {
+ DWORD last_error = ::GetLastError();
+ system_exception e("pqcs", last_error);
+ boost::throw_exception(e);
+ }
+ }
+ }
+
+ // Reset in preparation for a subsequent run invocation.
+ void reset()
+ {
+ ::InterlockedExchange(&interrupted_, 0);
+ }
+
+ // Notify that some work has started.
+ void work_started()
+ {
+ ::InterlockedIncrement(&outstanding_work_);
+ }
+
+ // Notify that some work has finished.
+ void work_finished()
+ {
+ if (::InterlockedDecrement(&outstanding_work_) == 0)
+ interrupt();
+ }
+
+ // Request invocation of the given handler.
+ template <typename Handler>
+ void dispatch(Handler handler)
+ {
+ if (call_stack<win_iocp_io_service>::contains(this))
+ asio_handler_invoke_helpers::invoke(handler, &handler);
+ else
+ post(handler);
+ }
+
+ // Request invocation of the given handler and return immediately.
+ template <typename Handler>
+ void post(Handler handler)
+ {
+ // If the service has been shut down we silently discard the handler.
+ if (::InterlockedExchangeAdd(&shutdown_, 0) != 0)
+ return;
+
+ // Allocate and construct an operation to wrap the handler.
+ typedef handler_operation<Handler> value_type;
+ typedef handler_alloc_traits<Handler, value_type> alloc_traits;
+ raw_handler_ptr<alloc_traits> raw_ptr(handler);
+ handler_ptr<alloc_traits> ptr(raw_ptr, *this, handler);
+
+ // Enqueue the operation on the I/O completion port.
+ if (!::PostQueuedCompletionStatus(iocp_.handle, 0, 0, ptr.get()))
+ {
+ DWORD last_error = ::GetLastError();
+ system_exception e("pqcs", last_error);
+ boost::throw_exception(e);
+ }
+
+ // Operation has been successfully posted.
+ ptr.release();
+ }
+
+ // Request invocation of the given OVERLAPPED-derived operation.
+ void post_completion(win_iocp_operation* op, DWORD op_last_error,
+ DWORD bytes_transferred)
+ {
+ // Enqueue the operation on the I/O completion port.
+ if (!::PostQueuedCompletionStatus(iocp_.handle,
+ bytes_transferred, op_last_error, op))
+ {
+ DWORD last_error = ::GetLastError();
+ system_exception e("pqcs", last_error);
+ boost::throw_exception(e);
+ }
+ }
+
+private:
+ // Dequeues at most one operation from the I/O completion port, and then
+ // executes it. Returns the number of operations that were dequeued (i.e.
+ // either 0 or 1).
+ size_t do_one(bool block)
+ {
+ for (;;)
+ {
+ // Get the next operation from the queue.
+ DWORD bytes_transferred = 0;
+#if (WINVER < 0x0500)
+ DWORD completion_key = 0;
+#else
+ DWORD_PTR completion_key = 0;
+#endif
+ LPOVERLAPPED overlapped = 0;
+ ::SetLastError(0);
+ BOOL ok = ::GetQueuedCompletionStatus(iocp_.handle, &bytes_transferred,
+ &completion_key, &overlapped, block ? INFINITE : 0);
+ DWORD last_error = ::GetLastError();
+
+ if (!ok && overlapped == 0)
+ return 0;
+
+ if (overlapped)
+ {
+ // We may have been passed a last_error value in the completion_key.
+ if (last_error == 0)
+ {
+ last_error = completion_key;
+ }
+
+ // Ensure that the io_service does not exit due to running out of work
+ // while we make the upcall.
+ auto_work work(*this);
+
+ // Dispatch the operation.
+ operation* op = static_cast<operation*>(overlapped);
+ op->do_completion(last_error, bytes_transferred);
+
+ return 1;
+ }
+ else
+ {
+ // The interrupted_ flag is always checked to ensure that any leftover
+ // interrupts from a previous run invocation are ignored.
+ if (::InterlockedExchangeAdd(&interrupted_, 0) != 0)
+ {
+ // Wake up next thread that is blocked on GetQueuedCompletionStatus.
+ if (!::PostQueuedCompletionStatus(iocp_.handle, 0, 0, 0))
+ {
+ DWORD last_error = ::GetLastError();
+ system_exception e("pqcs", last_error);
+ boost::throw_exception(e);
+ }
+
+ return 0;
+ }
+ }
+ }
+ }
+
+ struct auto_work
+ {
+ auto_work(win_iocp_io_service& io_service)
+ : io_service_(io_service)
+ {
+ io_service_.work_started();
+ }
+
+ ~auto_work()
+ {
+ io_service_.work_finished();
+ }
+
+ private:
+ win_iocp_io_service& io_service_;
+ };
+
+ template <typename Handler>
+ struct handler_operation
+ : public operation
+ {
+ handler_operation(win_iocp_io_service& io_service,
+ Handler handler)
+ : operation(&handler_operation<Handler>::do_completion_impl,
+ &handler_operation<Handler>::destroy_impl),
+ io_service_(io_service),
+ handler_(handler)
+ {
+ io_service_.work_started();
+ }
+
+ ~handler_operation()
+ {
+ io_service_.work_finished();
+ }
+
+ private:
+ // Prevent copying and assignment.
+ handler_operation(const handler_operation&);
+ void operator=(const handler_operation&);
+
+ static void do_completion_impl(operation* op, DWORD, size_t)
+ {
+ // Take ownership of the operation object.
+ typedef handler_operation<Handler> op_type;
+ op_type* handler_op(static_cast<op_type*>(op));
+ typedef handler_alloc_traits<Handler, op_type> alloc_traits;
+ handler_ptr<alloc_traits> ptr(handler_op->handler_, handler_op);
+
+ // Make a copy of the handler so that the memory can be deallocated before
+ // the upcall is made.
+ Handler handler(handler_op->handler_);
+
+ // Free the memory associated with the handler.
+ ptr.reset();
+
+ // Make the upcall.
+ asio_handler_invoke_helpers::invoke(handler, &handler);
+ }
+
+ static void destroy_impl(operation* op)
+ {
+ // Take ownership of the operation object.
+ typedef handler_operation<Handler> op_type;
+ op_type* handler_op(static_cast<op_type*>(op));
+ typedef handler_alloc_traits<Handler, op_type> alloc_traits;
+ handler_ptr<alloc_traits> ptr(handler_op->handler_, handler_op);
+ }
+
+ win_iocp_io_service& io_service_;
+ Handler handler_;
+ };
+
+ // The IO completion port used for queueing operations.
+ struct iocp_holder
+ {
+ HANDLE handle;
+ iocp_holder(HANDLE h) : handle(h) {}
+ ~iocp_holder() { ::CloseHandle(handle); }
+ } iocp_;
+
+ // The count of unfinished work.
+ long outstanding_work_;
+
+ // Flag to indicate whether the event loop has been interrupted.
+ long interrupted_;
+
+ // Flag to indicate whether the service has been shut down.
+ long shutdown_;
+};
+
+} // namespace detail
+} // namespace asio
+
+#endif // defined(ASIO_HAS_IOCP)
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_DETAIL_WIN_IOCP_IO_SERVICE_HPP
diff --git a/library/include/libtorrent/asio/detail/win_iocp_io_service_fwd.hpp b/library/include/libtorrent/asio/detail/win_iocp_io_service_fwd.hpp
new file mode 100644
index 000000000..005677207
--- /dev/null
+++ b/library/include/libtorrent/asio/detail/win_iocp_io_service_fwd.hpp
@@ -0,0 +1,46 @@
+//
+// win_iocp_io_service_fwd.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_WIN_IOCP_IO_SERVICE_FWD_HPP
+#define ASIO_DETAIL_WIN_IOCP_IO_SERVICE_FWD_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/detail/push_options.hpp"
+#include <boost/config.hpp>
+#include "asio/detail/pop_options.hpp"
+
+// This service is only supported on Win32 (NT4 and later).
+#if !defined(ASIO_DISABLE_IOCP)
+#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+#if defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0400)
+
+// Define this to indicate that IOCP is supported on the target platform.
+#define ASIO_HAS_IOCP 1
+
+namespace asio {
+namespace detail {
+
+class win_iocp_io_service;
+
+} // namespace detail
+} // namespace asio
+
+#endif // defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0400)
+#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+#endif // !defined(ASIO_DISABLE_IOCP)
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_DETAIL_WIN_IOCP_IO_SERVICE_FWD_HPP
diff --git a/library/include/libtorrent/asio/detail/win_iocp_operation.hpp b/library/include/libtorrent/asio/detail/win_iocp_operation.hpp
new file mode 100644
index 000000000..79eb348b4
--- /dev/null
+++ b/library/include/libtorrent/asio/detail/win_iocp_operation.hpp
@@ -0,0 +1,81 @@
+//
+// win_iocp_operation.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_WIN_IOCP_OPERATION_HPP
+#define ASIO_DETAIL_WIN_IOCP_OPERATION_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/detail/win_iocp_io_service_fwd.hpp"
+
+#if defined(ASIO_HAS_IOCP)
+
+#include "asio/detail/socket_types.hpp"
+
+namespace asio {
+namespace detail {
+
+// Base class for all IOCP operations. A function pointer is used instead of
+// virtual functions to avoid the associated overhead.
+//
+// This class inherits from OVERLAPPED so that we can downcast to get back to
+// the win_iocp_operation pointer from the LPOVERLAPPED out parameter of
+// GetQueuedCompletionStatus.
+struct win_iocp_operation
+ : public OVERLAPPED
+{
+ typedef void (*invoke_func_type)(win_iocp_operation*, DWORD, size_t);
+ typedef void (*destroy_func_type)(win_iocp_operation*);
+
+ win_iocp_operation(invoke_func_type invoke_func,
+ destroy_func_type destroy_func)
+ : invoke_func_(invoke_func),
+ destroy_func_(destroy_func)
+ {
+ Internal = 0;
+ InternalHigh = 0;
+ Offset = 0;
+ OffsetHigh = 0;
+ hEvent = 0;
+ }
+
+ void do_completion(DWORD last_error, size_t bytes_transferred)
+ {
+ invoke_func_(this, last_error, bytes_transferred);
+ }
+
+ void destroy()
+ {
+ destroy_func_(this);
+ }
+
+protected:
+ // Prevent deletion through this type.
+ ~win_iocp_operation()
+ {
+ }
+
+private:
+ invoke_func_type invoke_func_;
+ destroy_func_type destroy_func_;
+};
+
+} // namespace detail
+} // namespace asio
+
+#endif // defined(ASIO_HAS_IOCP)
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_DETAIL_WIN_IOCP_OPERATION_HPP
diff --git a/library/include/libtorrent/asio/detail/win_iocp_socket_service.hpp b/library/include/libtorrent/asio/detail/win_iocp_socket_service.hpp
new file mode 100644
index 000000000..3e4cd4d64
--- /dev/null
+++ b/library/include/libtorrent/asio/detail/win_iocp_socket_service.hpp
@@ -0,0 +1,2077 @@
+//
+// win_iocp_socket_service.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_WIN_IOCP_SOCKET_SERVICE_HPP
+#define ASIO_DETAIL_WIN_IOCP_SOCKET_SERVICE_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/detail/win_iocp_io_service_fwd.hpp"
+
+#if defined(ASIO_HAS_IOCP)
+
+#include "asio/detail/push_options.hpp"
+#include <cstring>
+#include <boost/shared_ptr.hpp>
+#include <boost/weak_ptr.hpp>
+#include "asio/detail/pop_options.hpp"
+
+#include "asio/buffer.hpp"
+#include "asio/error.hpp"
+#include "asio/error_handler.hpp"
+#include "asio/io_service.hpp"
+#include "asio/socket_base.hpp"
+#include "asio/detail/bind_handler.hpp"
+#include "asio/detail/handler_alloc_helpers.hpp"
+#include "asio/detail/handler_invoke_helpers.hpp"
+#include "asio/detail/mutex.hpp"
+#include "asio/detail/select_reactor.hpp"
+#include "asio/detail/socket_holder.hpp"
+#include "asio/detail/socket_ops.hpp"
+#include "asio/detail/socket_types.hpp"
+#include "asio/detail/win_iocp_io_service.hpp"
+
+namespace asio {
+namespace detail {
+
+template <typename Protocol>
+class win_iocp_socket_service
+ : public asio::io_service::service
+{
+public:
+ // The protocol type.
+ typedef Protocol protocol_type;
+
+ // The endpoint type.
+ typedef typename Protocol::endpoint endpoint_type;
+
+ // Base class for all operations.
+ typedef win_iocp_operation operation;
+
+ struct noop_deleter { void operator()(void*) {} };
+ typedef boost::shared_ptr<void> shared_cancel_token_type;
+ typedef boost::weak_ptr<void> weak_cancel_token_type;
+
+ // The native type of a socket.
+ class native_type
+ {
+ public:
+ native_type(socket_type s)
+ : socket_(s),
+ have_remote_endpoint_(false)
+ {
+ }
+
+ native_type(socket_type s, const endpoint_type& ep)
+ : socket_(s),
+ have_remote_endpoint_(true),
+ remote_endpoint_(ep)
+ {
+ }
+
+ void operator=(socket_type s)
+ {
+ socket_ = s;
+ have_remote_endpoint_ = false;
+ remote_endpoint_ = endpoint_type();
+ }
+
+ operator socket_type() const
+ {
+ return socket_;
+ }
+
+ bool have_remote_endpoint() const
+ {
+ return have_remote_endpoint_;
+ }
+
+ endpoint_type remote_endpoint() const
+ {
+ return remote_endpoint_;
+ }
+
+ private:
+ socket_type socket_;
+ bool have_remote_endpoint_;
+ endpoint_type remote_endpoint_;
+ };
+
+ // The implementation type of the socket.
+ class implementation_type
+ {
+ public:
+ // Default constructor.
+ implementation_type()
+ : socket_(invalid_socket),
+ flags_(0),
+ cancel_token_(),
+ protocol_(endpoint_type().protocol()),
+ next_(0),
+ prev_(0)
+ {
+ }
+
+ private:
+ // Only this service will have access to the internal values.
+ friend class win_iocp_socket_service;
+
+ // The native socket representation.
+ native_type socket_;
+
+ enum
+ {
+ enable_connection_aborted = 1, // User wants connection_aborted errors.
+ user_set_linger = 2 // The user set the linger option.
+ };
+
+ // Flags indicating the current state of the socket.
+ unsigned char flags_;
+
+ // We use a shared pointer as a cancellation token here to work around the
+ // broken Windows support for cancellation. MSDN says that when you call
+ // closesocket any outstanding WSARecv or WSASend operations will complete
+ // with the error ERROR_OPERATION_ABORTED. In practice they complete with
+ // ERROR_NETNAME_DELETED, which means you can't tell the difference between
+ // a local cancellation and the socket being hard-closed by the peer.
+ shared_cancel_token_type cancel_token_;
+
+ // The protocol associated with the socket.
+ protocol_type protocol_;
+
+ // The ID of the thread from which it is safe to cancel asynchronous
+ // operations. 0 means no asynchronous operations have been started yet.
+ // ~0 means asynchronous operations have been started from more than one
+ // thread, and cancellation is not supported for the socket.
+ DWORD safe_cancellation_thread_id_;
+
+ // Pointers to adjacent socket implementations in linked list.
+ implementation_type* next_;
+ implementation_type* prev_;
+ };
+
+ // The type of the reactor used for connect operations.
+ typedef detail::select_reactor<true> reactor_type;
+
+ // The maximum number of buffers to support in a single operation.
+ enum { max_buffers = 16 };
+
+ // Constructor.
+ win_iocp_socket_service(asio::io_service& io_service)
+ : asio::io_service::service(io_service),
+ iocp_service_(asio::use_service<win_iocp_io_service>(io_service)),
+ reactor_(0),
+ mutex_(),
+ impl_list_(0)
+ {
+ }
+
+ // Destroy all user-defined handler objects owned by the service.
+ void shutdown_service()
+ {
+ // Close all implementations, causing all operations to complete.
+ asio::detail::mutex::scoped_lock lock(mutex_);
+ implementation_type* impl = impl_list_;
+ while (impl)
+ {
+ close(*impl, asio::ignore_error());
+ impl = impl->next_;
+ }
+ }
+
+ // Construct a new socket implementation.
+ void construct(implementation_type& impl)
+ {
+ impl.socket_ = invalid_socket;
+ impl.cancel_token_.reset();
+ impl.safe_cancellation_thread_id_ = 0;
+
+ // Insert implementation into linked list of all implementations.
+ asio::detail::mutex::scoped_lock lock(mutex_);
+ impl.next_ = impl_list_;
+ impl.prev_ = 0;
+ if (impl_list_)
+ impl_list_->prev_ = &impl;
+ impl_list_ = &impl;
+ }
+
+ // Destroy a socket implementation.
+ void destroy(implementation_type& impl)
+ {
+ if (impl.socket_ != invalid_socket)
+ {
+ // Check if the reactor was created, in which case we need to close the
+ // socket on the reactor as well to cancel any operations that might be
+ // running there.
+ reactor_type* reactor = static_cast<reactor_type*>(
+ interlocked_compare_exchange_pointer(
+ reinterpret_cast<void**>(&reactor_), 0, 0));
+ if (reactor)
+ reactor->close_descriptor(impl.socket_);
+
+ if (impl.flags_ & implementation_type::user_set_linger)
+ {
+ ::linger opt;
+ opt.l_onoff = 0;
+ opt.l_linger = 0;
+ socket_ops::setsockopt(impl.socket_,
+ SOL_SOCKET, SO_LINGER, &opt, sizeof(opt));
+ }
+
+ socket_ops::close(impl.socket_);
+ impl.socket_ = invalid_socket;
+ impl.cancel_token_.reset();
+ impl.safe_cancellation_thread_id_ = 0;
+ }
+
+ // Remove implementation from linked list of all implementations.
+ asio::detail::mutex::scoped_lock lock(mutex_);
+ if (impl_list_ == &impl)
+ impl_list_ = impl.next_;
+ if (impl.prev_)
+ impl.prev_->next_ = impl.next_;
+ if (impl.next_)
+ impl.next_->prev_= impl.prev_;
+ impl.next_ = 0;
+ impl.prev_ = 0;
+ }
+
+ // Open a new socket implementation.
+ template <typename Error_Handler>
+ void open(implementation_type& impl, const protocol_type& protocol,
+ Error_Handler error_handler)
+ {
+ close(impl, asio::ignore_error());
+
+ socket_holder sock(socket_ops::socket(protocol.family(), protocol.type(),
+ protocol.protocol()));
+ if (sock.get() == invalid_socket)
+ {
+ error_handler(asio::error(socket_ops::get_error()));
+ return;
+ }
+
+ iocp_service_.register_socket(sock.get());
+
+ impl.socket_ = sock.release();
+ impl.cancel_token_.reset(static_cast<void*>(0), noop_deleter());
+ impl.protocol_ = protocol;
+
+ error_handler(asio::error(0));
+ }
+
+ // Assign a native socket to a socket implementation.
+ template <typename Error_Handler>
+ void assign(implementation_type& impl, const protocol_type& protocol,
+ const native_type& native_socket, Error_Handler error_handler)
+ {
+ close(impl, asio::ignore_error());
+
+ iocp_service_.register_socket(native_socket);
+
+ impl.socket_ = native_socket;
+ impl.cancel_token_.reset(static_cast<void*>(0), noop_deleter());
+ impl.protocol_ = protocol;
+
+ error_handler(asio::error(0));
+ }
+
+ // Destroy a socket implementation.
+ template <typename Error_Handler>
+ void close(implementation_type& impl, Error_Handler error_handler)
+ {
+ if (impl.socket_ != invalid_socket)
+ {
+ // Check if the reactor was created, in which case we need to close the
+ // socket on the reactor as well to cancel any operations that might be
+ // running there.
+ reactor_type* reactor = static_cast<reactor_type*>(
+ interlocked_compare_exchange_pointer(
+ reinterpret_cast<void**>(&reactor_), 0, 0));
+ if (reactor)
+ reactor->close_descriptor(impl.socket_);
+
+ if (socket_ops::close(impl.socket_) == socket_error_retval)
+ {
+ error_handler(asio::error(socket_ops::get_error()));
+ return;
+ }
+ else
+ {
+ impl.socket_ = invalid_socket;
+ impl.cancel_token_.reset();
+ impl.safe_cancellation_thread_id_ = 0;
+ }
+ }
+
+ error_handler(asio::error(0));
+ }
+
+ // Get the native socket representation.
+ native_type native(implementation_type& impl)
+ {
+ return impl.socket_;
+ }
+
+ // Cancel all operations associated with the socket.
+ template <typename Error_Handler>
+ void cancel(implementation_type& impl, Error_Handler error_handler)
+ {
+ if (impl.socket_ == invalid_socket)
+ {
+ asio::error error(asio::error::bad_descriptor);
+ error_handler(error);
+ }
+ else if (impl.safe_cancellation_thread_id_ == 0)
+ {
+ // No operations have been started, so there's nothing to cancel.
+ error_handler(asio::error(0));
+ }
+ else if (impl.safe_cancellation_thread_id_ == ::GetCurrentThreadId())
+ {
+ // Asynchronous operations have been started from the current thread only,
+ // so it is safe to try to cancel them using CancelIo.
+ socket_type sock = impl.socket_;
+ HANDLE sock_as_handle = reinterpret_cast<HANDLE>(sock);
+ if (!::CancelIo(sock_as_handle))
+ {
+ DWORD last_error = ::GetLastError();
+ error_handler(asio::error(last_error));
+ }
+ else
+ {
+ error_handler(asio::error(0));
+ }
+ }
+ else
+ {
+ // Asynchronous operations have been started from more than one thread,
+ // so cancellation is not safe.
+ error_handler(asio::error(asio::error::not_supported));
+ }
+ }
+
+ // Bind the socket to the specified local endpoint.
+ template <typename Error_Handler>
+ void bind(implementation_type& impl, const endpoint_type& endpoint,
+ Error_Handler error_handler)
+ {
+ if (socket_ops::bind(impl.socket_, endpoint.data(),
+ endpoint.size()) == socket_error_retval)
+ error_handler(asio::error(socket_ops::get_error()));
+ else
+ error_handler(asio::error(0));
+ }
+
+ // Place the socket into the state where it will listen for new connections.
+ template <typename Error_Handler>
+ void listen(implementation_type& impl, int backlog,
+ Error_Handler error_handler)
+ {
+ if (socket_ops::listen(impl.socket_, backlog) == socket_error_retval)
+ error_handler(asio::error(socket_ops::get_error()));
+ else
+ error_handler(asio::error(0));
+ }
+
+ // Set a socket option.
+ template <typename Option, typename Error_Handler>
+ void set_option(implementation_type& impl, const Option& option,
+ Error_Handler error_handler)
+ {
+ if (option.level(impl.protocol_) == custom_socket_option_level
+ && option.name(impl.protocol_) == enable_connection_aborted_option)
+ {
+ if (option.size(impl.protocol_) != sizeof(int))
+ {
+ error_handler(asio::error(asio::error::invalid_argument));
+ }
+ else
+ {
+ if (*reinterpret_cast<const int*>(option.data(impl.protocol_)))
+ impl.flags_ |= implementation_type::enable_connection_aborted;
+ else
+ impl.flags_ &= ~implementation_type::enable_connection_aborted;
+ error_handler(asio::error(0));
+ }
+ }
+ else
+ {
+ if (option.level(impl.protocol_) == SOL_SOCKET
+ && option.name(impl.protocol_) == SO_LINGER)
+ {
+ impl.flags_ |= implementation_type::user_set_linger;
+ }
+
+ if (socket_ops::setsockopt(impl.socket_,
+ option.level(impl.protocol_), option.name(impl.protocol_),
+ option.data(impl.protocol_), option.size(impl.protocol_)))
+ error_handler(asio::error(socket_ops::get_error()));
+ else
+ error_handler(asio::error(0));
+ }
+ }
+
+ // Set a socket option.
+ template <typename Option, typename Error_Handler>
+ void get_option(const implementation_type& impl, Option& option,
+ Error_Handler error_handler) const
+ {
+ if (option.level(impl.protocol_) == custom_socket_option_level
+ && option.name(impl.protocol_) == enable_connection_aborted_option)
+ {
+ if (option.size(impl.protocol_) != sizeof(int))
+ {
+ error_handler(asio::error(asio::error::invalid_argument));
+ }
+ else
+ {
+ int* target = reinterpret_cast<int*>(option.data(impl.protocol_));
+ if (impl.flags_ & implementation_type::enable_connection_aborted)
+ *target = 1;
+ else
+ *target = 0;
+ error_handler(asio::error(0));
+ }
+ }
+ else
+ {
+ size_t size = option.size(impl.protocol_);
+ if (socket_ops::getsockopt(impl.socket_,
+ option.level(impl.protocol_), option.name(impl.protocol_),
+ option.data(impl.protocol_), &size))
+ error_handler(asio::error(socket_ops::get_error()));
+ else
+ error_handler(asio::error(0));
+ }
+ }
+
+ // Perform an IO control command on the socket.
+ template <typename IO_Control_Command, typename Error_Handler>
+ void io_control(implementation_type& impl, IO_Control_Command& command,
+ Error_Handler error_handler)
+ {
+ if (socket_ops::ioctl(impl.socket_, command.name(),
+ static_cast<ioctl_arg_type*>(command.data())))
+ error_handler(asio::error(socket_ops::get_error()));
+ else
+ error_handler(asio::error(0));
+ }
+
+ // Get the local endpoint.
+ template <typename Error_Handler>
+ void get_local_endpoint(const implementation_type& impl,
+ endpoint_type& endpoint, Error_Handler error_handler) const
+ {
+ socket_addr_len_type addr_len = endpoint.capacity();
+ if (socket_ops::getsockname(impl.socket_, endpoint.data(), &addr_len))
+ {
+ error_handler(asio::error(socket_ops::get_error()));
+ return;
+ }
+
+ endpoint.resize(addr_len);
+ error_handler(asio::error(0));
+ }
+
+ // Get the remote endpoint.
+ template <typename Error_Handler>
+ void get_remote_endpoint(const implementation_type& impl,
+ endpoint_type& endpoint, Error_Handler error_handler) const
+ {
+ if (impl.socket_.have_remote_endpoint())
+ {
+ // Check if socket is still connected.
+ DWORD connect_time = 0;
+ size_t connect_time_len = sizeof(connect_time);
+ if (socket_ops::getsockopt(impl.socket_, SOL_SOCKET, SO_CONNECT_TIME,
+ &connect_time, &connect_time_len) == socket_error_retval)
+ {
+ error_handler(asio::error(socket_ops::get_error()));
+ return;
+ }
+ if (connect_time == 0xFFFFFFFF)
+ {
+ error_handler(asio::error(asio::error::not_connected));
+ return;
+ }
+
+ endpoint = impl.socket_.remote_endpoint();
+ error_handler(asio::error(0));
+ }
+ else
+ {
+ socket_addr_len_type addr_len = endpoint.capacity();
+ if (socket_ops::getpeername(impl.socket_, endpoint.data(), &addr_len))
+ {
+ error_handler(asio::error(socket_ops::get_error()));
+ return;
+ }
+
+ endpoint.resize(addr_len);
+ error_handler(asio::error(0));
+ }
+ }
+
+ /// Disable sends or receives on the socket.
+ template <typename Error_Handler>
+ void shutdown(implementation_type& impl, socket_base::shutdown_type what,
+ Error_Handler error_handler)
+ {
+ if (socket_ops::shutdown(impl.socket_, what) != 0)
+ error_handler(asio::error(socket_ops::get_error()));
+ else
+ error_handler(asio::error(0));
+ }
+
+ // Send the given data to the peer. Returns the number of bytes sent.
+ template <typename Const_Buffers, typename Error_Handler>
+ size_t send(implementation_type& impl, const Const_Buffers& buffers,
+ socket_base::message_flags flags, Error_Handler error_handler)
+ {
+ // Copy buffers into WSABUF array.
+ ::WSABUF bufs[max_buffers];
+ typename Const_Buffers::const_iterator iter = buffers.begin();
+ typename Const_Buffers::const_iterator end = buffers.end();
+ DWORD i = 0;
+ size_t total_buffer_size = 0;
+ for (; iter != end && i < max_buffers; ++iter, ++i)
+ {
+ asio::const_buffer buffer(*iter);
+ bufs[i].len = static_cast<u_long>(asio::buffer_size(buffer));
+ bufs[i].buf = const_cast<char*>(
+ asio::buffer_cast<const char*>(buffer));
+ total_buffer_size += asio::buffer_size(buffer);
+ }
+
+ // A request to receive 0 bytes on a stream socket is a no-op.
+ if (impl.protocol_.type() == SOCK_STREAM && total_buffer_size == 0)
+ {
+ error_handler(asio::error(0));
+ return 0;
+ }
+
+ // Send the data.
+ DWORD bytes_transferred = 0;
+ int result = ::WSASend(impl.socket_, bufs,
+ i, &bytes_transferred, flags, 0, 0);
+ if (result != 0)
+ {
+ DWORD last_error = ::WSAGetLastError();
+ if (last_error == ERROR_NETNAME_DELETED)
+ last_error = WSAECONNRESET;
+ error_handler(asio::error(last_error));
+ return 0;
+ }
+
+ error_handler(asio::error(0));
+ return bytes_transferred;
+ }
+
+ template <typename Const_Buffers, typename Handler>
+ class send_operation
+ : public operation
+ {
+ public:
+ send_operation(asio::io_service& io_service,
+ weak_cancel_token_type cancel_token,
+ const Const_Buffers& buffers, Handler handler)
+ : operation(
+ &send_operation<Const_Buffers, Handler>::do_completion_impl,
+ &send_operation<Const_Buffers, Handler>::destroy_impl),
+ work_(io_service),
+ cancel_token_(cancel_token),
+ buffers_(buffers),
+ handler_(handler)
+ {
+ }
+
+ private:
+ static void do_completion_impl(operation* op,
+ DWORD last_error, size_t bytes_transferred)
+ {
+ // Take ownership of the operation object.
+ typedef send_operation<Const_Buffers, Handler> op_type;
+ op_type* handler_op(static_cast<op_type*>(op));
+ typedef handler_alloc_traits<Handler, op_type> alloc_traits;
+ handler_ptr<alloc_traits> ptr(handler_op->handler_, handler_op);
+
+ // Map ERROR_NETNAME_DELETED to more useful error.
+ if (last_error == ERROR_NETNAME_DELETED)
+ {
+ if (handler_op->cancel_token_.expired())
+ last_error = ERROR_OPERATION_ABORTED;
+ else
+ last_error = WSAECONNRESET;
+ }
+
+ // Make a copy of the handler so that the memory can be deallocated before
+ // the upcall is made.
+ Handler handler(handler_op->handler_);
+
+ // Free the memory associated with the handler.
+ ptr.reset();
+
+ // Call the handler.
+ asio::error error(last_error);
+ asio_handler_invoke_helpers::invoke(
+ detail::bind_handler(handler, error, bytes_transferred), &handler);
+ }
+
+ static void destroy_impl(operation* op)
+ {
+ // Take ownership of the operation object.
+ typedef send_operation<Const_Buffers, Handler> op_type;
+ op_type* handler_op(static_cast<op_type*>(op));
+ typedef handler_alloc_traits<Handler, op_type> alloc_traits;
+ handler_ptr<alloc_traits> ptr(handler_op->handler_, handler_op);
+ }
+
+ asio::io_service::work work_;
+ weak_cancel_token_type cancel_token_;
+ Const_Buffers buffers_;
+ Handler handler_;
+ };
+
+ // Start an asynchronous send. The data being sent must be valid for the
+ // lifetime of the asynchronous operation.
+ template <typename Const_Buffers, typename Handler>
+ void async_send(implementation_type& impl, const Const_Buffers& buffers,
+ socket_base::message_flags flags, Handler handler)
+ {
+ // Update the ID of the thread from which cancellation is safe.
+ if (impl.safe_cancellation_thread_id_ == 0)
+ impl.safe_cancellation_thread_id_ = ::GetCurrentThreadId();
+ else
+ impl.safe_cancellation_thread_id_ = ~DWORD(0);
+
+ // Allocate and construct an operation to wrap the handler.
+ typedef send_operation<Const_Buffers, Handler> value_type;
+ typedef handler_alloc_traits<Handler, value_type> alloc_traits;
+ raw_handler_ptr<alloc_traits> raw_ptr(handler);
+ handler_ptr<alloc_traits> ptr(raw_ptr,
+ io_service(), impl.cancel_token_, buffers, handler);
+
+ // Copy buffers into WSABUF array.
+ ::WSABUF bufs[max_buffers];
+ typename Const_Buffers::const_iterator iter = buffers.begin();
+ typename Const_Buffers::const_iterator end = buffers.end();
+ DWORD i = 0;
+ size_t total_buffer_size = 0;
+ for (; iter != end && i < max_buffers; ++iter, ++i)
+ {
+ asio::const_buffer buffer(*iter);
+ bufs[i].len = static_cast<u_long>(asio::buffer_size(buffer));
+ bufs[i].buf = const_cast<char*>(
+ asio::buffer_cast<const char*>(buffer));
+ total_buffer_size += asio::buffer_size(buffer);
+ }
+
+ // A request to receive 0 bytes on a stream socket is a no-op.
+ if (impl.protocol_.type() == SOCK_STREAM && total_buffer_size == 0)
+ {
+ ptr.reset();
+ asio::error error(asio::error::success);
+ iocp_service_.post(bind_handler(handler, error, 0));
+ return;
+ }
+
+ // Send the data.
+ DWORD bytes_transferred = 0;
+ int result = ::WSASend(impl.socket_, bufs, i,
+ &bytes_transferred, flags, ptr.get(), 0);
+ DWORD last_error = ::WSAGetLastError();
+
+ // Check if the operation completed immediately.
+ if (result != 0 && last_error != WSA_IO_PENDING)
+ {
+ ptr.reset();
+ asio::error error(last_error);
+ iocp_service_.post(bind_handler(handler, error, bytes_transferred));
+ }
+ else
+ {
+ ptr.release();
+ }
+ }
+
+ // Send a datagram to the specified endpoint. Returns the number of bytes
+ // sent.
+ template <typename Const_Buffers, typename Error_Handler>
+ size_t send_to(implementation_type& impl, const Const_Buffers& buffers,
+ const endpoint_type& destination, socket_base::message_flags flags,
+ Error_Handler error_handler)
+ {
+ // Copy buffers into WSABUF array.
+ ::WSABUF bufs[max_buffers];
+ typename Const_Buffers::const_iterator iter = buffers.begin();
+ typename Const_Buffers::const_iterator end = buffers.end();
+ DWORD i = 0;
+ for (; iter != end && i < max_buffers; ++iter, ++i)
+ {
+ asio::const_buffer buffer(*iter);
+ bufs[i].len = static_cast<u_long>(asio::buffer_size(buffer));
+ bufs[i].buf = const_cast<char*>(
+ asio::buffer_cast<const char*>(buffer));
+ }
+
+ // Send the data.
+ DWORD bytes_transferred = 0;
+ int result = ::WSASendTo(impl.socket_, bufs, i, &bytes_transferred,
+ flags, destination.data(), destination.size(), 0, 0);
+ if (result != 0)
+ {
+ DWORD last_error = ::WSAGetLastError();
+ error_handler(asio::error(last_error));
+ return 0;
+ }
+
+ error_handler(asio::error(0));
+ return bytes_transferred;
+ }
+
+ template <typename Const_Buffers, typename Handler>
+ class send_to_operation
+ : public operation
+ {
+ public:
+ send_to_operation(asio::io_service& io_service,
+ const Const_Buffers& buffers, Handler handler)
+ : operation(
+ &send_to_operation<Const_Buffers, Handler>::do_completion_impl,
+ &send_to_operation<Const_Buffers, Handler>::destroy_impl),
+ work_(io_service),
+ buffers_(buffers),
+ handler_(handler)
+ {
+ }
+
+ private:
+ static void do_completion_impl(operation* op,
+ DWORD last_error, size_t bytes_transferred)
+ {
+ // Take ownership of the operation object.
+ typedef send_to_operation<Const_Buffers, Handler> op_type;
+ op_type* handler_op(static_cast<op_type*>(op));
+ typedef handler_alloc_traits<Handler, op_type> alloc_traits;
+ handler_ptr<alloc_traits> ptr(handler_op->handler_, handler_op);
+
+ // Make a copy of the handler so that the memory can be deallocated before
+ // the upcall is made.
+ Handler handler(handler_op->handler_);
+
+ // Free the memory associated with the handler.
+ ptr.reset();
+
+ // Call the handler.
+ asio::error error(last_error);
+ asio_handler_invoke_helpers::invoke(
+ detail::bind_handler(handler, error, bytes_transferred), &handler);
+ }
+
+ static void destroy_impl(operation* op)
+ {
+ // Take ownership of the operation object.
+ typedef send_to_operation<Const_Buffers, Handler> op_type;
+ op_type* handler_op(static_cast<op_type*>(op));
+ typedef handler_alloc_traits<Handler, op_type> alloc_traits;
+ handler_ptr<alloc_traits> ptr(handler_op->handler_, handler_op);
+ }
+
+ asio::io_service::work work_;
+ Const_Buffers buffers_;
+ Handler handler_;
+ };
+
+ // Start an asynchronous send. The data being sent must be valid for the
+ // lifetime of the asynchronous operation.
+ template <typename Const_Buffers, typename Handler>
+ void async_send_to(implementation_type& impl, const Const_Buffers& buffers,
+ const endpoint_type& destination, socket_base::message_flags flags,
+ Handler handler)
+ {
+ // Update the ID of the thread from which cancellation is safe.
+ if (impl.safe_cancellation_thread_id_ == 0)
+ impl.safe_cancellation_thread_id_ = ::GetCurrentThreadId();
+ else
+ impl.safe_cancellation_thread_id_ = ~DWORD(0);
+
+ // Allocate and construct an operation to wrap the handler.
+ typedef send_to_operation<Const_Buffers, Handler> value_type;
+ typedef handler_alloc_traits<Handler, value_type> alloc_traits;
+ raw_handler_ptr<alloc_traits> raw_ptr(handler);
+ handler_ptr<alloc_traits> ptr(raw_ptr, io_service(), buffers, handler);
+
+ // Copy buffers into WSABUF array.
+ ::WSABUF bufs[max_buffers];
+ typename Const_Buffers::const_iterator iter = buffers.begin();
+ typename Const_Buffers::const_iterator end = buffers.end();
+ DWORD i = 0;
+ for (; iter != end && i < max_buffers; ++iter, ++i)
+ {
+ asio::const_buffer buffer(*iter);
+ bufs[i].len = static_cast<u_long>(asio::buffer_size(buffer));
+ bufs[i].buf = const_cast<char*>(
+ asio::buffer_cast<const char*>(buffer));
+ }
+
+ // Send the data.
+ DWORD bytes_transferred = 0;
+ int result = ::WSASendTo(impl.socket_, bufs, i, &bytes_transferred,
+ flags, destination.data(), destination.size(), ptr.get(), 0);
+ DWORD last_error = ::WSAGetLastError();
+
+ // Check if the operation completed immediately.
+ if (result != 0 && last_error != WSA_IO_PENDING)
+ {
+ ptr.reset();
+ asio::error error(last_error);
+ iocp_service_.post(bind_handler(handler, error, bytes_transferred));
+ }
+ else
+ {
+ ptr.release();
+ }
+ }
+
+ // Receive some data from the peer. Returns the number of bytes received.
+ template <typename Mutable_Buffers, typename Error_Handler>
+ size_t receive(implementation_type& impl, const Mutable_Buffers& buffers,
+ socket_base::message_flags flags, Error_Handler error_handler)
+ {
+ // Copy buffers into WSABUF array.
+ ::WSABUF bufs[max_buffers];
+ typename Mutable_Buffers::const_iterator iter = buffers.begin();
+ typename Mutable_Buffers::const_iterator end = buffers.end();
+ DWORD i = 0;
+ size_t total_buffer_size = 0;
+ for (; iter != end && i < max_buffers; ++iter, ++i)
+ {
+ asio::mutable_buffer buffer(*iter);
+ bufs[i].len = static_cast<u_long>(asio::buffer_size(buffer));
+ bufs[i].buf = asio::buffer_cast<char*>(buffer);
+ total_buffer_size += asio::buffer_size(buffer);
+ }
+
+ // A request to receive 0 bytes on a stream socket is a no-op.
+ if (impl.protocol_.type() == SOCK_STREAM && total_buffer_size == 0)
+ {
+ error_handler(asio::error(0));
+ return 0;
+ }
+
+ // Receive some data.
+ DWORD bytes_transferred = 0;
+ DWORD recv_flags = flags;
+ int result = ::WSARecv(impl.socket_, bufs, i,
+ &bytes_transferred, &recv_flags, 0, 0);
+ if (result != 0)
+ {
+ DWORD last_error = ::WSAGetLastError();
+ if (last_error == ERROR_NETNAME_DELETED)
+ last_error = WSAECONNRESET;
+ error_handler(asio::error(last_error));
+ return 0;
+ }
+ if (bytes_transferred == 0)
+ {
+ error_handler(asio::error(asio::error::eof));
+ return 0;
+ }
+
+ error_handler(asio::error(0));
+ return bytes_transferred;
+ }
+
+ template <typename Mutable_Buffers, typename Handler>
+ class receive_operation
+ : public operation
+ {
+ public:
+ receive_operation(asio::io_service& io_service,
+ weak_cancel_token_type cancel_token,
+ const Mutable_Buffers& buffers, Handler handler)
+ : operation(
+ &receive_operation<Mutable_Buffers, Handler>::do_completion_impl,
+ &receive_operation<Mutable_Buffers, Handler>::destroy_impl),
+ work_(io_service),
+ cancel_token_(cancel_token),
+ buffers_(buffers),
+ handler_(handler)
+ {
+ }
+
+ private:
+ static void do_completion_impl(operation* op,
+ DWORD last_error, size_t bytes_transferred)
+ {
+ // Take ownership of the operation object.
+ typedef receive_operation<Mutable_Buffers, Handler> op_type;
+ op_type* handler_op(static_cast<op_type*>(op));
+ typedef handler_alloc_traits<Handler, op_type> alloc_traits;
+ handler_ptr<alloc_traits> ptr(handler_op->handler_, handler_op);
+
+ // Map ERROR_NETNAME_DELETED to more useful error.
+ if (last_error == ERROR_NETNAME_DELETED)
+ {
+ if (handler_op->cancel_token_.expired())
+ last_error = ERROR_OPERATION_ABORTED;
+ else
+ last_error = WSAECONNRESET;
+ }
+
+ // Check for connection closed.
+ else if (last_error == 0 && bytes_transferred == 0)
+ {
+ last_error = asio::error::eof;
+ }
+
+ // Make a copy of the handler so that the memory can be deallocated before
+ // the upcall is made.
+ Handler handler(handler_op->handler_);
+
+ // Free the memory associated with the handler.
+ ptr.reset();
+
+ // Call the handler.
+ asio::error error(last_error);
+ asio_handler_invoke_helpers::invoke(
+ detail::bind_handler(handler, error, bytes_transferred), &handler);
+ }
+
+ static void destroy_impl(operation* op)
+ {
+ // Take ownership of the operation object.
+ typedef receive_operation<Mutable_Buffers, Handler> op_type;
+ op_type* handler_op(static_cast<op_type*>(op));
+ typedef handler_alloc_traits<Handler, op_type> alloc_traits;
+ handler_ptr<alloc_traits> ptr(handler_op->handler_, handler_op);
+ }
+
+ asio::io_service::work work_;
+ weak_cancel_token_type cancel_token_;
+ Mutable_Buffers buffers_;
+ Handler handler_;
+ };
+
+ // Start an asynchronous receive. The buffer for the data being received
+ // must be valid for the lifetime of the asynchronous operation.
+ template <typename Mutable_Buffers, typename Handler>
+ void async_receive(implementation_type& impl, const Mutable_Buffers& buffers,
+ socket_base::message_flags flags, Handler handler)
+ {
+ // Update the ID of the thread from which cancellation is safe.
+ if (impl.safe_cancellation_thread_id_ == 0)
+ impl.safe_cancellation_thread_id_ = ::GetCurrentThreadId();
+ else
+ impl.safe_cancellation_thread_id_ = ~DWORD(0);
+
+ // Allocate and construct an operation to wrap the handler.
+ typedef receive_operation<Mutable_Buffers, Handler> value_type;
+ typedef handler_alloc_traits<Handler, value_type> alloc_traits;
+ raw_handler_ptr<alloc_traits> raw_ptr(handler);
+ handler_ptr<alloc_traits> ptr(raw_ptr,
+ io_service(), impl.cancel_token_, buffers, handler);
+
+ // Copy buffers into WSABUF array.
+ ::WSABUF bufs[max_buffers];
+ typename Mutable_Buffers::const_iterator iter = buffers.begin();
+ typename Mutable_Buffers::const_iterator end = buffers.end();
+ DWORD i = 0;
+ size_t total_buffer_size = 0;
+ for (; iter != end && i < max_buffers; ++iter, ++i)
+ {
+ asio::mutable_buffer buffer(*iter);
+ bufs[i].len = static_cast<u_long>(asio::buffer_size(buffer));
+ bufs[i].buf = asio::buffer_cast<char*>(buffer);
+ total_buffer_size += asio::buffer_size(buffer);
+ }
+
+ // A request to receive 0 bytes on a stream socket is a no-op.
+ if (impl.protocol_.type() == SOCK_STREAM && total_buffer_size == 0)
+ {
+ ptr.reset();
+ asio::error error(asio::error::success);
+ iocp_service_.post(bind_handler(handler, error, 0));
+ return;
+ }
+
+ // Receive some data.
+ DWORD bytes_transferred = 0;
+ DWORD recv_flags = flags;
+ int result = ::WSARecv(impl.socket_, bufs, i,
+ &bytes_transferred, &recv_flags, ptr.get(), 0);
+ DWORD last_error = ::WSAGetLastError();
+ if (result != 0 && last_error != WSA_IO_PENDING)
+ {
+ ptr.reset();
+ asio::error error(last_error);
+ iocp_service_.post(bind_handler(handler, error, bytes_transferred));
+ }
+ else
+ {
+ ptr.release();
+ }
+ }
+
+ // Receive a datagram with the endpoint of the sender. Returns the number of
+ // bytes received.
+ template <typename Mutable_Buffers, typename Error_Handler>
+ size_t receive_from(implementation_type& impl, const Mutable_Buffers& buffers,
+ endpoint_type& sender_endpoint, socket_base::message_flags flags,
+ Error_Handler error_handler)
+ {
+ // Copy buffers into WSABUF array.
+ ::WSABUF bufs[max_buffers];
+ typename Mutable_Buffers::const_iterator iter = buffers.begin();
+ typename Mutable_Buffers::const_iterator end = buffers.end();
+ DWORD i = 0;
+ for (; iter != end && i < max_buffers; ++iter, ++i)
+ {
+ asio::mutable_buffer buffer(*iter);
+ bufs[i].len = static_cast<u_long>(asio::buffer_size(buffer));
+ bufs[i].buf = asio::buffer_cast<char*>(buffer);
+ }
+
+ // Receive some data.
+ DWORD bytes_transferred = 0;
+ DWORD recv_flags = flags;
+ int endpoint_size = sender_endpoint.capacity();
+ int result = ::WSARecvFrom(impl.socket_, bufs, i, &bytes_transferred,
+ &recv_flags, sender_endpoint.data(), &endpoint_size, 0, 0);
+ if (result != 0)
+ {
+ DWORD last_error = ::WSAGetLastError();
+ error_handler(asio::error(last_error));
+ return 0;
+ }
+ if (bytes_transferred == 0)
+ {
+ error_handler(asio::error(asio::error::eof));
+ return 0;
+ }
+
+ sender_endpoint.resize(endpoint_size);
+
+ error_handler(asio::error(0));
+ return bytes_transferred;
+ }
+
+ template <typename Mutable_Buffers, typename Handler>
+ class receive_from_operation
+ : public operation
+ {
+ public:
+ receive_from_operation(asio::io_service& io_service,
+ endpoint_type& endpoint, const Mutable_Buffers& buffers,
+ Handler handler)
+ : operation(
+ &receive_from_operation<Mutable_Buffers, Handler>::do_completion_impl,
+ &receive_from_operation<Mutable_Buffers, Handler>::destroy_impl),
+ endpoint_(endpoint),
+ endpoint_size_(endpoint.capacity()),
+ work_(io_service),
+ buffers_(buffers),
+ handler_(handler)
+ {
+ }
+
+ int& endpoint_size()
+ {
+ return endpoint_size_;
+ }
+
+ private:
+ static void do_completion_impl(operation* op,
+ DWORD last_error, size_t bytes_transferred)
+ {
+ // Take ownership of the operation object.
+ typedef receive_from_operation<Mutable_Buffers, Handler> op_type;
+ op_type* handler_op(static_cast<op_type*>(op));
+ typedef handler_alloc_traits<Handler, op_type> alloc_traits;
+ handler_ptr<alloc_traits> ptr(handler_op->handler_, handler_op);
+
+ // Check for connection closed.
+ if (last_error == 0 && bytes_transferred == 0)
+ {
+ last_error = asio::error::eof;
+ }
+
+ // Record the size of the endpoint returned by the operation.
+ handler_op->endpoint_.resize(handler_op->endpoint_size_);
+
+ // Make a copy of the handler so that the memory can be deallocated before
+ // the upcall is made.
+ Handler handler(handler_op->handler_);
+
+ // Free the memory associated with the handler.
+ ptr.reset();
+
+ // Call the handler.
+ asio::error error(last_error);
+ asio_handler_invoke_helpers::invoke(
+ detail::bind_handler(handler, error, bytes_transferred), &handler);
+ }
+
+ static void destroy_impl(operation* op)
+ {
+ // Take ownership of the operation object.
+ typedef receive_from_operation<Mutable_Buffers, Handler> op_type;
+ op_type* handler_op(static_cast<op_type*>(op));
+ typedef handler_alloc_traits<Handler, op_type> alloc_traits;
+ handler_ptr<alloc_traits> ptr(handler_op->handler_, handler_op);
+ }
+
+ endpoint_type& endpoint_;
+ int endpoint_size_;
+ asio::io_service::work work_;
+ Mutable_Buffers buffers_;
+ Handler handler_;
+ };
+
+ // Start an asynchronous receive. The buffer for the data being received and
+ // the sender_endpoint object must both be valid for the lifetime of the
+ // asynchronous operation.
+ template <typename Mutable_Buffers, typename Handler>
+ void async_receive_from(implementation_type& impl,
+ const Mutable_Buffers& buffers, endpoint_type& sender_endp,
+ socket_base::message_flags flags, Handler handler)
+ {
+ // Update the ID of the thread from which cancellation is safe.
+ if (impl.safe_cancellation_thread_id_ == 0)
+ impl.safe_cancellation_thread_id_ = ::GetCurrentThreadId();
+ else
+ impl.safe_cancellation_thread_id_ = ~DWORD(0);
+
+ // Allocate and construct an operation to wrap the handler.
+ typedef receive_from_operation<Mutable_Buffers, Handler> value_type;
+ typedef handler_alloc_traits<Handler, value_type> alloc_traits;
+ raw_handler_ptr<alloc_traits> raw_ptr(handler);
+ handler_ptr<alloc_traits> ptr(raw_ptr,
+ io_service(), sender_endp, buffers, handler);
+
+ // Copy buffers into WSABUF array.
+ ::WSABUF bufs[max_buffers];
+ typename Mutable_Buffers::const_iterator iter = buffers.begin();
+ typename Mutable_Buffers::const_iterator end = buffers.end();
+ DWORD i = 0;
+ for (; iter != end && i < max_buffers; ++iter, ++i)
+ {
+ asio::mutable_buffer buffer(*iter);
+ bufs[i].len = static_cast<u_long>(asio::buffer_size(buffer));
+ bufs[i].buf = asio::buffer_cast<char*>(buffer);
+ }
+
+ // Receive some data.
+ DWORD bytes_transferred = 0;
+ DWORD recv_flags = flags;
+ int result = ::WSARecvFrom(impl.socket_, bufs, i, &bytes_transferred,
+ &recv_flags, sender_endp.data(), &ptr.get()->endpoint_size(),
+ ptr.get(), 0);
+ DWORD last_error = ::WSAGetLastError();
+ if (result != 0 && last_error != WSA_IO_PENDING)
+ {
+ ptr.reset();
+ asio::error error(last_error);
+ iocp_service_.post(bind_handler(handler, error, bytes_transferred));
+ }
+ else
+ {
+ ptr.release();
+ }
+ }
+
+ // Accept a new connection.
+ template <typename Socket, typename Error_Handler>
+ void accept(implementation_type& impl, Socket& peer,
+ Error_Handler error_handler)
+ {
+ // We cannot accept a socket that is already open.
+ if (peer.native() != invalid_socket)
+ {
+ error_handler(asio::error(asio::error::already_connected));
+ return;
+ }
+
+ for (;;)
+ {
+ socket_holder new_socket(socket_ops::accept(impl.socket_, 0, 0));
+ if (int err = socket_ops::get_error())
+ {
+ if (err == asio::error::connection_aborted
+ && !(impl.flags_ & implementation_type::enable_connection_aborted))
+ {
+ // Retry accept operation.
+ continue;
+ }
+ else
+ {
+ error_handler(asio::error(err));
+ return;
+ }
+ }
+
+ asio::error temp_error;
+ peer.assign(impl.protocol_, new_socket.get(),
+ asio::assign_error(temp_error));
+ if (temp_error)
+ {
+ error_handler(temp_error);
+ return;
+ }
+ else
+ {
+ new_socket.release();
+ error_handler(asio::error(0));
+ return;
+ }
+ }
+ }
+
+ // Accept a new connection.
+ template <typename Socket, typename Error_Handler>
+ void accept_endpoint(implementation_type& impl, Socket& peer,
+ endpoint_type& peer_endpoint, Error_Handler error_handler)
+ {
+ // We cannot accept a socket that is already open.
+ if (peer.native() != invalid_socket)
+ {
+ error_handler(asio::error(asio::error::already_connected));
+ return;
+ }
+
+ for (;;)
+ {
+ socket_addr_len_type addr_len = peer_endpoint.capacity();
+ socket_holder new_socket(socket_ops::accept(
+ impl.socket_, peer_endpoint.data(), &addr_len));
+ if (int err = socket_ops::get_error())
+ {
+ if (err == asio::error::connection_aborted
+ && !(impl.flags_ & implementation_type::enable_connection_aborted))
+ {
+ // Retry accept operation.
+ continue;
+ }
+ else
+ {
+ error_handler(asio::error(err));
+ return;
+ }
+ }
+
+ peer_endpoint.resize(addr_len);
+
+ asio::error temp_error;
+ peer.assign(impl.protocol_, new_socket.get(),
+ asio::assign_error(temp_error));
+ if (temp_error)
+ {
+ error_handler(temp_error);
+ return;
+ }
+ else
+ {
+ new_socket.release();
+ error_handler(asio::error(0));
+ return;
+ }
+ }
+ }
+
+ template <typename Socket, typename Handler>
+ class accept_operation
+ : public operation
+ {
+ public:
+ accept_operation(win_iocp_io_service& io_service, socket_type socket,
+ socket_type new_socket, Socket& peer, const protocol_type& protocol,
+ bool enable_connection_aborted, Handler handler)
+ : operation(
+ &accept_operation<Socket, Handler>::do_completion_impl,
+ &accept_operation<Socket, Handler>::destroy_impl),
+ io_service_(io_service),
+ socket_(socket),
+ new_socket_(new_socket),
+ peer_(peer),
+ protocol_(protocol),
+ work_(io_service.io_service()),
+ enable_connection_aborted_(enable_connection_aborted),
+ handler_(handler)
+ {
+ }
+
+ socket_type new_socket()
+ {
+ return new_socket_.get();
+ }
+
+ void* output_buffer()
+ {
+ return output_buffer_;
+ }
+
+ DWORD address_length()
+ {
+ return sizeof(sockaddr_storage_type) + 16;
+ }
+
+ private:
+ static void do_completion_impl(operation* op,
+ DWORD last_error, size_t bytes_transferred)
+ {
+ // Take ownership of the operation object.
+ typedef accept_operation<Socket, Handler> op_type;
+ op_type* handler_op(static_cast<op_type*>(op));
+ typedef handler_alloc_traits<Handler, op_type> alloc_traits;
+ handler_ptr<alloc_traits> ptr(handler_op->handler_, handler_op);
+
+ // Map Windows error ERROR_NETNAME_DELETED to connection_aborted.
+ if (last_error == ERROR_NETNAME_DELETED)
+ {
+ last_error = asio::error::connection_aborted;
+ }
+
+ // Restart the accept operation if we got the connection_aborted error
+ // and the enable_connection_aborted socket option is not set.
+ if (last_error == asio::error::connection_aborted
+ && !ptr.get()->enable_connection_aborted_)
+ {
+ // Reset OVERLAPPED structure.
+ ptr.get()->Internal = 0;
+ ptr.get()->InternalHigh = 0;
+ ptr.get()->Offset = 0;
+ ptr.get()->OffsetHigh = 0;
+ ptr.get()->hEvent = 0;
+
+ // Create a new socket for the next connection, since the AcceptEx call
+ // fails with WSAEINVAL if we try to reuse the same socket.
+ ptr.get()->new_socket_.reset();
+ ptr.get()->new_socket_.reset(
+ socket_ops::socket(ptr.get()->protocol_.family(),
+ ptr.get()->protocol_.type(), ptr.get()->protocol_.protocol()));
+ last_error = socket_ops::get_error();
+ if (ptr.get()->new_socket() != invalid_socket)
+ {
+ // Accept a connection.
+ DWORD bytes_read = 0;
+ BOOL result = ::AcceptEx(ptr.get()->socket_, ptr.get()->new_socket(),
+ ptr.get()->output_buffer(), 0, ptr.get()->address_length(),
+ ptr.get()->address_length(), &bytes_read, ptr.get());
+ last_error = ::WSAGetLastError();
+
+ // Check if the operation completed immediately.
+ if (!result && last_error != WSA_IO_PENDING)
+ {
+ if (last_error == ERROR_NETNAME_DELETED
+ || last_error == asio::error::connection_aborted)
+ {
+ // Post this handler so that operation will be restarted again.
+ ptr.get()->io_service_.post_completion(ptr.get(), last_error, 0);
+ ptr.release();
+ return;
+ }
+ else
+ {
+ // Operation already complete. Continue with rest of this handler.
+ }
+ }
+ else
+ {
+ // Asynchronous operation has been successfully restarted.
+ ptr.release();
+ return;
+ }
+ }
+ }
+
+ // Get the address of the peer.
+ endpoint_type peer_endpoint;
+ if (last_error == 0)
+ {
+ LPSOCKADDR local_addr = 0;
+ int local_addr_length = 0;
+ LPSOCKADDR remote_addr = 0;
+ int remote_addr_length = 0;
+ GetAcceptExSockaddrs(handler_op->output_buffer(), 0,
+ handler_op->address_length(), handler_op->address_length(),
+ &local_addr, &local_addr_length, &remote_addr, &remote_addr_length);
+ if (remote_addr_length > peer_endpoint.capacity())
+ {
+ last_error = asio::error::invalid_argument;
+ }
+ else
+ {
+ using namespace std; // For memcpy.
+ memcpy(peer_endpoint.data(), remote_addr, remote_addr_length);
+ peer_endpoint.resize(remote_addr_length);
+ }
+ }
+
+ // Need to set the SO_UPDATE_ACCEPT_CONTEXT option so that getsockname
+ // and getpeername will work on the accepted socket.
+ if (last_error == 0)
+ {
+ SOCKET update_ctx_param = handler_op->socket_;
+ if (socket_ops::setsockopt(handler_op->new_socket_.get(),
+ SOL_SOCKET, SO_UPDATE_ACCEPT_CONTEXT,
+ &update_ctx_param, sizeof(SOCKET)) != 0)
+ {
+ last_error = socket_ops::get_error();
+ }
+ }
+
+ // If the socket was successfully accepted, transfer ownership of the
+ // socket to the peer object.
+ if (last_error == 0)
+ {
+ asio::error temp_error;
+ handler_op->peer_.assign(handler_op->protocol_,
+ native_type(handler_op->new_socket_.get(), peer_endpoint),
+ asio::assign_error(temp_error));
+ if (temp_error)
+ last_error = temp_error.code();
+ else
+ handler_op->new_socket_.release();
+ }
+
+ // Make a copy of the handler so that the memory can be deallocated before
+ // the upcall is made.
+ Handler handler(handler_op->handler_);
+
+ // Free the memory associated with the handler.
+ ptr.reset();
+
+ // Call the handler.
+ asio::error error(last_error);
+ asio_handler_invoke_helpers::invoke(
+ detail::bind_handler(handler, error), &handler);
+ }
+
+ static void destroy_impl(operation* op)
+ {
+ // Take ownership of the operation object.
+ typedef accept_operation<Socket, Handler> op_type;
+ op_type* handler_op(static_cast<op_type*>(op));
+ typedef handler_alloc_traits<Handler, op_type> alloc_traits;
+ handler_ptr<alloc_traits> ptr(handler_op->handler_, handler_op);
+ }
+
+ win_iocp_io_service& io_service_;
+ socket_type socket_;
+ socket_holder new_socket_;
+ Socket& peer_;
+ protocol_type protocol_;
+ asio::io_service::work work_;
+ unsigned char output_buffer_[(sizeof(sockaddr_storage_type) + 16) * 2];
+ bool enable_connection_aborted_;
+ Handler handler_;
+ };
+
+ // Start an asynchronous accept. The peer object must be valid until the
+ // accept's handler is invoked.
+ template <typename Socket, typename Handler>
+ void async_accept(implementation_type& impl, Socket& peer, Handler handler)
+ {
+ // Update the ID of the thread from which cancellation is safe.
+ if (impl.safe_cancellation_thread_id_ == 0)
+ impl.safe_cancellation_thread_id_ = ::GetCurrentThreadId();
+ else
+ impl.safe_cancellation_thread_id_ = ~DWORD(0);
+
+ // Check whether acceptor has been initialised.
+ if (impl.socket_ == invalid_socket)
+ {
+ asio::error error(asio::error::bad_descriptor);
+ io_service().post(bind_handler(handler, error));
+ return;
+ }
+
+ // Check that peer socket has not already been connected.
+ if (peer.native() != invalid_socket)
+ {
+ asio::error error(asio::error::already_connected);
+ io_service().post(bind_handler(handler, error));
+ return;
+ }
+
+ // Create a new socket for the connection.
+ socket_holder sock(socket_ops::socket(impl.protocol_.family(),
+ impl.protocol_.type(), impl.protocol_.protocol()));
+ if (sock.get() == invalid_socket)
+ {
+ asio::error error(socket_ops::get_error());
+ io_service().post(bind_handler(handler, error));
+ return;
+ }
+
+ // Allocate and construct an operation to wrap the handler.
+ typedef accept_operation<Socket, Handler> value_type;
+ typedef handler_alloc_traits<Handler, value_type> alloc_traits;
+ raw_handler_ptr<alloc_traits> raw_ptr(handler);
+ socket_type new_socket = sock.get();
+ bool enable_connection_aborted =
+ (impl.flags_ & implementation_type::enable_connection_aborted);
+ handler_ptr<alloc_traits> ptr(raw_ptr,
+ iocp_service_, impl.socket_, new_socket, peer, impl.protocol_,
+ enable_connection_aborted, handler);
+ sock.release();
+
+ // Accept a connection.
+ DWORD bytes_read = 0;
+ BOOL result = ::AcceptEx(impl.socket_, ptr.get()->new_socket(),
+ ptr.get()->output_buffer(), 0, ptr.get()->address_length(),
+ ptr.get()->address_length(), &bytes_read, ptr.get());
+ DWORD last_error = ::WSAGetLastError();
+
+ // Check if the operation completed immediately.
+ if (!result && last_error != WSA_IO_PENDING)
+ {
+ if (!enable_connection_aborted
+ && (last_error == ERROR_NETNAME_DELETED
+ || last_error == asio::error::connection_aborted))
+ {
+ // Post handler so that operation will be restarted again. We do not
+ // perform the AcceptEx again here to avoid the possibility of starving
+ // other handlers.
+ iocp_service_.post_completion(ptr.get(), last_error, 0);
+ ptr.release();
+ }
+ else
+ {
+ ptr.reset();
+ asio::error error(last_error);
+ iocp_service_.post(bind_handler(handler, error));
+ }
+ }
+ else
+ {
+ ptr.release();
+ }
+ }
+
+ template <typename Socket, typename Handler>
+ class accept_endp_operation
+ : public operation
+ {
+ public:
+ accept_endp_operation(win_iocp_io_service& io_service,
+ socket_type socket, socket_type new_socket, Socket& peer,
+ const protocol_type& protocol, endpoint_type& peer_endpoint,
+ bool enable_connection_aborted, Handler handler)
+ : operation(
+ &accept_endp_operation<Socket, Handler>::do_completion_impl,
+ &accept_endp_operation<Socket, Handler>::destroy_impl),
+ io_service_(io_service),
+ socket_(socket),
+ new_socket_(new_socket),
+ peer_(peer),
+ protocol_(protocol),
+ peer_endpoint_(peer_endpoint),
+ work_(io_service.io_service()),
+ enable_connection_aborted_(enable_connection_aborted),
+ handler_(handler)
+ {
+ }
+
+ socket_type new_socket()
+ {
+ return new_socket_.get();
+ }
+
+ void* output_buffer()
+ {
+ return output_buffer_;
+ }
+
+ DWORD address_length()
+ {
+ return sizeof(sockaddr_storage_type) + 16;
+ }
+
+ private:
+ static void do_completion_impl(operation* op,
+ DWORD last_error, size_t bytes_transferred)
+ {
+ // Take ownership of the operation object.
+ typedef accept_endp_operation<Socket, Handler> op_type;
+ op_type* handler_op(static_cast<op_type*>(op));
+ typedef handler_alloc_traits<Handler, op_type> alloc_traits;
+ handler_ptr<alloc_traits> ptr(handler_op->handler_, handler_op);
+
+ // Map Windows error ERROR_NETNAME_DELETED to connection_aborted.
+ if (last_error == ERROR_NETNAME_DELETED)
+ {
+ last_error = asio::error::connection_aborted;
+ }
+
+ // Restart the accept operation if we got the connection_aborted error
+ // and the enable_connection_aborted socket option is not set.
+ if (last_error == asio::error::connection_aborted
+ && !ptr.get()->enable_connection_aborted_)
+ {
+ // Reset OVERLAPPED structure.
+ ptr.get()->Internal = 0;
+ ptr.get()->InternalHigh = 0;
+ ptr.get()->Offset = 0;
+ ptr.get()->OffsetHigh = 0;
+ ptr.get()->hEvent = 0;
+
+ // Create a new socket for the next connection, since the AcceptEx call
+ // fails with WSAEINVAL if we try to reuse the same socket.
+ ptr.get()->new_socket_.reset();
+ ptr.get()->new_socket_.reset(
+ socket_ops::socket(ptr.get()->protocol_.family(),
+ ptr.get()->protocol_.type(), ptr.get()->protocol_.protocol()));
+ last_error = socket_ops::get_error();
+ if (ptr.get()->new_socket() != invalid_socket)
+ {
+ // Accept a connection.
+ DWORD bytes_read = 0;
+ BOOL result = ::AcceptEx(ptr.get()->socket_, ptr.get()->new_socket(),
+ ptr.get()->output_buffer(), 0, ptr.get()->address_length(),
+ ptr.get()->address_length(), &bytes_read, ptr.get());
+ last_error = ::WSAGetLastError();
+
+ // Check if the operation completed immediately.
+ if (!result && last_error != WSA_IO_PENDING)
+ {
+ if (last_error == ERROR_NETNAME_DELETED
+ || last_error == asio::error::connection_aborted)
+ {
+ // Post this handler so that operation will be restarted again.
+ ptr.get()->io_service_.post_completion(ptr.get(), last_error, 0);
+ ptr.release();
+ return;
+ }
+ else
+ {
+ // Operation already complete. Continue with rest of this handler.
+ }
+ }
+ else
+ {
+ // Asynchronous operation has been successfully restarted.
+ ptr.release();
+ return;
+ }
+ }
+ }
+
+ // Get the address of the peer.
+ if (last_error == 0)
+ {
+ LPSOCKADDR local_addr = 0;
+ int local_addr_length = 0;
+ LPSOCKADDR remote_addr = 0;
+ int remote_addr_length = 0;
+ GetAcceptExSockaddrs(handler_op->output_buffer(), 0,
+ handler_op->address_length(), handler_op->address_length(),
+ &local_addr, &local_addr_length, &remote_addr, &remote_addr_length);
+ if (remote_addr_length > handler_op->peer_endpoint_.capacity())
+ {
+ last_error = asio::error::invalid_argument;
+ }
+ else
+ {
+ using namespace std; // For memcpy.
+ memcpy(handler_op->peer_endpoint_.data(),
+ remote_addr, remote_addr_length);
+ handler_op->peer_endpoint_.resize(remote_addr_length);
+ }
+ }
+
+ // Need to set the SO_UPDATE_ACCEPT_CONTEXT option so that getsockname
+ // and getpeername will work on the accepted socket.
+ if (last_error == 0)
+ {
+ SOCKET update_ctx_param = handler_op->socket_;
+ if (socket_ops::setsockopt(handler_op->new_socket_.get(),
+ SOL_SOCKET, SO_UPDATE_ACCEPT_CONTEXT,
+ &update_ctx_param, sizeof(SOCKET)) != 0)
+ {
+ last_error = socket_ops::get_error();
+ }
+ }
+
+ // If the socket was successfully accepted, transfer ownership of the
+ // socket to the peer object.
+ if (last_error == 0)
+ {
+ asio::error temp_error;
+ handler_op->peer_.assign(handler_op->peer_endpoint_.protocol(),
+ native_type(handler_op->new_socket_.get(),
+ handler_op->peer_endpoint_),
+ asio::assign_error(temp_error));
+ if (temp_error)
+ last_error = temp_error.code();
+ else
+ handler_op->new_socket_.release();
+ }
+
+ // Make a copy of the handler so that the memory can be deallocated before
+ // the upcall is made.
+ Handler handler(handler_op->handler_);
+
+ // Free the memory associated with the handler.
+ ptr.reset();
+
+ // Call the handler.
+ asio::error error(last_error);
+ asio_handler_invoke_helpers::invoke(
+ detail::bind_handler(handler, error), &handler);
+ }
+
+ static void destroy_impl(operation* op)
+ {
+ // Take ownership of the operation object.
+ typedef accept_endp_operation<Socket, Handler> op_type;
+ op_type* handler_op(static_cast<op_type*>(op));
+ typedef handler_alloc_traits<Handler, op_type> alloc_traits;
+ handler_ptr<alloc_traits> ptr(handler_op->handler_, handler_op);
+ }
+
+ win_iocp_io_service& io_service_;
+ socket_type socket_;
+ socket_holder new_socket_;
+ Socket& peer_;
+ protocol_type protocol_;
+ endpoint_type& peer_endpoint_;
+ asio::io_service::work work_;
+ unsigned char output_buffer_[(sizeof(sockaddr_storage_type) + 16) * 2];
+ bool enable_connection_aborted_;
+ Handler handler_;
+ };
+
+ // Start an asynchronous accept. The peer and peer_endpoint objects
+ // must be valid until the accept's handler is invoked.
+ template <typename Socket, typename Handler>
+ void async_accept_endpoint(implementation_type& impl, Socket& peer,
+ endpoint_type& peer_endpoint, Handler handler)
+ {
+ // Update the ID of the thread from which cancellation is safe.
+ if (impl.safe_cancellation_thread_id_ == 0)
+ impl.safe_cancellation_thread_id_ = ::GetCurrentThreadId();
+ else
+ impl.safe_cancellation_thread_id_ = ~DWORD(0);
+
+ // Check whether acceptor has been initialised.
+ if (impl.socket_ == invalid_socket)
+ {
+ asio::error error(asio::error::bad_descriptor);
+ io_service().post(bind_handler(handler, error));
+ return;
+ }
+
+ // Check that peer socket has not already been connected.
+ if (peer.native() != invalid_socket)
+ {
+ asio::error error(asio::error::already_connected);
+ io_service().post(bind_handler(handler, error));
+ return;
+ }
+
+ // Create a new socket for the connection.
+ socket_holder sock(socket_ops::socket(impl.protocol_.family(),
+ impl.protocol_.type(), impl.protocol_.protocol()));
+ if (sock.get() == invalid_socket)
+ {
+ asio::error error(socket_ops::get_error());
+ io_service().post(bind_handler(handler, error));
+ return;
+ }
+
+ // Allocate and construct an operation to wrap the handler.
+ typedef accept_endp_operation<Socket, Handler> value_type;
+ typedef handler_alloc_traits<Handler, value_type> alloc_traits;
+ raw_handler_ptr<alloc_traits> raw_ptr(handler);
+ socket_type new_socket = sock.get();
+ bool enable_connection_aborted =
+ (impl.flags_ & implementation_type::enable_connection_aborted);
+ handler_ptr<alloc_traits> ptr(raw_ptr,
+ iocp_service_, impl.socket_, new_socket, peer, impl.protocol_,
+ peer_endpoint, enable_connection_aborted, handler);
+ sock.release();
+
+ // Accept a connection.
+ DWORD bytes_read = 0;
+ BOOL result = ::AcceptEx(impl.socket_, ptr.get()->new_socket(),
+ ptr.get()->output_buffer(), 0, ptr.get()->address_length(),
+ ptr.get()->address_length(), &bytes_read, ptr.get());
+ DWORD last_error = ::WSAGetLastError();
+
+ // Check if the operation completed immediately.
+ if (!result && last_error != WSA_IO_PENDING)
+ {
+ if (!enable_connection_aborted
+ && (last_error == ERROR_NETNAME_DELETED
+ || last_error == asio::error::connection_aborted))
+ {
+ // Post handler so that operation will be restarted again. We do not
+ // perform the AcceptEx again here to avoid the possibility of starving
+ // other handlers.
+ iocp_service_.post_completion(ptr.get(), last_error, 0);
+ ptr.release();
+ }
+ else
+ {
+ ptr.reset();
+ asio::error error(last_error);
+ iocp_service_.post(bind_handler(handler, error));
+ }
+ }
+ else
+ {
+ ptr.release();
+ }
+ }
+
+ // Connect the socket to the specified endpoint.
+ template <typename Error_Handler>
+ void connect(implementation_type& impl, const endpoint_type& peer_endpoint,
+ Error_Handler error_handler)
+ {
+ // Open the socket if it is not already open.
+ if (impl.socket_ == invalid_socket)
+ {
+ // Get the flags used to create the new socket.
+ int family = peer_endpoint.protocol().family();
+ int type = peer_endpoint.protocol().type();
+ int proto = peer_endpoint.protocol().protocol();
+
+ // Create a new socket.
+ impl.socket_ = socket_ops::socket(family, type, proto);
+ if (impl.socket_ == invalid_socket)
+ {
+ error_handler(asio::error(socket_ops::get_error()));
+ return;
+ }
+ iocp_service_.register_socket(impl.socket_);
+ }
+
+ // Perform the connect operation.
+ int result = socket_ops::connect(impl.socket_,
+ peer_endpoint.data(), peer_endpoint.size());
+ if (result == socket_error_retval)
+ error_handler(asio::error(socket_ops::get_error()));
+ else
+ error_handler(asio::error(0));
+ }
+
+ template <typename Handler>
+ class connect_handler
+ {
+ public:
+ connect_handler(socket_type socket,
+ boost::shared_ptr<bool> completed,
+ asio::io_service& io_service,
+ reactor_type& reactor, Handler handler)
+ : socket_(socket),
+ completed_(completed),
+ io_service_(io_service),
+ reactor_(reactor),
+ work_(io_service),
+ handler_(handler)
+ {
+ }
+
+ bool operator()(int result)
+ {
+ // Check whether a handler has already been called for the connection.
+ // If it has, then we don't want to do anything in this handler.
+ if (*completed_)
+ return true;
+
+ // Cancel the other reactor operation for the connection.
+ *completed_ = true;
+ reactor_.enqueue_cancel_ops_unlocked(socket_);
+
+ // Check whether the operation was successful.
+ if (result != 0)
+ {
+ asio::error error(result);
+ io_service_.post(bind_handler(handler_, error));
+ return true;
+ }
+
+ // Get the error code from the connect operation.
+ int connect_error = 0;
+ size_t connect_error_len = sizeof(connect_error);
+ if (socket_ops::getsockopt(socket_, SOL_SOCKET, SO_ERROR,
+ &connect_error, &connect_error_len) == socket_error_retval)
+ {
+ asio::error error(socket_ops::get_error());
+ io_service_.post(bind_handler(handler_, error));
+ return true;
+ }
+
+ // If connection failed then post the handler with the error code.
+ if (connect_error)
+ {
+ asio::error error(connect_error);
+ io_service_.post(bind_handler(handler_, error));
+ return true;
+ }
+
+ // Make the socket blocking again (the default).
+ ioctl_arg_type non_blocking = 0;
+ if (socket_ops::ioctl(socket_, FIONBIO, &non_blocking))
+ {
+ asio::error error(socket_ops::get_error());
+ io_service_.post(bind_handler(handler_, error));
+ return true;
+ }
+
+ // Post the result of the successful connection operation.
+ asio::error error(asio::error::success);
+ io_service_.post(bind_handler(handler_, error));
+ return true;
+ }
+
+ private:
+ socket_type socket_;
+ boost::shared_ptr<bool> completed_;
+ asio::io_service& io_service_;
+ reactor_type& reactor_;
+ asio::io_service::work work_;
+ Handler handler_;
+ };
+
+ // Start an asynchronous connect.
+ template <typename Handler>
+ void async_connect(implementation_type& impl,
+ const endpoint_type& peer_endpoint, Handler handler)
+ {
+ // Update the ID of the thread from which cancellation is safe.
+ if (impl.safe_cancellation_thread_id_ == 0)
+ impl.safe_cancellation_thread_id_ = ::GetCurrentThreadId();
+ else
+ impl.safe_cancellation_thread_id_ = ~DWORD(0);
+
+ // Check if the reactor was already obtained from the io_service.
+ reactor_type* reactor = static_cast<reactor_type*>(
+ interlocked_compare_exchange_pointer(
+ reinterpret_cast<void**>(&reactor_), 0, 0));
+ if (!reactor)
+ {
+ reactor = &(asio::use_service<reactor_type>(io_service()));
+ interlocked_exchange_pointer(
+ reinterpret_cast<void**>(&reactor_), reactor);
+ }
+
+ // Open the socket if it is not already open.
+ if (impl.socket_ == invalid_socket)
+ {
+ // Get the flags used to create the new socket.
+ int family = peer_endpoint.protocol().family();
+ int type = peer_endpoint.protocol().type();
+ int proto = peer_endpoint.protocol().protocol();
+
+ // Create a new socket.
+ impl.socket_ = socket_ops::socket(family, type, proto);
+ if (impl.socket_ == invalid_socket)
+ {
+ asio::error error(socket_ops::get_error());
+ io_service().post(bind_handler(handler, error));
+ return;
+ }
+ iocp_service_.register_socket(impl.socket_);
+ }
+
+ // Mark the socket as non-blocking so that the connection will take place
+ // asynchronously.
+ ioctl_arg_type non_blocking = 1;
+ if (socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking))
+ {
+ asio::error error(socket_ops::get_error());
+ io_service().post(bind_handler(handler, error));
+ return;
+ }
+
+ // Start the connect operation.
+ if (socket_ops::connect(impl.socket_, peer_endpoint.data(),
+ peer_endpoint.size()) == 0)
+ {
+ // The connect operation has finished successfully so we need to post the
+ // handler immediately.
+ asio::error error(asio::error::success);
+ io_service().post(bind_handler(handler, error));
+ }
+ else if (socket_ops::get_error() == asio::error::in_progress
+ || socket_ops::get_error() == asio::error::would_block)
+ {
+ // The connection is happening in the background, and we need to wait
+ // until the socket becomes writeable.
+ boost::shared_ptr<bool> completed(new bool(false));
+ reactor->start_write_and_except_ops(impl.socket_,
+ connect_handler<Handler>(
+ impl.socket_, completed, io_service(), *reactor, handler));
+ }
+ else
+ {
+ // The connect operation has failed, so post the handler immediately.
+ asio::error error(socket_ops::get_error());
+ io_service().post(bind_handler(handler, error));
+ }
+ }
+
+private:
+ // Helper function to provide InterlockedCompareExchangePointer functionality
+ // on very old Platform SDKs.
+ void* interlocked_compare_exchange_pointer(void** dest, void* exch, void* cmp)
+ {
+#if defined(_WIN32_WINNT) && (_WIN32_WINNT <= 0x400) && (_M_IX86)
+ return reinterpret_cast<void*>(InterlockedCompareExchange(
+ reinterpret_cast<LONG*>(dest), reinterpret_cast<LONG>(exch),
+ reinterpret_cast<LONG>(cmp)));
+#else
+ return InterlockedCompareExchangePointer(dest, exch, cmp);
+#endif
+ }
+
+ // Helper function to provide InterlockedExchangePointer functionality on very
+ // old Platform SDKs.
+ void* interlocked_exchange_pointer(void** dest, void* val)
+ {
+#if defined(_WIN32_WINNT) && (_WIN32_WINNT <= 0x400) && (_M_IX86)
+ return reinterpret_cast<void*>(InterlockedExchange(
+ reinterpret_cast<LONG*>(dest), reinterpret_cast<LONG>(val)));
+#else
+ return InterlockedExchangePointer(dest, val);
+#endif
+ }
+
+ // The IOCP service used for running asynchronous operations and dispatching
+ // handlers.
+ win_iocp_io_service& iocp_service_;
+
+ // The reactor used for performing connect operations. This object is created
+ // only if needed.
+ reactor_type* reactor_;
+
+ // Mutex to protect access to the linked list of implementations.
+ asio::detail::mutex mutex_;
+
+ // The head of a linked list of all implementations.
+ implementation_type* impl_list_;
+};
+
+} // namespace detail
+} // namespace asio
+
+#endif // defined(ASIO_HAS_IOCP)
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_DETAIL_WIN_IOCP_SOCKET_SERVICE_HPP
diff --git a/library/include/libtorrent/asio/detail/win_local_free_on_block_exit.hpp b/library/include/libtorrent/asio/detail/win_local_free_on_block_exit.hpp
new file mode 100644
index 000000000..c909e1af3
--- /dev/null
+++ b/library/include/libtorrent/asio/detail/win_local_free_on_block_exit.hpp
@@ -0,0 +1,59 @@
+//
+// win_local_free_on_block_exit.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_WIN_LOCAL_FREE_ON_BLOCK_EXIT_HPP
+#define ASIO_DETAIL_WIN_LOCAL_FREE_ON_BLOCK_EXIT_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/detail/push_options.hpp"
+#include <boost/config.hpp>
+#include "asio/detail/pop_options.hpp"
+
+#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+
+#include "asio/detail/noncopyable.hpp"
+#include "asio/detail/socket_types.hpp"
+
+namespace asio {
+namespace detail {
+
+class win_local_free_on_block_exit
+ : private noncopyable
+{
+public:
+ // Constructor blocks all signals for the calling thread.
+ explicit win_local_free_on_block_exit(void* p)
+ : p_(p)
+ {
+ }
+
+ // Destructor restores the previous signal mask.
+ ~win_local_free_on_block_exit()
+ {
+ ::LocalFree(p_);
+ }
+
+private:
+ void* p_;
+};
+
+} // namespace detail
+} // namespace asio
+
+#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_DETAIL_WIN_LOCAL_FREE_ON_BLOCK_EXIT_HPP
diff --git a/library/include/libtorrent/asio/detail/win_mutex.hpp b/library/include/libtorrent/asio/detail/win_mutex.hpp
new file mode 100644
index 000000000..9f3dd85c9
--- /dev/null
+++ b/library/include/libtorrent/asio/detail/win_mutex.hpp
@@ -0,0 +1,142 @@
+//
+// win_mutex.hpp
+// ~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_WIN_MUTEX_HPP
+#define ASIO_DETAIL_WIN_MUTEX_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/detail/push_options.hpp"
+#include <boost/config.hpp>
+#include "asio/detail/pop_options.hpp"
+
+#if defined(BOOST_WINDOWS)
+
+#include "asio/system_exception.hpp"
+#include "asio/detail/noncopyable.hpp"
+#include "asio/detail/socket_types.hpp"
+#include "asio/detail/scoped_lock.hpp"
+
+#include "asio/detail/push_options.hpp"
+#include <boost/throw_exception.hpp>
+#include "asio/detail/pop_options.hpp"
+
+namespace asio {
+namespace detail {
+
+class win_mutex
+ : private noncopyable
+{
+public:
+ typedef asio::detail::scoped_lock<win_mutex> scoped_lock;
+
+ // Constructor.
+ win_mutex()
+ {
+ int error = do_init();
+ if (error != 0)
+ {
+ system_exception e("mutex", error);
+ boost::throw_exception(e);
+ }
+ }
+
+ // Destructor.
+ ~win_mutex()
+ {
+ ::DeleteCriticalSection(&crit_section_);
+ }
+
+ // Lock the mutex.
+ void lock()
+ {
+ int error = do_lock();
+ if (error != 0)
+ {
+ system_exception e("mutex", error);
+ boost::throw_exception(e);
+ }
+ }
+
+ // Unlock the mutex.
+ void unlock()
+ {
+ ::LeaveCriticalSection(&crit_section_);
+ }
+
+private:
+ // Initialisation must be performed in a separate function to the constructor
+ // since the compiler does not support the use of structured exceptions and
+ // C++ exceptions in the same function.
+ int do_init()
+ {
+#if defined(__MINGW32__)
+ // Not sure if MinGW supports structured exception handling, so for now
+ // we'll just call the Windows API and hope.
+ ::InitializeCriticalSection(&crit_section_);
+ return 0;
+#else
+ __try
+ {
+ ::InitializeCriticalSection(&crit_section_);
+ }
+ __except(GetExceptionCode() == STATUS_NO_MEMORY
+ ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
+ {
+ return ERROR_OUTOFMEMORY;
+ }
+
+ return 0;
+#endif
+ }
+
+ // Locking must be performed in a separate function to lock() since the
+ // compiler does not support the use of structured exceptions and C++
+ // exceptions in the same function.
+ int do_lock()
+ {
+#if defined(__MINGW32__)
+ // Not sure if MinGW supports structured exception handling, so for now
+ // we'll just call the Windows API and hope.
+ ::EnterCriticalSection(&crit_section_);
+ return 0;
+#else
+ __try
+ {
+ ::EnterCriticalSection(&crit_section_);
+ }
+ __except(GetExceptionCode() == STATUS_INVALID_HANDLE
+ || GetExceptionCode() == STATUS_NO_MEMORY
+ ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
+ {
+ if (GetExceptionCode() == STATUS_NO_MEMORY)
+ return ERROR_OUTOFMEMORY;
+ return ERROR_INVALID_HANDLE;
+ }
+
+ return 0;
+#endif
+ }
+
+ ::CRITICAL_SECTION crit_section_;
+};
+
+} // namespace detail
+} // namespace asio
+
+#endif // defined(BOOST_WINDOWS)
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_DETAIL_WIN_MUTEX_HPP
diff --git a/library/include/libtorrent/asio/detail/win_signal_blocker.hpp b/library/include/libtorrent/asio/detail/win_signal_blocker.hpp
new file mode 100644
index 000000000..a1c2e992b
--- /dev/null
+++ b/library/include/libtorrent/asio/detail/win_signal_blocker.hpp
@@ -0,0 +1,67 @@
+//
+// win_signal_blocker.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_WIN_SIGNAL_BLOCKER_HPP
+#define ASIO_DETAIL_WIN_SIGNAL_BLOCKER_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/detail/push_options.hpp"
+#include <boost/config.hpp>
+#include "asio/detail/pop_options.hpp"
+
+#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+
+#include "asio/detail/noncopyable.hpp"
+
+namespace asio {
+namespace detail {
+
+class win_signal_blocker
+ : private noncopyable
+{
+public:
+ // Constructor blocks all signals for the calling thread.
+ win_signal_blocker()
+ {
+ // No-op.
+ }
+
+ // Destructor restores the previous signal mask.
+ ~win_signal_blocker()
+ {
+ // No-op.
+ }
+
+ // Block all signals for the calling thread.
+ void block()
+ {
+ // No-op.
+ }
+
+ // Restore the previous signal mask.
+ void unblock()
+ {
+ // No-op.
+ }
+};
+
+} // namespace detail
+} // namespace asio
+
+#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_DETAIL_WIN_SIGNAL_BLOCKER_HPP
diff --git a/library/include/libtorrent/asio/detail/win_thread.hpp b/library/include/libtorrent/asio/detail/win_thread.hpp
new file mode 100644
index 000000000..4e6adb880
--- /dev/null
+++ b/library/include/libtorrent/asio/detail/win_thread.hpp
@@ -0,0 +1,121 @@
+//
+// win_thread.hpp
+// ~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_WIN_THREAD_HPP
+#define ASIO_DETAIL_WIN_THREAD_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/detail/push_options.hpp"
+#include <boost/config.hpp>
+#include "asio/detail/pop_options.hpp"
+
+#if defined(BOOST_WINDOWS)
+
+#include "asio/system_exception.hpp"
+#include "asio/detail/noncopyable.hpp"
+#include "asio/detail/socket_types.hpp"
+
+#include "asio/detail/push_options.hpp"
+#include <boost/throw_exception.hpp>
+#include <memory>
+#include <process.h>
+#include "asio/detail/pop_options.hpp"
+
+namespace asio {
+namespace detail {
+
+unsigned int __stdcall win_thread_function(void* arg);
+
+class win_thread
+ : private noncopyable
+{
+public:
+ // Constructor.
+ template <typename Function>
+ win_thread(Function f)
+ {
+ std::auto_ptr<func_base> arg(new func<Function>(f));
+ unsigned int thread_id = 0;
+ thread_ = reinterpret_cast<HANDLE>(::_beginthreadex(0, 0,
+ win_thread_function, arg.get(), 0, &thread_id));
+ if (!thread_)
+ {
+ DWORD last_error = ::GetLastError();
+ system_exception e("thread", last_error);
+ boost::throw_exception(e);
+ }
+ arg.release();
+ }
+
+ // Destructor.
+ ~win_thread()
+ {
+ ::CloseHandle(thread_);
+ }
+
+ // Wait for the thread to exit.
+ void join()
+ {
+ ::WaitForSingleObject(thread_, INFINITE);
+ }
+
+private:
+ friend unsigned int __stdcall win_thread_function(void* arg);
+
+ class func_base
+ {
+ public:
+ virtual ~func_base() {}
+ virtual void run() = 0;
+ };
+
+ template <typename Function>
+ class func
+ : public func_base
+ {
+ public:
+ func(Function f)
+ : f_(f)
+ {
+ }
+
+ virtual void run()
+ {
+ f_();
+ }
+
+ private:
+ Function f_;
+ };
+
+ ::HANDLE thread_;
+};
+
+inline unsigned int __stdcall win_thread_function(void* arg)
+{
+ std::auto_ptr<win_thread::func_base> func(
+ static_cast<win_thread::func_base*>(arg));
+ func->run();
+ return 0;
+}
+
+} // namespace detail
+} // namespace asio
+
+#endif // defined(BOOST_WINDOWS)
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_DETAIL_WIN_THREAD_HPP
diff --git a/library/include/libtorrent/asio/detail/win_tss_ptr.hpp b/library/include/libtorrent/asio/detail/win_tss_ptr.hpp
new file mode 100644
index 000000000..5381094c5
--- /dev/null
+++ b/library/include/libtorrent/asio/detail/win_tss_ptr.hpp
@@ -0,0 +1,85 @@
+//
+// win_tss_ptr.hpp
+// ~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_WIN_TSS_PTR_HPP
+#define ASIO_DETAIL_WIN_TSS_PTR_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/detail/push_options.hpp"
+#include <boost/config.hpp>
+#include "asio/detail/pop_options.hpp"
+
+#if defined(BOOST_WINDOWS)
+
+#include "asio/system_exception.hpp"
+#include "asio/detail/noncopyable.hpp"
+#include "asio/detail/socket_types.hpp"
+
+#include "asio/detail/push_options.hpp"
+#include <boost/throw_exception.hpp>
+#include "asio/detail/pop_options.hpp"
+
+namespace asio {
+namespace detail {
+
+template <typename T>
+class win_tss_ptr
+ : private noncopyable
+{
+public:
+ // Constructor.
+ win_tss_ptr()
+ {
+ tss_key_ = ::TlsAlloc();
+ if (tss_key_ == TLS_OUT_OF_INDEXES)
+ {
+ DWORD last_error = ::GetLastError();
+ system_exception e("tss", last_error);
+ boost::throw_exception(e);
+ }
+ }
+
+ // Destructor.
+ ~win_tss_ptr()
+ {
+ ::TlsFree(tss_key_);
+ }
+
+ // Get the value.
+ operator T*() const
+ {
+ return static_cast<T*>(::TlsGetValue(tss_key_));
+ }
+
+ // Set the value.
+ void operator=(T* value)
+ {
+ ::TlsSetValue(tss_key_, value);
+ }
+
+private:
+ // Thread-specific storage to allow unlocked access to determine whether a
+ // thread is a member of the pool.
+ DWORD tss_key_;
+};
+
+} // namespace detail
+} // namespace asio
+
+#endif // defined(BOOST_WINDOWS)
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_DETAIL_WIN_TSS_PTR_HPP
diff --git a/library/include/libtorrent/asio/detail/winsock_init.hpp b/library/include/libtorrent/asio/detail/winsock_init.hpp
new file mode 100644
index 000000000..7ae66d0e5
--- /dev/null
+++ b/library/include/libtorrent/asio/detail/winsock_init.hpp
@@ -0,0 +1,116 @@
+//
+// winsock_init.hpp
+// ~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_WINSOCK_INIT_HPP
+#define ASIO_DETAIL_WINSOCK_INIT_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/detail/push_options.hpp"
+#include <boost/config.hpp>
+#include "asio/detail/pop_options.hpp"
+
+#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+
+#include "asio/detail/push_options.hpp"
+#include <boost/shared_ptr.hpp>
+#include <boost/throw_exception.hpp>
+#include "asio/detail/pop_options.hpp"
+
+#include "asio/system_exception.hpp"
+#include "asio/detail/noncopyable.hpp"
+#include "asio/detail/socket_types.hpp"
+
+namespace asio {
+namespace detail {
+
+template <int Major = 2, int Minor = 0>
+class winsock_init
+ : private noncopyable
+{
+private:
+ // Structure to perform the actual initialisation.
+ struct do_init
+ {
+ do_init()
+ {
+ WSADATA wsa_data;
+ result_ = ::WSAStartup(MAKEWORD(Major, Minor), &wsa_data);
+ }
+
+ ~do_init()
+ {
+ ::WSACleanup();
+ }
+
+ int result() const
+ {
+ return result_;
+ }
+
+ // Helper function to manage a do_init singleton. The static instance of the
+ // winsock_init object ensures that this function is always called before
+ // main, and therefore before any other threads can get started. The do_init
+ // instance must be static in this function to ensure that it gets
+ // initialised before any other global objects try to use it.
+ static boost::shared_ptr<do_init> instance()
+ {
+ static boost::shared_ptr<do_init> init(new do_init);
+ return init;
+ }
+
+ private:
+ int result_;
+ };
+
+public:
+ // Constructor.
+ winsock_init()
+ : ref_(do_init::instance())
+ {
+ // Check whether winsock was successfully initialised. This check is not
+ // performed for the global instance since there will be nobody around to
+ // catch the exception.
+ if (this != &instance_ && ref_->result() != 0)
+ {
+ system_exception e("winsock", ref_->result());
+ boost::throw_exception(e);
+ }
+ }
+
+ // Destructor.
+ ~winsock_init()
+ {
+ }
+
+private:
+ // Instance to force initialisation of winsock at global scope.
+ static winsock_init instance_;
+
+ // Reference to singleton do_init object to ensure that winsock does not get
+ // cleaned up until the last user has finished with it.
+ boost::shared_ptr<do_init> ref_;
+};
+
+template <int Major, int Minor>
+winsock_init<Major, Minor> winsock_init<Major, Minor>::instance_;
+
+} // namespace detail
+} // namespace asio
+
+#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_DETAIL_WINSOCK_INIT_HPP
diff --git a/library/include/libtorrent/asio/detail/wrapped_handler.hpp b/library/include/libtorrent/asio/detail/wrapped_handler.hpp
new file mode 100644
index 000000000..e77b38dd8
--- /dev/null
+++ b/library/include/libtorrent/asio/detail/wrapped_handler.hpp
@@ -0,0 +1,187 @@
+//
+// wrapped_handler.hpp
+// ~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_WRAPPED_HANDLER_HPP
+#define ASIO_DETAIL_WRAPPED_HANDLER_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/detail/bind_handler.hpp"
+#include "asio/detail/handler_alloc_helpers.hpp"
+#include "asio/detail/handler_invoke_helpers.hpp"
+
+namespace asio {
+namespace detail {
+
+template <typename Dispatcher, typename Handler>
+class wrapped_handler
+{
+public:
+ typedef void result_type;
+
+ wrapped_handler(Dispatcher& dispatcher, Handler handler)
+ : dispatcher_(dispatcher),
+ handler_(handler)
+ {
+ }
+
+ void operator()()
+ {
+ dispatcher_.dispatch(handler_);
+ }
+
+ void operator()() const
+ {
+ dispatcher_.dispatch(handler_);
+ }
+
+ template <typename Arg1>
+ void operator()(const Arg1& arg1)
+ {
+ dispatcher_.dispatch(detail::bind_handler(handler_, arg1));
+ }
+
+ template <typename Arg1>
+ void operator()(const Arg1& arg1) const
+ {
+ dispatcher_.dispatch(detail::bind_handler(handler_, arg1));
+ }
+
+ template <typename Arg1, typename Arg2>
+ void operator()(const Arg1& arg1, const Arg2& arg2)
+ {
+ dispatcher_.dispatch(detail::bind_handler(handler_, arg1, arg2));
+ }
+
+ template <typename Arg1, typename Arg2>
+ void operator()(const Arg1& arg1, const Arg2& arg2) const
+ {
+ dispatcher_.dispatch(detail::bind_handler(handler_, arg1, arg2));
+ }
+
+ template <typename Arg1, typename Arg2, typename Arg3>
+ void operator()(const Arg1& arg1, const Arg2& arg2, const Arg3& arg3)
+ {
+ dispatcher_.dispatch(detail::bind_handler(handler_, arg1, arg2, arg3));
+ }
+
+ template <typename Arg1, typename Arg2, typename Arg3>
+ void operator()(const Arg1& arg1, const Arg2& arg2, const Arg3& arg3) const
+ {
+ dispatcher_.dispatch(detail::bind_handler(handler_, arg1, arg2, arg3));
+ }
+
+ template <typename Arg1, typename Arg2, typename Arg3, typename Arg4>
+ void operator()(const Arg1& arg1, const Arg2& arg2, const Arg3& arg3,
+ const Arg4& arg4)
+ {
+ dispatcher_.dispatch(
+ detail::bind_handler(handler_, arg1, arg2, arg3, arg4));
+ }
+
+ template <typename Arg1, typename Arg2, typename Arg3, typename Arg4>
+ void operator()(const Arg1& arg1, const Arg2& arg2, const Arg3& arg3,
+ const Arg4& arg4) const
+ {
+ dispatcher_.dispatch(
+ detail::bind_handler(handler_, arg1, arg2, arg3, arg4));
+ }
+
+ template <typename Arg1, typename Arg2, typename Arg3, typename Arg4,
+ typename Arg5>
+ void operator()(const Arg1& arg1, const Arg2& arg2, const Arg3& arg3,
+ const Arg4& arg4, const Arg5& arg5)
+ {
+ dispatcher_.dispatch(
+ detail::bind_handler(handler_, arg1, arg2, arg3, arg4, arg5));
+ }
+
+ template <typename Arg1, typename Arg2, typename Arg3, typename Arg4,
+ typename Arg5>
+ void operator()(const Arg1& arg1, const Arg2& arg2, const Arg3& arg3,
+ const Arg4& arg4, const Arg5& arg5) const
+ {
+ dispatcher_.dispatch(
+ detail::bind_handler(handler_, arg1, arg2, arg3, arg4, arg5));
+ }
+
+//private:
+ Dispatcher& dispatcher_;
+ Handler handler_;
+};
+
+template <typename Dispatcher, typename Handler>
+inline void* asio_handler_allocate(std::size_t size,
+ wrapped_handler<Dispatcher, Handler>* this_handler)
+{
+ return asio_handler_alloc_helpers::allocate(
+ size, &this_handler->handler_);
+}
+
+template <typename Dispatcher, typename Handler>
+inline void asio_handler_deallocate(void* pointer, std::size_t size,
+ wrapped_handler<Dispatcher, Handler>* this_handler)
+{
+ asio_handler_alloc_helpers::deallocate(
+ pointer, size, &this_handler->handler_);
+}
+
+template <typename Handler, typename Context>
+class rewrapped_handler
+{
+public:
+ explicit rewrapped_handler(const Handler& handler, const Context& context)
+ : handler_(handler),
+ context_(context)
+ {
+ }
+
+ void operator()()
+ {
+ handler_();
+ }
+
+ void operator()() const
+ {
+ handler_();
+ }
+
+//private:
+ Handler handler_;
+ Context context_;
+};
+
+template <typename Function, typename Dispatcher, typename Handler>
+inline void asio_handler_invoke(const Function& function,
+ wrapped_handler<Dispatcher, Handler>* this_handler)
+{
+ this_handler->dispatcher_.dispatch(
+ rewrapped_handler<Function, Handler>(
+ function, this_handler->handler_));
+}
+
+template <typename Function, typename Dispatcher, typename Handler>
+inline void asio_handler_invoke(const Function& function,
+ rewrapped_handler<Dispatcher, Handler>* this_handler)
+{
+ asio_handler_invoke_helpers::invoke(
+ function, &this_handler->context_);
+}
+
+} // namespace detail
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_DETAIL_WRAPPED_HANDLER_HPP
diff --git a/library/include/libtorrent/asio/error.hpp b/library/include/libtorrent/asio/error.hpp
new file mode 100644
index 000000000..c52df6cd8
--- /dev/null
+++ b/library/include/libtorrent/asio/error.hpp
@@ -0,0 +1,387 @@
+//
+// error.hpp
+// ~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_ERROR_HPP
+#define ASIO_ERROR_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/detail/push_options.hpp"
+#include <boost/config.hpp>
+#include <boost/scoped_ptr.hpp>
+#include <cerrno>
+#include <cstring>
+#include <exception>
+#include <string>
+#include <boost/detail/workaround.hpp>
+#if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564))
+# include <iostream>
+#endif // BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564))
+#include "asio/detail/pop_options.hpp"
+
+#include "asio/detail/socket_types.hpp"
+#include "asio/detail/win_local_free_on_block_exit.hpp"
+
+namespace asio {
+
+#if defined(GENERATING_DOCUMENTATION)
+/// INTERNAL ONLY.
+# define ASIO_SOCKET_ERROR(e) implementation_defined
+/// INTERNAL ONLY.
+# define ASIO_NETDB_ERROR(e) implementation_defined
+/// INTERNAL ONLY.
+# define ASIO_GETADDRINFO_ERROR(e) implementation_defined
+/// INTERNAL ONLY.
+# define ASIO_OS_ERROR(e_win, e_posix) implementation_defined
+#elif defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+# define ASIO_SOCKET_ERROR(e) WSA ## e
+# define ASIO_NETDB_ERROR(e) WSA ## e
+# define ASIO_GETADDRINFO_ERROR(e) e
+# define ASIO_OS_ERROR(e_win, e_posix) e_win
+#else
+# define ASIO_SOCKET_ERROR(e) e
+# define ASIO_NETDB_ERROR(e) 16384 + e
+# define ASIO_GETADDRINFO_ERROR(e) 32768 + e
+# define ASIO_OS_ERROR(e_win, e_posix) e_posix
+#endif
+
+/// The error class is used to encapsulate system error codes.
+class error
+ : public std::exception
+{
+public:
+ /// Error codes.
+ enum code_type
+ {
+ /// Permission denied.
+ access_denied = ASIO_SOCKET_ERROR(EACCES),
+
+ /// Address family not supported by protocol.
+ address_family_not_supported = ASIO_SOCKET_ERROR(EAFNOSUPPORT),
+
+ /// Address already in use.
+ address_in_use = ASIO_SOCKET_ERROR(EADDRINUSE),
+
+ /// Transport endpoint is already connected.
+ already_connected = ASIO_SOCKET_ERROR(EISCONN),
+
+ /// Operation already in progress.
+ already_started = ASIO_SOCKET_ERROR(EALREADY),
+
+ /// A connection has been aborted.
+ connection_aborted = ASIO_SOCKET_ERROR(ECONNABORTED),
+
+ /// Connection refused.
+ connection_refused = ASIO_SOCKET_ERROR(ECONNREFUSED),
+
+ /// Connection reset by peer.
+ connection_reset = ASIO_SOCKET_ERROR(ECONNRESET),
+
+ /// Bad file descriptor.
+ bad_descriptor = ASIO_SOCKET_ERROR(EBADF),
+
+ /// End of file or stream.
+ eof = ASIO_OS_ERROR(ERROR_HANDLE_EOF, -1),
+
+ /// Bad address.
+ fault = ASIO_SOCKET_ERROR(EFAULT),
+
+ /// Host not found (authoritative).
+ host_not_found = ASIO_NETDB_ERROR(HOST_NOT_FOUND),
+
+ /// Host not found (non-authoritative).
+ host_not_found_try_again = ASIO_NETDB_ERROR(TRY_AGAIN),
+
+ /// No route to host.
+ host_unreachable = ASIO_SOCKET_ERROR(EHOSTUNREACH),
+
+ /// Operation now in progress.
+ in_progress = ASIO_SOCKET_ERROR(EINPROGRESS),
+
+ /// Interrupted system call.
+ interrupted = ASIO_SOCKET_ERROR(EINTR),
+
+ /// Invalid argument.
+ invalid_argument = ASIO_SOCKET_ERROR(EINVAL),
+
+ /// Message too long.
+ message_size = ASIO_SOCKET_ERROR(EMSGSIZE),
+
+ /// Network is down.
+ network_down = ASIO_SOCKET_ERROR(ENETDOWN),
+
+ /// Network dropped connection on reset.
+ network_reset = ASIO_SOCKET_ERROR(ENETRESET),
+
+ /// Network is unreachable.
+ network_unreachable = ASIO_SOCKET_ERROR(ENETUNREACH),
+
+ /// Too many open files.
+ no_descriptors = ASIO_SOCKET_ERROR(EMFILE),
+
+ /// No buffer space available.
+ no_buffer_space = ASIO_SOCKET_ERROR(ENOBUFS),
+
+ /// The query is valid but does not have associated address data.
+ no_data = ASIO_NETDB_ERROR(NO_DATA),
+
+ /// Cannot allocate memory.
+ no_memory = ASIO_OS_ERROR(ERROR_OUTOFMEMORY, ENOMEM),
+
+ /// Operation not permitted.
+ no_permission = ASIO_OS_ERROR(ERROR_ACCESS_DENIED, EPERM),
+
+ /// Protocol not available.
+ no_protocol_option = ASIO_SOCKET_ERROR(ENOPROTOOPT),
+
+ /// A non-recoverable error occurred.
+ no_recovery = ASIO_NETDB_ERROR(NO_RECOVERY),
+
+ /// Transport endpoint is not connected.
+ not_connected = ASIO_SOCKET_ERROR(ENOTCONN),
+
+ /// Socket operation on non-socket.
+ not_socket = ASIO_SOCKET_ERROR(ENOTSOCK),
+
+ /// Operation not supported.
+ not_supported = ASIO_SOCKET_ERROR(EOPNOTSUPP),
+
+ /// Operation cancelled.
+ operation_aborted = ASIO_OS_ERROR(ERROR_OPERATION_ABORTED, ECANCELED),
+
+ /// The service is not supported for the given socket type.
+ service_not_found = ASIO_OS_ERROR(
+ WSATYPE_NOT_FOUND,
+ ASIO_GETADDRINFO_ERROR(EAI_SERVICE)),
+
+ /// The socket type is not supported.
+ socket_type_not_supported = ASIO_OS_ERROR(
+ WSAESOCKTNOSUPPORT,
+ ASIO_GETADDRINFO_ERROR(EAI_SOCKTYPE)),
+
+ /// Cannot send after transport endpoint shutdown.
+ shut_down = ASIO_SOCKET_ERROR(ESHUTDOWN),
+
+ /// Success.
+ success = 0,
+
+ /// Connection timed out.
+ timed_out = ASIO_SOCKET_ERROR(ETIMEDOUT),
+
+ /// Resource temporarily unavailable.
+ try_again = ASIO_OS_ERROR(ERROR_RETRY, EAGAIN),
+
+ /// The socket is marked non-blocking and the requested operation would
+ /// block.
+ would_block = ASIO_SOCKET_ERROR(EWOULDBLOCK)
+ };
+
+ /// Default constructor.
+ error()
+ : code_(success)
+ {
+ }
+
+ /// Construct with a specific error code.
+ error(int code)
+ : code_(code)
+ {
+ }
+
+ /// Copy constructor.
+ error(const error& e)
+ : std::exception(e),
+ code_(e.code_)
+ {
+ }
+
+ /// Destructor.
+ virtual ~error() throw ()
+ {
+ }
+
+ /// Assignment operator.
+ error& operator=(const error& e)
+ {
+ code_ = e.code_;
+ what_.reset();
+ return *this;
+ }
+
+ /// Get a string representation of the exception.
+ virtual const char* what() const throw ()
+ {
+#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+ try
+ {
+ if (!what_)
+ {
+ char* msg = 0;
+ DWORD length = ::FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER
+ | FORMAT_MESSAGE_FROM_SYSTEM
+ | FORMAT_MESSAGE_IGNORE_INSERTS, 0, code_,
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (char*)&msg, 0, 0);
+ detail::win_local_free_on_block_exit local_free_obj(msg);
+ if (length && msg[length - 1] == '\n')
+ msg[--length] = '\0';
+ if (length && msg[length - 1] == '\r')
+ msg[--length] = '\0';
+ if (length)
+ what_.reset(new std::string(msg));
+ else
+ return "asio error";
+ }
+ return what_->c_str();
+ }
+ catch (std::exception&)
+ {
+ return "asio error";
+ }
+#else // defined(BOOST_WINDOWS)
+ switch (code_)
+ {
+ case error::eof:
+ return "End of file.";
+ case error::host_not_found:
+ return "Host not found (authoritative).";
+ case error::host_not_found_try_again:
+ return "Host not found (non-authoritative), try again later.";
+ case error::no_recovery:
+ return "A non-recoverable error occurred during database lookup.";
+ case error::no_data:
+ return "The query is valid, but it does not have associated data.";
+#if !defined(__sun)
+ case error::operation_aborted:
+ return "Operation aborted.";
+#endif // !defined(__sun)
+ case error::service_not_found:
+ return "Service not found.";
+ case error::socket_type_not_supported:
+ return "Socket type not supported.";
+ default:
+#if defined(__sun) || defined(__QNX__)
+ return strerror(code_);
+#elif defined(__MACH__) && defined(__APPLE__) \
+ || defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__)
+ try
+ {
+ char buf[256] = "";
+ strerror_r(code_, buf, sizeof(buf));
+ what_.reset(new std::string(buf));
+ return what_->c_str();
+ }
+ catch (std::exception&)
+ {
+ return "asio error";
+ }
+#else
+ try
+ {
+ char buf[256] = "";
+ what_.reset(new std::string(strerror_r(code_, buf, sizeof(buf))));
+ return what_->c_str();
+ }
+ catch (std::exception&)
+ {
+ return "asio error";
+ }
+#endif
+ }
+#endif // defined(BOOST_WINDOWS)
+ }
+
+ /// Get the code associated with the error.
+ int code() const
+ {
+ return code_;
+ }
+
+ struct unspecified_bool_type_t
+ {
+ };
+
+ typedef unspecified_bool_type_t* unspecified_bool_type;
+
+ /// Operator returns non-null if there is a non-success error code.
+ operator unspecified_bool_type() const
+ {
+ if (code_ == success)
+ return 0;
+ else
+ return reinterpret_cast<unspecified_bool_type>(1);
+ }
+
+ /// Operator to test if the error represents success.
+ bool operator!() const
+ {
+ return code_ == success;
+ }
+
+ /// Equality operator to compare two error objects.
+ friend bool operator==(const error& e1, const error& e2)
+ {
+ return e1.code_ == e2.code_;
+ }
+
+ /// Inequality operator to compare two error objects.
+ friend bool operator!=(const error& e1, const error& e2)
+ {
+ return e1.code_ != e2.code_;
+ }
+
+private:
+ // The code associated with the error.
+ int code_;
+
+ // The string representation of the error.
+ mutable boost::scoped_ptr<std::string> what_;
+};
+
+/// Output the string associated with an error.
+/**
+ * Used to output a human-readable string that is associated with an error.
+ *
+ * @param os The output stream to which the string will be written.
+ *
+ * @param e The error to be written.
+ *
+ * @return The output stream.
+ *
+ * @relates asio::error
+ */
+#if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564))
+std::ostream& operator<<(std::ostream& os, const error& e)
+{
+ os << e.what();
+ return os;
+}
+#else // BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564))
+template <typename Ostream>
+Ostream& operator<<(Ostream& os, const error& e)
+{
+ os << e.what();
+ return os;
+}
+#endif // BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564))
+
+} // namespace asio
+
+#undef ASIO_SOCKET_ERROR
+#undef ASIO_NETDB_ERROR
+#undef ASIO_GETADDRINFO_ERROR
+#undef ASIO_OS_ERROR
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_ERROR_HPP
diff --git a/library/include/libtorrent/asio/error_handler.hpp b/library/include/libtorrent/asio/error_handler.hpp
new file mode 100644
index 000000000..c315c8d5e
--- /dev/null
+++ b/library/include/libtorrent/asio/error_handler.hpp
@@ -0,0 +1,120 @@
+//
+// error_handler.hpp
+// ~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_ERROR_HANDLER_HPP
+#define ASIO_ERROR_HANDLER_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/detail/push_options.hpp"
+#include <boost/throw_exception.hpp>
+#include "asio/detail/pop_options.hpp"
+
+namespace asio {
+
+namespace detail {
+
+class ignore_error_t
+{
+public:
+ typedef void result_type;
+
+ template <typename Error>
+ void operator()(const Error&) const
+ {
+ }
+};
+
+class throw_error_t
+{
+public:
+ typedef void result_type;
+
+ template <typename Error>
+ void operator()(const Error& err) const
+ {
+ if (err)
+ boost::throw_exception(err);
+ }
+};
+
+template <typename Target>
+class assign_error_t
+{
+public:
+ typedef void result_type;
+
+ assign_error_t(Target& target)
+ : target_(&target)
+ {
+ }
+
+ template <typename Error>
+ void operator()(const Error& err) const
+ {
+ *target_ = err;
+ }
+
+private:
+ Target* target_;
+};
+
+} // namespace detail
+
+/**
+ * @defgroup error_handler Error Handler Function Objects
+ *
+ * Function objects for custom error handling.
+ */
+/*@{*/
+
+/// Return a function object that always ignores the error.
+#if defined(GENERATING_DOCUMENTATION)
+unspecified ignore_error();
+#else
+inline detail::ignore_error_t ignore_error()
+{
+ return detail::ignore_error_t();
+}
+#endif
+
+/// Return a function object that always throws the error.
+#if defined(GENERATING_DOCUMENTATION)
+unspecified throw_error();
+#else
+inline detail::throw_error_t throw_error()
+{
+ return detail::throw_error_t();
+}
+#endif
+
+/// Return a function object that assigns the error to a variable.
+#if defined(GENERATING_DOCUMENTATION)
+template <typename Target>
+unspecified assign_error(Target& target);
+#else
+template <typename Target>
+inline detail::assign_error_t<Target> assign_error(Target& target)
+{
+ return detail::assign_error_t<Target>(target);
+}
+#endif
+
+/*@}*/
+
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_ERROR_HANDLER_HPP
diff --git a/library/include/libtorrent/asio/handler_alloc_hook.hpp b/library/include/libtorrent/asio/handler_alloc_hook.hpp
new file mode 100644
index 000000000..d15c85061
--- /dev/null
+++ b/library/include/libtorrent/asio/handler_alloc_hook.hpp
@@ -0,0 +1,88 @@
+//
+// handler_alloc_hook.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_HANDLER_ALLOC_HOOK_HPP
+#define ASIO_HANDLER_ALLOC_HOOK_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/detail/push_options.hpp"
+#include <cstddef>
+#include <boost/config.hpp>
+#include "asio/detail/pop_options.hpp"
+
+namespace asio {
+
+/// Default allocation function for handlers.
+/**
+ * Asynchronous operations may need to allocate temporary objects. Since
+ * asynchronous operations have a handler function object, these temporary
+ * objects can be said to be associated with the handler.
+ *
+ * Implement asio_handler_allocate and asio_handler_deallocate for your own
+ * handlers to provide custom allocation for these temporary objects.
+ *
+ * This default implementation is simply:
+ * @code
+ * return ::operator new(bytes);
+ * @endcode
+ *
+ * @note All temporary objects associated with a handler will be deallocated
+ * before the upcall to the handler is performed. This allows the same memory to
+ * be reused for a subsequent asynchronous operation initiated by the handler.
+ *
+ * @par Example:
+ * @code
+ * class my_handler;
+ *
+ * void* asio_handler_allocate(std::size_t size, my_handler* context)
+ * {
+ * return ::operator new(size);
+ * }
+ *
+ * void asio_handler_deallocate(void* pointer, std::size_t size,
+ * my_handler* context)
+ * {
+ * ::operator delete(pointer);
+ * }
+ * @endcode
+ */
+inline void* asio_handler_allocate(std::size_t size, ...)
+{
+ return ::operator new(size);
+}
+
+/// Default deallocation function for handlers.
+/**
+ * Implement asio_handler_allocate and asio_handler_deallocate for your own
+ * handlers to provide custom allocation for the associated temporary objects.
+ *
+ * This default implementation is simply:
+ * @code
+ * ::operator delete(pointer);
+ * @endcode
+ *
+ * @sa asio_handler_allocate.
+ */
+inline void asio_handler_deallocate(void* pointer, std::size_t size, ...)
+{
+ (void)(size);
+ return ::operator delete(pointer);
+}
+
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_HANDLER_ALLOC_HOOK_HPP
diff --git a/library/include/libtorrent/asio/handler_invoke_hook.hpp b/library/include/libtorrent/asio/handler_invoke_hook.hpp
new file mode 100644
index 000000000..124b76e20
--- /dev/null
+++ b/library/include/libtorrent/asio/handler_invoke_hook.hpp
@@ -0,0 +1,69 @@
+//
+// handler_invoke_hook.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_HANDLER_INVOKE_HOOK_HPP
+#define ASIO_HANDLER_INVOKE_HOOK_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+namespace asio {
+
+/// Default invoke function for handlers.
+/**
+ * Completion handlers for asynchronous operations are invoked by the
+ * io_service associated with the corresponding object (e.g. a socket or
+ * deadline_timer). Certain guarantees are made on when the handler may be
+ * invoked, in particular that a handler can only be invoked from a thread that
+ * is currently calling asio::io_service::run() on the corresponding
+ * io_service object. Handlers may subsequently be invoked through other
+ * objects (such as asio::strand objects) that provide additional
+ * guarantees.
+ *
+ * When asynchronous operations are composed from other asynchronous
+ * operations, all intermediate handlers should be invoked using the same
+ * method as the final handler. This is required to ensure that user-defined
+ * objects are not accessed in a way that may violate the guarantees. This
+ * hooking function ensures that the invoked method used for the final handler
+ * is accessible at each intermediate step.
+ *
+ * Implement asio_handler_invoke for your own handlers to specify a custom
+ * invocation strategy.
+ *
+ * This default implementation is simply:
+ * @code
+ * function();
+ * @endcode
+ *
+ * @par Example:
+ * @code
+ * class my_handler;
+ *
+ * template <typename Function>
+ * void asio_handler_invoke(Function function, my_handler* context)
+ * {
+ * context->strand_.dispatch(function);
+ * }
+ * @endcode
+ */
+template <typename Function>
+inline void asio_handler_invoke(Function function, ...)
+{
+ function();
+}
+
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_HANDLER_INVOKE_HOOK_HPP
diff --git a/library/include/libtorrent/asio/impl/io_service.ipp b/library/include/libtorrent/asio/impl/io_service.ipp
new file mode 100644
index 000000000..5e017831b
--- /dev/null
+++ b/library/include/libtorrent/asio/impl/io_service.ipp
@@ -0,0 +1,150 @@
+//
+// io_service.ipp
+// ~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_IO_SERVICE_IPP
+#define ASIO_IO_SERVICE_IPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/detail/epoll_reactor.hpp"
+#include "asio/detail/kqueue_reactor.hpp"
+#include "asio/detail/select_reactor.hpp"
+#include "asio/detail/task_io_service.hpp"
+#include "asio/detail/win_iocp_io_service.hpp"
+
+namespace asio {
+
+inline io_service::io_service()
+ : service_registry_(*this),
+ impl_(service_registry_.use_service<impl_type>())
+{
+}
+
+inline size_t io_service::run()
+{
+ return impl_.run();
+}
+
+inline size_t io_service::run_one()
+{
+ return impl_.run_one();
+}
+
+inline size_t io_service::poll()
+{
+ return impl_.poll();
+}
+
+inline size_t io_service::poll_one()
+{
+ return impl_.poll_one();
+}
+
+inline void io_service::interrupt()
+{
+ impl_.interrupt();
+}
+
+inline void io_service::reset()
+{
+ impl_.reset();
+}
+
+template <typename Handler>
+inline void io_service::dispatch(Handler handler)
+{
+ impl_.dispatch(handler);
+}
+
+template <typename Handler>
+inline void io_service::post(Handler handler)
+{
+ impl_.post(handler);
+}
+
+template <typename Handler>
+#if defined(GENERATING_DOCUMENTATION)
+unspecified
+#else
+inline detail::wrapped_handler<io_service, Handler>
+#endif
+io_service::wrap(Handler handler)
+{
+ return detail::wrapped_handler<io_service, Handler>(*this, handler);
+}
+
+inline io_service::work::work(asio::io_service& io_service)
+ : io_service_(io_service)
+{
+ io_service_.impl_.work_started();
+}
+
+inline io_service::work::work(const work& other)
+ : io_service_(other.io_service_)
+{
+ io_service_.impl_.work_started();
+}
+
+inline io_service::work::~work()
+{
+ io_service_.impl_.work_finished();
+}
+
+inline asio::io_service& io_service::work::io_service()
+{
+ return io_service_;
+}
+
+inline io_service::service::service(asio::io_service& owner)
+ : owner_(owner),
+ type_info_(0),
+ next_(0)
+{
+}
+
+inline io_service::service::~service()
+{
+}
+
+inline asio::io_service& io_service::service::io_service()
+{
+ return owner_;
+}
+
+template <typename Service>
+inline Service& use_service(io_service& ios)
+{
+ return ios.service_registry_.template use_service<Service>();
+}
+
+template <typename Service>
+void add_service(io_service& ios, Service* svc)
+{
+ if (&ios != &svc->io_service())
+ boost::throw_exception(invalid_service_owner());
+ if (!ios.service_registry_.template add_service<Service>(svc))
+ boost::throw_exception(service_already_exists());
+}
+
+template <typename Service>
+bool has_service(io_service& ios)
+{
+ return ios.service_registry_.template has_service<Service>();
+}
+
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_IO_SERVICE_IPP
diff --git a/library/include/libtorrent/asio/impl/read.ipp b/library/include/libtorrent/asio/impl/read.ipp
new file mode 100644
index 000000000..dece26985
--- /dev/null
+++ b/library/include/libtorrent/asio/impl/read.ipp
@@ -0,0 +1,294 @@
+//
+// read.ipp
+// ~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_READ_IPP
+#define ASIO_READ_IPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/buffer.hpp"
+#include "asio/completion_condition.hpp"
+#include "asio/error_handler.hpp"
+#include "asio/detail/bind_handler.hpp"
+#include "asio/detail/consuming_buffers.hpp"
+#include "asio/detail/handler_alloc_helpers.hpp"
+#include "asio/detail/handler_invoke_helpers.hpp"
+
+namespace asio {
+
+template <typename Sync_Read_Stream, typename Mutable_Buffers,
+ typename Completion_Condition, typename Error_Handler>
+std::size_t read(Sync_Read_Stream& s, const Mutable_Buffers& buffers,
+ Completion_Condition completion_condition, Error_Handler error_handler)
+{
+ asio::detail::consuming_buffers<
+ mutable_buffer, Mutable_Buffers> tmp(buffers);
+ std::size_t total_transferred = 0;
+ while (tmp.begin() != tmp.end())
+ {
+ typename Sync_Read_Stream::error_type e;
+ std::size_t bytes_transferred = s.read_some(tmp, assign_error(e));
+ tmp.consume(bytes_transferred);
+ total_transferred += bytes_transferred;
+ if (completion_condition(e, total_transferred))
+ {
+ error_handler(e);
+ return total_transferred;
+ }
+ }
+ typename Sync_Read_Stream::error_type e;
+ error_handler(e);
+ return total_transferred;
+}
+
+template <typename Sync_Read_Stream, typename Mutable_Buffers>
+inline std::size_t read(Sync_Read_Stream& s, const Mutable_Buffers& buffers)
+{
+ return read(s, buffers, transfer_all(), throw_error());
+}
+
+template <typename Sync_Read_Stream, typename Mutable_Buffers,
+ typename Completion_Condition>
+inline std::size_t read(Sync_Read_Stream& s, const Mutable_Buffers& buffers,
+ Completion_Condition completion_condition)
+{
+ return read(s, buffers, completion_condition, throw_error());
+}
+
+template <typename Sync_Read_Stream, typename Allocator,
+ typename Completion_Condition, typename Error_Handler>
+std::size_t read(Sync_Read_Stream& s,
+ asio::basic_streambuf<Allocator>& b,
+ Completion_Condition completion_condition, Error_Handler error_handler)
+{
+ std::size_t total_transferred = 0;
+ for (;;)
+ {
+ typename Sync_Read_Stream::error_type e;
+ std::size_t bytes_transferred = s.read_some(
+ b.prepare(512), assign_error(e));
+ b.commit(bytes_transferred);
+ total_transferred += bytes_transferred;
+ if (completion_condition(e, total_transferred))
+ {
+ error_handler(e);
+ return total_transferred;
+ }
+ }
+}
+
+template <typename Sync_Read_Stream, typename Allocator>
+inline std::size_t read(Sync_Read_Stream& s,
+ asio::basic_streambuf<Allocator>& b)
+{
+ return read(s, b, transfer_all(), throw_error());
+}
+
+template <typename Sync_Read_Stream, typename Allocator,
+ typename Completion_Condition>
+inline std::size_t read(Sync_Read_Stream& s,
+ asio::basic_streambuf<Allocator>& b,
+ Completion_Condition completion_condition)
+{
+ return read(s, b, completion_condition, throw_error());
+}
+
+namespace detail
+{
+ template <typename Async_Read_Stream, typename Mutable_Buffers,
+ typename Completion_Condition, typename Handler>
+ class read_handler
+ {
+ public:
+ read_handler(Async_Read_Stream& stream, const Mutable_Buffers& buffers,
+ Completion_Condition completion_condition, Handler handler)
+ : stream_(stream),
+ buffers_(buffers),
+ total_transferred_(0),
+ completion_condition_(completion_condition),
+ handler_(handler)
+ {
+ }
+
+ void operator()(const typename Async_Read_Stream::error_type& e,
+ std::size_t bytes_transferred)
+ {
+ total_transferred_ += bytes_transferred;
+ buffers_.consume(bytes_transferred);
+ if (completion_condition_(e, total_transferred_)
+ || buffers_.begin() == buffers_.end())
+ {
+ handler_(e, total_transferred_);
+ }
+ else
+ {
+ stream_.async_read_some(buffers_, *this);
+ }
+ }
+
+ //private:
+ Async_Read_Stream& stream_;
+ asio::detail::consuming_buffers<
+ mutable_buffer, Mutable_Buffers> buffers_;
+ std::size_t total_transferred_;
+ Completion_Condition completion_condition_;
+ Handler handler_;
+ };
+
+ template <typename Async_Read_Stream, typename Mutable_Buffers,
+ typename Completion_Condition, typename Handler>
+ inline void* asio_handler_allocate(std::size_t size,
+ read_handler<Async_Read_Stream, Mutable_Buffers,
+ Completion_Condition, Handler>* this_handler)
+ {
+ return asio_handler_alloc_helpers::allocate(
+ size, &this_handler->handler_);
+ }
+
+ template <typename Async_Read_Stream, typename Mutable_Buffers,
+ typename Completion_Condition, typename Handler>
+ inline void asio_handler_deallocate(void* pointer, std::size_t size,
+ read_handler<Async_Read_Stream, Mutable_Buffers,
+ Completion_Condition, Handler>* this_handler)
+ {
+ asio_handler_alloc_helpers::deallocate(
+ pointer, size, &this_handler->handler_);
+ }
+
+ template <typename Function, typename Async_Read_Stream,
+ typename Mutable_Buffers, typename Completion_Condition, typename Handler>
+ inline void asio_handler_invoke(const Function& function,
+ read_handler<Async_Read_Stream, Mutable_Buffers,
+ Completion_Condition, Handler>* this_handler)
+ {
+ asio_handler_invoke_helpers::invoke(
+ function, &this_handler->handler_);
+ }
+} // namespace detail
+
+template <typename Async_Read_Stream, typename Mutable_Buffers,
+ typename Completion_Condition, typename Handler>
+inline void async_read(Async_Read_Stream& s, const Mutable_Buffers& buffers,
+ Completion_Condition completion_condition, Handler handler)
+{
+ s.async_read_some(buffers,
+ detail::read_handler<Async_Read_Stream, Mutable_Buffers,
+ Completion_Condition, Handler>(
+ s, buffers, completion_condition, handler));
+}
+
+template <typename Async_Read_Stream, typename Mutable_Buffers,
+ typename Handler>
+inline void async_read(Async_Read_Stream& s, const Mutable_Buffers& buffers,
+ Handler handler)
+{
+ async_read(s, buffers, transfer_all(), handler);
+}
+
+namespace detail
+{
+ template <typename Async_Read_Stream, typename Allocator,
+ typename Completion_Condition, typename Handler>
+ class read_streambuf_handler
+ {
+ public:
+ read_streambuf_handler(Async_Read_Stream& stream,
+ basic_streambuf<Allocator>& streambuf,
+ Completion_Condition completion_condition, Handler handler)
+ : stream_(stream),
+ streambuf_(streambuf),
+ total_transferred_(0),
+ completion_condition_(completion_condition),
+ handler_(handler)
+ {
+ }
+
+ void operator()(const typename Async_Read_Stream::error_type& e,
+ std::size_t bytes_transferred)
+ {
+ total_transferred_ += bytes_transferred;
+ streambuf_.commit(bytes_transferred);
+ if (completion_condition_(e, total_transferred_))
+ {
+ handler_(e, total_transferred_);
+ }
+ else
+ {
+ stream_.async_read_some(streambuf_.prepare(512), *this);
+ }
+ }
+
+ //private:
+ Async_Read_Stream& stream_;
+ asio::basic_streambuf<Allocator>& streambuf_;
+ std::size_t total_transferred_;
+ Completion_Condition completion_condition_;
+ Handler handler_;
+ };
+
+ template <typename Async_Read_Stream, typename Allocator,
+ typename Completion_Condition, typename Handler>
+ inline void* asio_handler_allocate(std::size_t size,
+ read_streambuf_handler<Async_Read_Stream, Allocator,
+ Completion_Condition, Handler>* this_handler)
+ {
+ return asio_handler_alloc_helpers::allocate(
+ size, &this_handler->handler_);
+ }
+
+ template <typename Async_Read_Stream, typename Allocator,
+ typename Completion_Condition, typename Handler>
+ inline void asio_handler_deallocate(void* pointer, std::size_t size,
+ read_streambuf_handler<Async_Read_Stream, Allocator,
+ Completion_Condition, Handler>* this_handler)
+ {
+ asio_handler_alloc_helpers::deallocate(
+ pointer, size, &this_handler->handler_);
+ }
+
+ template <typename Function, typename Async_Read_Stream,
+ typename Allocator, typename Completion_Condition, typename Handler>
+ inline void asio_handler_invoke(const Function& function,
+ read_streambuf_handler<Async_Read_Stream, Allocator,
+ Completion_Condition, Handler>* this_handler)
+ {
+ asio_handler_invoke_helpers::invoke(
+ function, &this_handler->handler_);
+ }
+} // namespace detail
+
+template <typename Async_Read_Stream, typename Allocator,
+ typename Completion_Condition, typename Handler>
+inline void async_read(Async_Read_Stream& s,
+ asio::basic_streambuf<Allocator>& b,
+ Completion_Condition completion_condition, Handler handler)
+{
+ s.async_read_some(b.prepare(512),
+ detail::read_streambuf_handler<Async_Read_Stream, Allocator,
+ Completion_Condition, Handler>(
+ s, b, completion_condition, handler));
+}
+
+template <typename Async_Read_Stream, typename Allocator, typename Handler>
+inline void async_read(Async_Read_Stream& s,
+ asio::basic_streambuf<Allocator>& b, Handler handler)
+{
+ async_read(s, b, transfer_all(), handler);
+}
+
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_READ_IPP
diff --git a/library/include/libtorrent/asio/impl/read_until.ipp b/library/include/libtorrent/asio/impl/read_until.ipp
new file mode 100644
index 000000000..accb8d955
--- /dev/null
+++ b/library/include/libtorrent/asio/impl/read_until.ipp
@@ -0,0 +1,664 @@
+//
+// read_until.ipp
+// ~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_READ_UNTIL_IPP
+#define ASIO_READ_UNTIL_IPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/detail/push_options.hpp"
+#include <algorithm>
+#include <limits>
+#include <string>
+#include <utility>
+#include "asio/detail/pop_options.hpp"
+
+#include "asio/buffer.hpp"
+#include "asio/error_handler.hpp"
+#include "asio/detail/bind_handler.hpp"
+#include "asio/detail/const_buffers_iterator.hpp"
+#include "asio/detail/handler_alloc_helpers.hpp"
+#include "asio/detail/handler_invoke_helpers.hpp"
+
+namespace asio {
+
+template <typename Sync_Read_Stream, typename Allocator>
+inline std::size_t read_until(Sync_Read_Stream& s,
+ asio::basic_streambuf<Allocator>& b, char delim)
+{
+ return read_until(s, b, delim, throw_error());
+}
+
+template <typename Sync_Read_Stream, typename Allocator, typename Error_Handler>
+std::size_t read_until(Sync_Read_Stream& s,
+ asio::basic_streambuf<Allocator>& b, char delim,
+ Error_Handler error_handler)
+{
+ std::size_t next_search_start = 0;
+ for (;;)
+ {
+ // Determine the range of the data to be searched.
+ typedef typename asio::basic_streambuf<
+ Allocator>::const_buffers_type const_buffers_type;
+ typedef asio::detail::const_buffers_iterator<
+ const_buffers_type> iterator;
+ const_buffers_type buffers = b.data();
+ iterator begin(buffers, next_search_start);
+ iterator end(buffers, (std::numeric_limits<std::size_t>::max)());
+
+ // Look for a match.
+ iterator iter = std::find(begin, end, delim);
+ if (iter != end)
+ {
+ // Found a match. We're done.
+ return iter.position() + 1;
+ }
+ else
+ {
+ // No match. Next search can start with the new data.
+ next_search_start = end.position();
+ }
+
+ // Need more data.
+ typename Sync_Read_Stream::error_type error;
+ b.commit(s.read_some(b.prepare(512), asio::assign_error(error)));
+ if (error)
+ {
+ error_handler(error);
+ return 0;
+ }
+ }
+}
+
+template <typename Sync_Read_Stream, typename Allocator>
+inline std::size_t read_until(Sync_Read_Stream& s,
+ asio::basic_streambuf<Allocator>& b, const std::string& delim)
+{
+ return read_until(s, b, delim, throw_error());
+}
+
+namespace detail
+{
+ // Algorithm that finds a subsequence of equal values in a sequence. Returns
+ // (iterator,true) if a full match was found, in which case the iterator
+ // points to the beginning of the match. Returns (iterator,false) if a
+ // partial match was found at the end of the first sequence, in which case
+ // the iterator points to the beginning of the partial match. Returns
+ // (last1,false) if no full or partial match was found.
+ template <typename Iterator1, typename Iterator2>
+ std::pair<Iterator1, bool> partial_search(
+ Iterator1 first1, Iterator1 last1, Iterator2 first2, Iterator2 last2)
+ {
+ for (Iterator1 iter1 = first1; iter1 != last1; ++iter1)
+ {
+ Iterator1 test_iter1 = iter1;
+ Iterator2 test_iter2 = first2;
+ for (;; ++test_iter1, ++test_iter2)
+ {
+ if (test_iter2 == last2)
+ return std::make_pair(iter1, true);
+ if (test_iter1 == last1)
+ {
+ if (test_iter2 != first2)
+ return std::make_pair(iter1, false);
+ else
+ break;
+ }
+ if (*test_iter1 != *test_iter2)
+ break;
+ }
+ }
+ return std::make_pair(last1, false);
+ }
+} // namespace detail
+
+template <typename Sync_Read_Stream, typename Allocator, typename Error_Handler>
+std::size_t read_until(Sync_Read_Stream& s,
+ asio::basic_streambuf<Allocator>& b, const std::string& delim,
+ Error_Handler error_handler)
+{
+ std::size_t next_search_start = 0;
+ for (;;)
+ {
+ // Determine the range of the data to be searched.
+ typedef typename asio::basic_streambuf<
+ Allocator>::const_buffers_type const_buffers_type;
+ typedef asio::detail::const_buffers_iterator<
+ const_buffers_type> iterator;
+ const_buffers_type buffers = b.data();
+ iterator begin(buffers, next_search_start);
+ iterator end(buffers, (std::numeric_limits<std::size_t>::max)());
+
+ // Look for a match.
+ std::pair<iterator, bool> result = asio::detail::partial_search(
+ begin, end, delim.begin(), delim.end());
+ if (result.first != end)
+ {
+ if (result.second)
+ {
+ // Full match. We're done.
+ return result.first.position() + delim.length();
+ }
+ else
+ {
+ // Partial match. Next search needs to start from beginning of match.
+ next_search_start = result.first.position();
+ }
+ }
+ else
+ {
+ // No match. Next search can start with the new data.
+ next_search_start = end.position();
+ }
+
+ // Need more data.
+ typename Sync_Read_Stream::error_type error;
+ b.commit(s.read_some(b.prepare(512), asio::assign_error(error)));
+ if (error)
+ {
+ error_handler(error);
+ return 0;
+ }
+ }
+}
+
+template <typename Sync_Read_Stream, typename Allocator>
+inline std::size_t read_until(Sync_Read_Stream& s,
+ asio::basic_streambuf<Allocator>& b, const boost::regex& expr)
+{
+ return read_until(s, b, expr, throw_error());
+}
+
+template <typename Sync_Read_Stream, typename Allocator, typename Error_Handler>
+std::size_t read_until(Sync_Read_Stream& s,
+ asio::basic_streambuf<Allocator>& b, const boost::regex& expr,
+ Error_Handler error_handler)
+{
+ std::size_t next_search_start = 0;
+ for (;;)
+ {
+ // Determine the range of the data to be searched.
+ typedef typename asio::basic_streambuf<
+ Allocator>::const_buffers_type const_buffers_type;
+ typedef asio::detail::const_buffers_iterator<
+ const_buffers_type> iterator;
+ const_buffers_type buffers = b.data();
+ iterator begin(buffers, next_search_start);
+ iterator end(buffers, (std::numeric_limits<std::size_t>::max)());
+
+ // Look for a match.
+ boost::match_results<iterator> match_results;
+ if (boost::regex_search(begin, end, match_results, expr,
+ boost::match_default | boost::match_partial))
+ {
+ if (match_results[0].matched)
+ {
+ // Full match. We're done.
+ return match_results[0].second.position();
+ }
+ else
+ {
+ // Partial match. Next search needs to start from beginning of match.
+ next_search_start = match_results[0].first.position();
+ }
+ }
+ else
+ {
+ // No match. Next search can start with the new data.
+ next_search_start = end.position();
+ }
+
+ // Need more data.
+ typename Sync_Read_Stream::error_type error;
+ b.commit(s.read_some(b.prepare(512), asio::assign_error(error)));
+ if (error)
+ {
+ error_handler(error);
+ return 0;
+ }
+ }
+}
+
+namespace detail
+{
+ template <typename Async_Read_Stream, typename Allocator, typename Handler>
+ class read_until_delim_handler
+ {
+ public:
+ read_until_delim_handler(Async_Read_Stream& stream,
+ asio::basic_streambuf<Allocator>& streambuf, char delim,
+ std::size_t next_search_start, Handler handler)
+ : stream_(stream),
+ streambuf_(streambuf),
+ delim_(delim),
+ next_search_start_(next_search_start),
+ handler_(handler)
+ {
+ }
+
+ void operator()(const typename Async_Read_Stream::error_type& e,
+ std::size_t bytes_transferred)
+ {
+ // Check for errors.
+ if (e)
+ {
+ std::size_t bytes = 0;
+ handler_(e, bytes);
+ return;
+ }
+
+ // Commit received data to streambuf's get area.
+ streambuf_.commit(bytes_transferred);
+
+ // Determine the range of the data to be searched.
+ typedef typename asio::basic_streambuf<
+ Allocator>::const_buffers_type const_buffers_type;
+ typedef asio::detail::const_buffers_iterator<
+ const_buffers_type> iterator;
+ const_buffers_type buffers = streambuf_.data();
+ iterator begin(buffers, next_search_start_);
+ iterator end(buffers, (std::numeric_limits<std::size_t>::max)());
+
+ // Look for a match.
+ iterator iter = std::find(begin, end, delim_);
+ if (iter != end)
+ {
+ // Found a match. We're done.
+ std::size_t bytes = iter.position() + 1;
+ handler_(e, bytes);
+ return;
+ }
+
+ // No match. Start a new asynchronous read operation to obtain more data.
+ next_search_start_ = end.position();
+ stream_.async_read_some(streambuf_.prepare(512), *this);
+ }
+
+ //private:
+ Async_Read_Stream& stream_;
+ asio::basic_streambuf<Allocator>& streambuf_;
+ char delim_;
+ std::size_t next_search_start_;
+ Handler handler_;
+ };
+
+ template <typename Async_Read_Stream, typename Allocator, typename Handler>
+ inline void* asio_handler_allocate(std::size_t size,
+ read_until_delim_handler<Async_Read_Stream,
+ Allocator, Handler>* this_handler)
+ {
+ return asio_handler_alloc_helpers::allocate(
+ size, &this_handler->handler_);
+ }
+
+ template <typename Async_Read_Stream, typename Allocator, typename Handler>
+ inline void asio_handler_deallocate(void* pointer, std::size_t size,
+ read_until_delim_handler<Async_Read_Stream,
+ Allocator, Handler>* this_handler)
+ {
+ asio_handler_alloc_helpers::deallocate(
+ pointer, size, &this_handler->handler_);
+ }
+
+ template <typename Function, typename Async_Read_Stream, typename Allocator,
+ typename Handler>
+ inline void asio_handler_invoke(const Function& function,
+ read_until_delim_handler<Async_Read_Stream,
+ Allocator, Handler>* this_handler)
+ {
+ asio_handler_invoke_helpers::invoke(
+ function, &this_handler->handler_);
+ }
+} // namespace detail
+
+template <typename Async_Read_Stream, typename Allocator, typename Handler>
+void async_read_until(Async_Read_Stream& s,
+ asio::basic_streambuf<Allocator>& b, char delim, Handler handler)
+{
+ // Determine the range of the data to be searched.
+ typedef typename asio::basic_streambuf<
+ Allocator>::const_buffers_type const_buffers_type;
+ typedef asio::detail::const_buffers_iterator<
+ const_buffers_type> iterator;
+ const_buffers_type buffers = b.data();
+ iterator begin(buffers, 0);
+ iterator end(buffers, (std::numeric_limits<std::size_t>::max)());
+
+ // Look for a match.
+ iterator iter = std::find(begin, end, delim);
+ if (iter != end)
+ {
+ // Found a match. We're done.
+ typename Async_Read_Stream::error_type error;
+ std::size_t bytes = iter.position() + 1;
+ s.io_service().post(detail::bind_handler(handler, error, bytes));
+ return;
+ }
+
+ // No match. Start a new asynchronous read operation to obtain more data.
+ s.async_read_some(b.prepare(512),
+ detail::read_until_delim_handler<Async_Read_Stream, Allocator, Handler>(
+ s, b, delim, end.position(), handler));
+}
+
+namespace detail
+{
+ template <typename Async_Read_Stream, typename Allocator, typename Handler>
+ class read_until_delim_string_handler
+ {
+ public:
+ read_until_delim_string_handler(Async_Read_Stream& stream,
+ asio::basic_streambuf<Allocator>& streambuf,
+ const std::string& delim, std::size_t next_search_start,
+ Handler handler)
+ : stream_(stream),
+ streambuf_(streambuf),
+ delim_(delim),
+ next_search_start_(next_search_start),
+ handler_(handler)
+ {
+ }
+
+ void operator()(const typename Async_Read_Stream::error_type& e,
+ std::size_t bytes_transferred)
+ {
+ // Check for errors.
+ if (e)
+ {
+ std::size_t bytes = 0;
+ handler_(e, bytes);
+ return;
+ }
+
+ // Commit received data to streambuf's get area.
+ streambuf_.commit(bytes_transferred);
+
+ // Determine the range of the data to be searched.
+ typedef typename asio::basic_streambuf<
+ Allocator>::const_buffers_type const_buffers_type;
+ typedef asio::detail::const_buffers_iterator<
+ const_buffers_type> iterator;
+ const_buffers_type buffers = streambuf_.data();
+ iterator begin(buffers, next_search_start_);
+ iterator end(buffers, (std::numeric_limits<std::size_t>::max)());
+
+ // Look for a match.
+ std::pair<iterator, bool> result = asio::detail::partial_search(
+ begin, end, delim_.begin(), delim_.end());
+ if (result.first != end)
+ {
+ if (result.second)
+ {
+ // Full match. We're done.
+ std::size_t bytes = result.first.position() + delim_.length();
+ handler_(e, bytes);
+ return;
+ }
+ else
+ {
+ // Partial match. Next search needs to start from beginning of match.
+ next_search_start_ = result.first.position();
+ }
+ }
+ else
+ {
+ // No match. Next search can start with the new data.
+ next_search_start_ = end.position();
+ }
+
+ // No match. Start a new asynchronous read operation to obtain more data.
+ stream_.async_read_some(streambuf_.prepare(512), *this);
+ }
+
+ //private:
+ Async_Read_Stream& stream_;
+ asio::basic_streambuf<Allocator>& streambuf_;
+ std::string delim_;
+ std::size_t next_search_start_;
+ Handler handler_;
+ };
+
+ template <typename Async_Read_Stream, typename Allocator, typename Handler>
+ inline void* asio_handler_allocate(std::size_t size,
+ read_until_delim_string_handler<Async_Read_Stream,
+ Allocator, Handler>* this_handler)
+ {
+ return asio_handler_alloc_helpers::allocate(
+ size, &this_handler->handler_);
+ }
+
+ template <typename Async_Read_Stream, typename Allocator, typename Handler>
+ inline void asio_handler_deallocate(void* pointer, std::size_t size,
+ read_until_delim_string_handler<Async_Read_Stream,
+ Allocator, Handler>* this_handler)
+ {
+ asio_handler_alloc_helpers::deallocate(
+ pointer, size, &this_handler->handler_);
+ }
+
+ template <typename Function, typename Async_Read_Stream,
+ typename Allocator, typename Handler>
+ inline void asio_handler_invoke(const Function& function,
+ read_until_delim_string_handler<Async_Read_Stream,
+ Allocator, Handler>* this_handler)
+ {
+ asio_handler_invoke_helpers::invoke(
+ function, &this_handler->handler_);
+ }
+} // namespace detail
+
+template <typename Async_Read_Stream, typename Allocator, typename Handler>
+void async_read_until(Async_Read_Stream& s,
+ asio::basic_streambuf<Allocator>& b, const std::string& delim,
+ Handler handler)
+{
+ // Determine the range of the data to be searched.
+ typedef typename asio::basic_streambuf<
+ Allocator>::const_buffers_type const_buffers_type;
+ typedef asio::detail::const_buffers_iterator<
+ const_buffers_type> iterator;
+ const_buffers_type buffers = b.data();
+ iterator begin(buffers, 0);
+ iterator end(buffers, (std::numeric_limits<std::size_t>::max)());
+
+ // Look for a match.
+ std::size_t next_search_start;
+ std::pair<iterator, bool> result = asio::detail::partial_search(
+ begin, end, delim.begin(), delim.end());
+ if (result.first != end)
+ {
+ if (result.second)
+ {
+ // Full match. We're done.
+ typename Async_Read_Stream::error_type error;
+ std::size_t bytes = result.first.position() + delim.length();
+ s.io_service().post(detail::bind_handler(handler, error, bytes));
+ return;
+ }
+ else
+ {
+ // Partial match. Next search needs to start from beginning of match.
+ next_search_start = result.first.position();
+ }
+ }
+ else
+ {
+ // No match. Next search can start with the new data.
+ next_search_start = end.position();
+ }
+
+ // No match. Start a new asynchronous read operation to obtain more data.
+ s.async_read_some(b.prepare(512),
+ detail::read_until_delim_string_handler<
+ Async_Read_Stream, Allocator, Handler>(
+ s, b, delim, next_search_start, handler));
+}
+
+namespace detail
+{
+ template <typename Async_Read_Stream, typename Allocator, typename Handler>
+ class read_until_expr_handler
+ {
+ public:
+ read_until_expr_handler(Async_Read_Stream& stream,
+ asio::basic_streambuf<Allocator>& streambuf,
+ const boost::regex& expr, std::size_t next_search_start,
+ Handler handler)
+ : stream_(stream),
+ streambuf_(streambuf),
+ expr_(expr),
+ next_search_start_(next_search_start),
+ handler_(handler)
+ {
+ }
+
+ void operator()(const typename Async_Read_Stream::error_type& e,
+ std::size_t bytes_transferred)
+ {
+ // Check for errors.
+ if (e)
+ {
+ std::size_t bytes = 0;
+ handler_(e, bytes);
+ return;
+ }
+
+ // Commit received data to streambuf's get area.
+ streambuf_.commit(bytes_transferred);
+
+ // Determine the range of the data to be searched.
+ typedef typename asio::basic_streambuf<
+ Allocator>::const_buffers_type const_buffers_type;
+ typedef asio::detail::const_buffers_iterator<
+ const_buffers_type> iterator;
+ const_buffers_type buffers = streambuf_.data();
+ iterator begin(buffers, next_search_start_);
+ iterator end(buffers, (std::numeric_limits<std::size_t>::max)());
+
+ // Look for a match.
+ boost::match_results<iterator> match_results;
+ if (boost::regex_search(begin, end, match_results, expr_,
+ boost::match_default | boost::match_partial))
+ {
+ if (match_results[0].matched)
+ {
+ // Full match. We're done.
+ std::size_t bytes = match_results[0].second.position();
+ handler_(e, bytes);
+ return;
+ }
+ else
+ {
+ // Partial match. Next search needs to start from beginning of match.
+ next_search_start_ = match_results[0].first.position();
+ }
+ }
+ else
+ {
+ // No match. Next search can start with the new data.
+ next_search_start_ = end.position();
+ }
+
+ // No match. Start a new asynchronous read operation to obtain more data.
+ stream_.async_read_some(streambuf_.prepare(512), *this);
+ }
+
+ //private:
+ Async_Read_Stream& stream_;
+ asio::basic_streambuf<Allocator>& streambuf_;
+ boost::regex expr_;
+ std::size_t next_search_start_;
+ Handler handler_;
+ };
+
+ template <typename Async_Read_Stream, typename Allocator, typename Handler>
+ inline void* asio_handler_allocate(std::size_t size,
+ read_until_expr_handler<Async_Read_Stream,
+ Allocator, Handler>* this_handler)
+ {
+ return asio_handler_alloc_helpers::allocate(
+ size, &this_handler->handler_);
+ }
+
+ template <typename Async_Read_Stream, typename Allocator, typename Handler>
+ inline void asio_handler_deallocate(void* pointer, std::size_t size,
+ read_until_expr_handler<Async_Read_Stream,
+ Allocator, Handler>* this_handler)
+ {
+ asio_handler_alloc_helpers::deallocate(
+ pointer, size, &this_handler->handler_);
+ }
+
+ template <typename Function, typename Async_Read_Stream, typename Allocator,
+ typename Handler>
+ inline void asio_handler_invoke(const Function& function,
+ read_until_expr_handler<Async_Read_Stream,
+ Allocator, Handler>* this_handler)
+ {
+ asio_handler_invoke_helpers::invoke(
+ function, &this_handler->handler_);
+ }
+} // namespace detail
+
+template <typename Async_Read_Stream, typename Allocator, typename Handler>
+void async_read_until(Async_Read_Stream& s,
+ asio::basic_streambuf<Allocator>& b, const boost::regex& expr,
+ Handler handler)
+{
+ // Determine the range of the data to be searched.
+ typedef typename asio::basic_streambuf<
+ Allocator>::const_buffers_type const_buffers_type;
+ typedef asio::detail::const_buffers_iterator<
+ const_buffers_type> iterator;
+ const_buffers_type buffers = b.data();
+ iterator begin(buffers, 0);
+ iterator end(buffers, (std::numeric_limits<std::size_t>::max)());
+
+ // Look for a match.
+ std::size_t next_search_start;
+ boost::match_results<iterator> match_results;
+ if (boost::regex_search(begin, end, match_results, expr,
+ boost::match_default | boost::match_partial))
+ {
+ if (match_results[0].matched)
+ {
+ // Full match. We're done.
+ typename Async_Read_Stream::error_type error;
+ std::size_t bytes = match_results[0].second.position();
+ s.io_service().post(detail::bind_handler(handler, error, bytes));
+ return;
+ }
+ else
+ {
+ // Partial match. Next search needs to start from beginning of match.
+ next_search_start = match_results[0].first.position();
+ }
+ }
+ else
+ {
+ // No match. Next search can start with the new data.
+ next_search_start = end.position();
+ }
+
+ // No match. Start a new asynchronous read operation to obtain more data.
+ s.async_read_some(b.prepare(512),
+ detail::read_until_expr_handler<Async_Read_Stream, Allocator, Handler>(
+ s, b, expr, next_search_start, handler));
+}
+
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_READ_UNTIL_IPP
diff --git a/library/include/libtorrent/asio/impl/write.ipp b/library/include/libtorrent/asio/impl/write.ipp
new file mode 100644
index 000000000..88b14b239
--- /dev/null
+++ b/library/include/libtorrent/asio/impl/write.ipp
@@ -0,0 +1,266 @@
+//
+// write.ipp
+// ~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_WRITE_IPP
+#define ASIO_WRITE_IPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/buffer.hpp"
+#include "asio/completion_condition.hpp"
+#include "asio/error_handler.hpp"
+#include "asio/detail/bind_handler.hpp"
+#include "asio/detail/consuming_buffers.hpp"
+#include "asio/detail/handler_alloc_helpers.hpp"
+#include "asio/detail/handler_invoke_helpers.hpp"
+
+namespace asio {
+
+template <typename Sync_Write_Stream, typename Const_Buffers,
+ typename Completion_Condition, typename Error_Handler>
+std::size_t write(Sync_Write_Stream& s, const Const_Buffers& buffers,
+ Completion_Condition completion_condition, Error_Handler error_handler)
+{
+ asio::detail::consuming_buffers<
+ const_buffer, Const_Buffers> tmp(buffers);
+ std::size_t total_transferred = 0;
+ while (tmp.begin() != tmp.end())
+ {
+ typename Sync_Write_Stream::error_type e;
+ std::size_t bytes_transferred = s.write_some(tmp, assign_error(e));
+ tmp.consume(bytes_transferred);
+ total_transferred += bytes_transferred;
+ if (completion_condition(e, total_transferred))
+ {
+ error_handler(e);
+ return total_transferred;
+ }
+ }
+ typename Sync_Write_Stream::error_type e;
+ error_handler(e);
+ return total_transferred;
+}
+
+template <typename Sync_Write_Stream, typename Const_Buffers>
+inline std::size_t write(Sync_Write_Stream& s, const Const_Buffers& buffers)
+{
+ return write(s, buffers, transfer_all(), throw_error());
+}
+
+template <typename Sync_Write_Stream, typename Const_Buffers,
+ typename Completion_Condition>
+inline std::size_t write(Sync_Write_Stream& s, const Const_Buffers& buffers,
+ Completion_Condition completion_condition)
+{
+ return write(s, buffers, completion_condition, throw_error());
+}
+
+template <typename Sync_Write_Stream, typename Allocator,
+ typename Completion_Condition, typename Error_Handler>
+std::size_t write(Sync_Write_Stream& s,
+ asio::basic_streambuf<Allocator>& b,
+ Completion_Condition completion_condition, Error_Handler error_handler)
+{
+ typename Sync_Write_Stream::error_type error;
+ std::size_t bytes_transferred = write(s, b.data(),
+ completion_condition, asio::assign_error(error));
+ b.consume(bytes_transferred);
+ error_handler(error);
+ return bytes_transferred;
+}
+
+template <typename Sync_Write_Stream, typename Allocator>
+inline std::size_t write(Sync_Write_Stream& s,
+ asio::basic_streambuf<Allocator>& b)
+{
+ return write(s, b, transfer_all(), throw_error());
+}
+
+template <typename Sync_Write_Stream, typename Allocator,
+ typename Completion_Condition>
+inline std::size_t write(Sync_Write_Stream& s,
+ asio::basic_streambuf<Allocator>& b,
+ Completion_Condition completion_condition)
+{
+ return write(s, b, completion_condition, throw_error());
+}
+
+namespace detail
+{
+ template <typename Async_Write_Stream, typename Const_Buffers,
+ typename Completion_Condition, typename Handler>
+ class write_handler
+ {
+ public:
+ write_handler(Async_Write_Stream& stream, const Const_Buffers& buffers,
+ Completion_Condition completion_condition, Handler handler)
+ : stream_(stream),
+ buffers_(buffers),
+ total_transferred_(0),
+ completion_condition_(completion_condition),
+ handler_(handler)
+ {
+ }
+
+ void operator()(const typename Async_Write_Stream::error_type& e,
+ std::size_t bytes_transferred)
+ {
+ total_transferred_ += bytes_transferred;
+ buffers_.consume(bytes_transferred);
+ if (completion_condition_(e, total_transferred_)
+ || buffers_.begin() == buffers_.end())
+ {
+ handler_(e, total_transferred_);
+ }
+ else
+ {
+ stream_.async_write_some(buffers_, *this);
+ }
+ }
+
+ //private:
+ Async_Write_Stream& stream_;
+ asio::detail::consuming_buffers<
+ const_buffer, Const_Buffers> buffers_;
+ std::size_t total_transferred_;
+ Completion_Condition completion_condition_;
+ Handler handler_;
+ };
+
+ template <typename Async_Write_Stream, typename Const_Buffers,
+ typename Completion_Condition, typename Handler>
+ inline void* asio_handler_allocate(std::size_t size,
+ write_handler<Async_Write_Stream, Const_Buffers,
+ Completion_Condition, Handler>* this_handler)
+ {
+ return asio_handler_alloc_helpers::allocate(
+ size, &this_handler->handler_);
+ }
+
+ template <typename Async_Write_Stream, typename Const_Buffers,
+ typename Completion_Condition, typename Handler>
+ inline void asio_handler_deallocate(void* pointer, std::size_t size,
+ write_handler<Async_Write_Stream, Const_Buffers,
+ Completion_Condition, Handler>* this_handler)
+ {
+ asio_handler_alloc_helpers::deallocate(
+ pointer, size, &this_handler->handler_);
+ }
+
+ template <typename Function, typename Async_Write_Stream,
+ typename Const_Buffers, typename Completion_Condition, typename Handler>
+ inline void asio_handler_invoke(const Function& function,
+ write_handler<Async_Write_Stream, Const_Buffers,
+ Completion_Condition, Handler>* this_handler)
+ {
+ asio_handler_invoke_helpers::invoke(
+ function, &this_handler->handler_);
+ }
+} // namespace detail
+
+template <typename Async_Write_Stream, typename Const_Buffers,
+ typename Completion_Condition, typename Handler>
+inline void async_write(Async_Write_Stream& s, const Const_Buffers& buffers,
+ Completion_Condition completion_condition, Handler handler)
+{
+ s.async_write_some(buffers,
+ detail::write_handler<Async_Write_Stream, Const_Buffers,
+ Completion_Condition, Handler>(
+ s, buffers, completion_condition, handler));
+}
+
+template <typename Async_Write_Stream, typename Const_Buffers, typename Handler>
+inline void async_write(Async_Write_Stream& s, const Const_Buffers& buffers,
+ Handler handler)
+{
+ async_write(s, buffers, transfer_all(), handler);
+}
+
+namespace detail
+{
+ template <typename Async_Write_Stream, typename Allocator, typename Handler>
+ class write_streambuf_handler
+ {
+ public:
+ write_streambuf_handler(asio::basic_streambuf<Allocator>& streambuf,
+ Handler handler)
+ : streambuf_(streambuf),
+ handler_(handler)
+ {
+ }
+
+ void operator()(const typename Async_Write_Stream::error_type& e,
+ std::size_t bytes_transferred)
+ {
+ streambuf_.consume(bytes_transferred);
+ handler_(e, bytes_transferred);
+ }
+
+ //private:
+ asio::basic_streambuf<Allocator>& streambuf_;
+ Handler handler_;
+ };
+
+ template <typename Async_Write_Stream, typename Allocator, typename Handler>
+ inline void* asio_handler_allocate(std::size_t size,
+ write_streambuf_handler<Async_Write_Stream,
+ Allocator, Handler>* this_handler)
+ {
+ return asio_handler_alloc_helpers::allocate(
+ size, &this_handler->handler_);
+ }
+
+ template <typename Async_Write_Stream, typename Allocator, typename Handler>
+ inline void asio_handler_deallocate(void* pointer, std::size_t size,
+ write_streambuf_handler<Async_Write_Stream,
+ Allocator, Handler>* this_handler)
+ {
+ asio_handler_alloc_helpers::deallocate(
+ pointer, size, &this_handler->handler_);
+ }
+
+ template <typename Function, typename Async_Write_Stream, typename Allocator,
+ typename Handler>
+ inline void asio_handler_invoke(const Function& function,
+ write_streambuf_handler<Async_Write_Stream,
+ Allocator, Handler>* this_handler)
+ {
+ asio_handler_invoke_helpers::invoke(
+ function, &this_handler->handler_);
+ }
+} // namespace detail
+
+template <typename Async_Write_Stream, typename Allocator,
+ typename Completion_Condition, typename Handler>
+inline void async_write(Async_Write_Stream& s,
+ asio::basic_streambuf<Allocator>& b,
+ Completion_Condition completion_condition, Handler handler)
+{
+ async_write(s, b.data(), completion_condition,
+ detail::write_streambuf_handler<Async_Write_Stream, Allocator, Handler>(
+ b, handler));
+}
+
+template <typename Async_Write_Stream, typename Allocator, typename Handler>
+inline void async_write(Async_Write_Stream& s,
+ asio::basic_streambuf<Allocator>& b, Handler handler)
+{
+ async_write(s, b, transfer_all(), handler);
+}
+
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_WRITE_IPP
diff --git a/library/include/libtorrent/asio/io_service.hpp b/library/include/libtorrent/asio/io_service.hpp
new file mode 100644
index 000000000..6808f3d45
--- /dev/null
+++ b/library/include/libtorrent/asio/io_service.hpp
@@ -0,0 +1,424 @@
+//
+// io_service.hpp
+// ~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_IO_SERVICE_HPP
+#define ASIO_IO_SERVICE_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/detail/push_options.hpp"
+#include <cstddef>
+#include <stdexcept>
+#include <typeinfo>
+#include <boost/config.hpp>
+#include <boost/throw_exception.hpp>
+#include "asio/detail/pop_options.hpp"
+
+#include "asio/detail/epoll_reactor_fwd.hpp"
+#include "asio/detail/kqueue_reactor_fwd.hpp"
+#include "asio/detail/noncopyable.hpp"
+#include "asio/detail/select_reactor_fwd.hpp"
+#include "asio/detail/service_registry.hpp"
+#include "asio/detail/signal_init.hpp"
+#include "asio/detail/task_io_service_fwd.hpp"
+#include "asio/detail/win_iocp_io_service_fwd.hpp"
+#include "asio/detail/winsock_init.hpp"
+#include "asio/detail/wrapped_handler.hpp"
+
+namespace asio {
+
+/// Provides core I/O functionality.
+/**
+ * The io_service class provides the core I/O functionality for users of the
+ * asynchronous I/O objects, including:
+ *
+ * @li asio::ip::tcp::socket
+ * @li asio::ip::tcp::acceptor
+ * @li asio::ip::udp::socket
+ * @li asio::deadline_timer.
+ *
+ * The io_service class also includes facilities intended for developers of
+ * custom asynchronous services.
+ *
+ * @par Thread Safety:
+ * @e Distinct @e objects: Safe.@n
+ * @e Shared @e objects: Safe, with the exception that calling reset()
+ * while there are unfinished run() calls results in undefined behaviour.
+ *
+ * @par Concepts:
+ * Dispatcher.
+ *
+ * @sa @ref io_service_handler_exception
+ */
+class io_service
+ : private noncopyable
+{
+private:
+ // The type of the platform-specific implementation.
+#if defined(ASIO_HAS_IOCP)
+ typedef detail::win_iocp_io_service impl_type;
+#elif defined(ASIO_HAS_EPOLL)
+ typedef detail::task_io_service<detail::epoll_reactor<false> > impl_type;
+#elif defined(ASIO_HAS_KQUEUE)
+ typedef detail::task_io_service<detail::kqueue_reactor<false> > impl_type;
+#else
+ typedef detail::task_io_service<detail::select_reactor<false> > impl_type;
+#endif
+
+public:
+ class work;
+ friend class work;
+
+ class service;
+
+ class strand;
+
+ /// Default constructor.
+ io_service();
+
+ /// Run the io_service's event processing loop.
+ /**
+ * The run() function blocks until all work has finished and there are no
+ * more handlers to be dispatched, or until the io_service has been
+ * interrupted.
+ *
+ * Multiple threads may call the run() function to set up a pool of threads
+ * from which the io_service may execute handlers.
+ *
+ * The run() function may be safely called again once it has completed only
+ * after a call to reset().
+ *
+ * @return The number of handlers that were executed.
+ */
+ size_t run();
+
+ /// Run the io_service's event processing loop to execute at most one handler.
+ /**
+ * The run_one() function blocks until one handler has been dispatched, or
+ * until the io_service has been interrupted.
+ *
+ * @return The number of handlers that were executed.
+ */
+ size_t run_one();
+
+ /// Run the io_service's event processing loop to execute ready handlers.
+ /**
+ * The poll() function runs handlers that are ready to run, without blocking,
+ * until the io_service has been interrupted or there are no more ready
+ * handlers.
+ *
+ * @return The number of handlers that were executed.
+ */
+ size_t poll();
+
+ /// Run the io_service's event processing loop to execute one ready handler.
+ /**
+ * The poll_one() function runs at most one handler that is ready to run,
+ * without blocking.
+ *
+ * @return The number of handlers that were executed.
+ */
+ size_t poll_one();
+
+ /// Interrupt the io_service's event processing loop.
+ /**
+ * This function does not block, but instead simply signals to the io_service
+ * that all invocations of its run() or run_one() member functions should
+ * return as soon as possible.
+ *
+ * Note that if the run() function is interrupted and is not called again
+ * later then its work may not have finished and handlers may not be
+ * delivered. In this case an io_service implementation is not required to
+ * make any guarantee that the resources associated with unfinished work will
+ * be cleaned up.
+ */
+ void interrupt();
+
+ /// Reset the io_service in preparation for a subsequent run() invocation.
+ /**
+ * This function must be called prior to any second or later set of
+ * invocations of the run(), run_one(), poll() or poll_one() functions. It
+ * allows the io_service to reset any internal state, such as an interrupt
+ * flag.
+ *
+ * This function must not be called while there are any unfinished calls to
+ * the run(), run_one(), poll() or poll_one() functions.
+ */
+ void reset();
+
+ /// Request the io_service to invoke the given handler.
+ /**
+ * This function is used to ask the io_service to execute the given handler.
+ *
+ * The io_service guarantees that the handler will only be called in a thread
+ * in which the run(), run_one(), poll() or poll_one() member functions is
+ * currently being invoked. The handler may be executed inside this function
+ * if the guarantee can be met.
+ *
+ * @param handler The handler to be called. The io_service will make
+ * a copy of the handler object as required. The function signature of the
+ * handler must be: @code void handler(); @endcode
+ */
+ template <typename Handler>
+ void dispatch(Handler handler);
+
+ /// Request the io_service to invoke the given handler and return immediately.
+ /**
+ * This function is used to ask the io_service to execute the given handler,
+ * but without allowing the io_service to call the handler from inside this
+ * function.
+ *
+ * The io_service guarantees that the handler will only be called in a thread
+ * in which the run(), run_one(), poll() or poll_one() member functions is
+ * currently being invoked.
+ *
+ * @param handler The handler to be called. The io_service will make
+ * a copy of the handler object as required. The function signature of the
+ * handler must be: @code void handler(); @endcode
+ */
+ template <typename Handler>
+ void post(Handler handler);
+
+ /// Create a new handler that automatically dispatches the wrapped handler
+ /// on the io_service.
+ /**
+ * This function is used to create a new handler function object that, when
+ * invoked, will automatically pass the wrapped handler to the io_service's
+ * dispatch function.
+ *
+ * @param handler The handler to be wrapped. The io_service will make a copy
+ * of the handler object as required. The function signature of the handler
+ * must be: @code void handler(A1 a1, ... An an); @endcode
+ *
+ * @return A function object that, when invoked, passes the wrapped handler to
+ * the io_service's dispatch function. Given a function object with the
+ * signature:
+ * @code R f(A1 a1, ... An an); @endcode
+ * If this function object is passed to the wrap function like so:
+ * @code io_service.wrap(f); @endcode
+ * then the return value is a function object with the signature
+ * @code void g(A1 a1, ... An an); @endcode
+ * that, when invoked, executes code equivalent to:
+ * @code io_service.dispatch(boost::bind(f, a1, ... an)); @endcode
+ */
+ template <typename Handler>
+#if defined(GENERATING_DOCUMENTATION)
+ unspecified
+#else
+ detail::wrapped_handler<io_service, Handler>
+#endif
+ wrap(Handler handler);
+
+ /// Obtain the service object corresponding to the given type.
+ /**
+ * This function is used to locate a service object that corresponds to
+ * the given service type. If there is no existing implementation of the
+ * service, then the io_service will create a new instance of the service.
+ *
+ * @param ios The io_service object that owns the service.
+ *
+ * @return The service interface implementing the specified service type.
+ * Ownership of the service interface is not transferred to the caller.
+ */
+ template <typename Service>
+ friend Service& use_service(io_service& ios);
+
+ /// Add a service object to the io_service.
+ /**
+ * This function is used to add a service to the io_service.
+ *
+ * @param ios The io_service object that owns the service.
+ *
+ * @param svc The service object. On success, ownership of the service object
+ * is transferred to the io_service. When the io_service object is destroyed,
+ * it will destroy the service object by performing:
+ * @code delete static_cast<io_service::service*>(svc) @endcode
+ *
+ * @throws asio::service_already_exists Thrown if a service of the
+ * given type is already present in the io_service.
+ *
+ * @throws asio::invalid_service_owner Thrown if the service's owning
+ * io_service is not the io_service object specified by the ios parameter.
+ */
+ template <typename Service>
+ friend void add_service(io_service& ios, Service* svc);
+
+ /// Determine if an io_service contains a specified service type.
+ /**
+ * This function is used to determine whether the io_service contains a
+ * service object corresponding to the given service type.
+ *
+ * @param ios The io_service object that owns the service.
+ *
+ * @return A boolean indicating whether the io_service contains the service.
+ */
+ template <typename Service>
+ friend bool has_service(io_service& ios);
+
+private:
+#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+ detail::winsock_init<> init_;
+#elif defined(__sun) || defined(__QNX__)
+ detail::signal_init<> init_;
+#endif
+
+ // The service registry.
+ detail::service_registry<io_service> service_registry_;
+
+ // The implementation.
+ impl_type& impl_;
+};
+
+/// Class to inform the io_service when it has work to do.
+/**
+ * The work class is used to inform the io_service when work starts and
+ * finishes. This ensures that the io_service's run() function will not exit
+ * while work is underway, and that it does exit when there is no unfinished
+ * work remaining.
+ *
+ * The work class is copy-constructible so that it may be used as a data member
+ * in a handler class. It is not assignable.
+ */
+class io_service::work
+{
+public:
+ /// Constructor notifies the io_service that work is starting.
+ /**
+ * The constructor is used to inform the io_service that some work has begun.
+ * This ensures that the io_service's run() function will not exit while the
+ * work is underway.
+ */
+ explicit work(asio::io_service& io_service);
+
+ /// Copy constructor notifies the io_service that work is starting.
+ /**
+ * The constructor is used to inform the io_service that some work has begun.
+ * This ensures that the io_service's run() function will not exit while the
+ * work is underway.
+ */
+ work(const work& other);
+
+ /// Destructor notifies the io_service that the work is complete.
+ /**
+ * The destructor is used to inform the io_service that some work has
+ * finished. Once the count of unfinished work reaches zero, the io_service's
+ * run() function is permitted to exit.
+ */
+ ~work();
+
+ /// Get the io_service associated with the work.
+ asio::io_service& io_service();
+
+private:
+ // Prevent assignment.
+ void operator=(const work& other);
+
+ // The io_service.
+ asio::io_service& io_service_;
+};
+
+/// Base class for all io_service services.
+class io_service::service
+ : private noncopyable
+{
+public:
+ /// Get the io_service object that owns the service.
+ asio::io_service& io_service();
+
+protected:
+ /// Constructor.
+ /**
+ * @param owner The io_service object that owns the service.
+ */
+ service(asio::io_service& owner);
+
+ /// Destructor.
+ virtual ~service();
+
+private:
+ /// Destroy all user-defined handler objects owned by the service.
+ virtual void shutdown_service() = 0;
+
+ friend class detail::service_registry<asio::io_service>;
+ asio::io_service& owner_;
+ const std::type_info* type_info_;
+ service* next_;
+};
+
+/// Exception thrown when trying to add a duplicate service to an io_service.
+class service_already_exists
+ : public std::logic_error
+{
+public:
+ service_already_exists()
+ : std::logic_error("Service already exists.")
+ {
+ }
+};
+
+/// Exception thrown when trying to add a service object to an io_service where
+/// the service has a different owner.
+class invalid_service_owner
+ : public std::logic_error
+{
+public:
+ invalid_service_owner()
+ : std::logic_error("Invalid service owner.")
+ {
+ }
+};
+
+/**
+ * @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"
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_IO_SERVICE_HPP
diff --git a/library/include/libtorrent/asio/ip/address.hpp b/library/include/libtorrent/asio/ip/address.hpp
new file mode 100644
index 000000000..5d9a6e3a1
--- /dev/null
+++ b/library/include/libtorrent/asio/ip/address.hpp
@@ -0,0 +1,284 @@
+//
+// address.hpp
+// ~~~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_IP_ADDRESS_HPP
+#define ASIO_IP_ADDRESS_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/detail/push_options.hpp"
+#include <iosfwd>
+#include <string>
+#include <boost/throw_exception.hpp>
+#include "asio/detail/pop_options.hpp"
+
+#include "asio/error.hpp"
+#include "asio/error_handler.hpp"
+#include "asio/ip/address_v4.hpp"
+#include "asio/ip/address_v6.hpp"
+
+namespace asio {
+namespace ip {
+
+/// Implements version-independent IP addresses.
+/**
+ * The asio::ip::address class provides the ability to use either IP
+ * version 4 or version 6 addresses.
+ *
+ * @par Thread Safety:
+ * @e Distinct @e objects: Safe.@n
+ * @e Shared @e objects: Unsafe.
+ */
+class address
+{
+public:
+ /// Default constructor.
+ address()
+ : type_(ipv4),
+ ipv4_address_(),
+ ipv6_address_()
+ {
+ }
+
+ /// Construct an address from an IPv4 address.
+ address(const asio::ip::address_v4& ipv4_address)
+ : type_(ipv4),
+ ipv4_address_(ipv4_address),
+ ipv6_address_()
+ {
+ }
+
+ /// Construct an address from an IPv6 address.
+ address(const asio::ip::address_v6& ipv6_address)
+ : type_(ipv6),
+ ipv4_address_(),
+ ipv6_address_(ipv6_address)
+ {
+ }
+
+ /// Copy constructor.
+ address(const address& other)
+ : type_(other.type_),
+ ipv4_address_(other.ipv4_address_),
+ ipv6_address_(other.ipv6_address_)
+ {
+ }
+
+ /// Assign from another address.
+ address& operator=(const address& other)
+ {
+ type_ = other.type_;
+ ipv4_address_ = other.ipv4_address_;
+ ipv6_address_ = other.ipv6_address_;
+ return *this;
+ }
+
+ /// Assign from an IPv4 address.
+ address& operator=(const asio::ip::address_v4& ipv4_address)
+ {
+ type_ = ipv4;
+ ipv4_address_ = ipv4_address;
+ ipv6_address_ = asio::ip::address_v6();
+ return *this;
+ }
+
+ /// Assign from an IPv6 address.
+ address& operator=(const asio::ip::address_v6& ipv6_address)
+ {
+ type_ = ipv6;
+ ipv4_address_ = asio::ip::address_v4();
+ ipv6_address_ = ipv6_address;
+ return *this;
+ }
+
+ /// Get whether the address is an IP version 4 address.
+ bool is_v4() const
+ {
+ return type_ == ipv4;
+ }
+
+ /// Get whether the address is an IP version 6 address.
+ bool is_v6() const
+ {
+ return type_ == ipv6;
+ }
+
+ /// Get the address as an IP version 4 address.
+ asio::ip::address_v4 to_v4() const
+ {
+ if (type_ != ipv4)
+ {
+ asio::error error(
+ asio::error::address_family_not_supported);
+ boost::throw_exception(error);
+ }
+ return ipv4_address_;
+ }
+
+ /// Get the address as an IP version 6 address.
+ asio::ip::address_v6 to_v6() const
+ {
+ if (type_ != ipv6)
+ {
+ asio::error error(
+ asio::error::address_family_not_supported);
+ boost::throw_exception(error);
+ }
+ return ipv6_address_;
+ }
+
+ /// Get the address as a string in dotted decimal format.
+ std::string to_string() const
+ {
+ if (type_ == ipv6)
+ return ipv6_address_.to_string();
+ return ipv4_address_.to_string();
+ }
+
+ /// Get the address as a string in dotted decimal format.
+ template <typename Error_Handler>
+ std::string to_string(Error_Handler error_handler) const
+ {
+ if (type_ == ipv6)
+ return ipv6_address_.to_string(error_handler);
+ return ipv4_address_.to_string(error_handler);
+ }
+
+ /// Create an address from an IPv4 address string in dotted decimal form,
+ /// or from an IPv6 address in hexadecimal notation.
+ static address from_string(const char* str)
+ {
+ return from_string(str, asio::throw_error());
+ }
+
+ /// Create an address from an IPv4 address string in dotted decimal form,
+ /// or from an IPv6 address in hexadecimal notation.
+ template <typename Error_Handler>
+ static address from_string(const char* str, Error_Handler error_handler)
+ {
+ asio::error error;
+ asio::ip::address_v6 ipv6_address =
+ asio::ip::address_v6::from_string(str,
+ asio::assign_error(error));
+ if (!error)
+ {
+ address tmp;
+ tmp.type_ = ipv6;
+ tmp.ipv6_address_ = ipv6_address;
+ error_handler(error);
+ return tmp;
+ }
+
+ error = asio::error();
+ asio::ip::address_v4 ipv4_address =
+ asio::ip::address_v4::from_string(str,
+ asio::assign_error(error));
+ if (!error)
+ {
+ address tmp;
+ tmp.type_ = ipv4;
+ tmp.ipv4_address_ = ipv4_address;
+ error_handler(error);
+ return tmp;
+ }
+
+ error_handler(error);
+ return address();
+ }
+
+ /// Create an address from an IPv4 address string in dotted decimal form,
+ /// or from an IPv6 address in hexadecimal notation.
+ static address from_string(const std::string& str)
+ {
+ return from_string(str.c_str(), asio::throw_error());
+ }
+
+ /// Create an address from an IPv4 address string in dotted decimal form,
+ /// or from an IPv6 address in hexadecimal notation.
+ template <typename Error_Handler>
+ static address from_string(const std::string& str,
+ Error_Handler error_handler)
+ {
+ return from_string(str.c_str(), error_handler);
+ }
+
+ /// Compare two addresses for equality.
+ friend bool operator==(const address& a1, const address& a2)
+ {
+ if (a1.type_ != a2.type_)
+ return false;
+ if (a1.type_ == ipv6)
+ return a1.ipv6_address_ == a2.ipv6_address_;
+ return a1.ipv4_address_ == a2.ipv4_address_;
+ }
+
+ /// Compare two addresses for inequality.
+ friend bool operator!=(const address& a1, const address& a2)
+ {
+ if (a1.type_ != a2.type_)
+ return true;
+ if (a1.type_ == ipv6)
+ return a1.ipv6_address_ != a2.ipv6_address_;
+ return a1.ipv4_address_ != a2.ipv4_address_;
+ }
+
+ /// Compare addresses for ordering.
+ friend bool operator<(const address& a1, const address& a2)
+ {
+ if (a1.type_ < a2.type_)
+ return true;
+ if (a1.type_ > a2.type_)
+ return false;
+ if (a1.type_ == ipv6)
+ return a1.ipv6_address_ < a2.ipv6_address_;
+ return a1.ipv4_address_ < a2.ipv4_address_;
+ }
+
+private:
+ // The type of the address.
+ enum { ipv4, ipv6 } type_;
+
+ // The underlying IPv4 address.
+ asio::ip::address_v4 ipv4_address_;
+
+ // The underlying IPv6 address.
+ asio::ip::address_v6 ipv6_address_;
+};
+
+/// Output an address as a string.
+/**
+ * Used to output a human-readable string for a specified address.
+ *
+ * @param os The output stream to which the string will be written.
+ *
+ * @param addr The address to be written.
+ *
+ * @return The output stream.
+ *
+ * @relates asio::ip::address
+ */
+template <typename Elem, typename Traits>
+std::basic_ostream<Elem, Traits>& operator<<(
+ std::basic_ostream<Elem, Traits>& os, const address& addr)
+{
+ os << addr.to_string();
+ return os;
+}
+
+} // namespace ip
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_IP_ADDRESS_HPP
diff --git a/library/include/libtorrent/asio/ip/address_v4.hpp b/library/include/libtorrent/asio/ip/address_v4.hpp
new file mode 100644
index 000000000..eea9919b4
--- /dev/null
+++ b/library/include/libtorrent/asio/ip/address_v4.hpp
@@ -0,0 +1,292 @@
+//
+// address_v4.hpp
+// ~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_IP_ADDRESS_V4_HPP
+#define ASIO_IP_ADDRESS_V4_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/detail/push_options.hpp"
+#include <string>
+#include <boost/array.hpp>
+#include <boost/throw_exception.hpp>
+#include "asio/detail/pop_options.hpp"
+
+#include "asio/error.hpp"
+#include "asio/error_handler.hpp"
+#include "asio/detail/socket_ops.hpp"
+#include "asio/detail/socket_types.hpp"
+
+namespace asio {
+namespace ip {
+
+/// Implements IP version 4 style addresses.
+/**
+ * The asio::ip::address_v4 class provides the ability to use and
+ * manipulate IP version 4 addresses.
+ *
+ * @par Thread Safety:
+ * @e Distinct @e objects: Safe.@n
+ * @e Shared @e objects: Unsafe.
+ */
+class address_v4
+{
+public:
+ /// The type used to represent an address as an array of bytes.
+ typedef boost::array<unsigned char, 4> bytes_type;
+
+ /// Default constructor.
+ address_v4()
+ {
+ addr_.s_addr = 0;
+ }
+
+ /// Construct an address from raw bytes.
+ explicit address_v4(const bytes_type& bytes)
+ {
+ using namespace std; // For memcpy.
+ memcpy(&addr_.s_addr, bytes.elems, 4);
+ }
+
+ /// Construct an address from a unsigned long in host byte order.
+ explicit address_v4(unsigned long addr)
+ {
+ addr_.s_addr = asio::detail::socket_ops::host_to_network_long(addr);
+ }
+
+ /// Copy constructor.
+ address_v4(const address_v4& other)
+ : addr_(other.addr_)
+ {
+ }
+
+ /// Assign from another address.
+ address_v4& operator=(const address_v4& other)
+ {
+ addr_ = other.addr_;
+ return *this;
+ }
+
+ /// Get the address in bytes.
+ bytes_type to_bytes() const
+ {
+ using namespace std; // For memcpy.
+ bytes_type bytes;
+ memcpy(bytes.elems, &addr_.s_addr, 4);
+ return bytes;
+ }
+
+ /// Get the address as an unsigned long in host byte order
+ unsigned long to_ulong() const
+ {
+ return asio::detail::socket_ops::network_to_host_long(addr_.s_addr);
+ }
+
+ /// Get the address as a string in dotted decimal format.
+ std::string to_string() const
+ {
+ return to_string(asio::throw_error());
+ }
+
+ /// Get the address as a string in dotted decimal format.
+ template <typename Error_Handler>
+ std::string to_string(Error_Handler error_handler) const
+ {
+ char addr_str[asio::detail::max_addr_v4_str_len];
+ const char* addr =
+ asio::detail::socket_ops::inet_ntop(AF_INET, &addr_, addr_str,
+ asio::detail::max_addr_v4_str_len);
+ if (addr == 0)
+ {
+ asio::error e(asio::detail::socket_ops::get_error());
+ error_handler(e);
+ return std::string();
+ }
+ asio::error e;
+ error_handler(e);
+ return addr;
+ }
+
+ /// Create an address from an IP address string in dotted decimal form.
+ static address_v4 from_string(const char* str)
+ {
+ return from_string(str, asio::throw_error());
+ }
+
+ /// Create an address from an IP address string in dotted decimal form.
+ template <typename Error_Handler>
+ static address_v4 from_string(const char* str, Error_Handler error_handler)
+ {
+ address_v4 tmp;
+ if (asio::detail::socket_ops::inet_pton(
+ AF_INET, str, &tmp.addr_) <= 0)
+ {
+ asio::error e(asio::detail::socket_ops::get_error());
+ error_handler(e);
+ return address_v4();
+ }
+ asio::error e;
+ error_handler(e);
+ return tmp;
+ }
+
+ /// Create an address from an IP address string in dotted decimal form.
+ static address_v4 from_string(const std::string& str)
+ {
+ return from_string(str.c_str(), asio::throw_error());
+ }
+
+ /// Create an address from an IP address string in dotted decimal form.
+ template <typename Error_Handler>
+ static address_v4 from_string(const std::string& str,
+ Error_Handler error_handler)
+ {
+ return from_string(str.c_str(), error_handler);
+ }
+
+ /// Determine whether the address is a class A address.
+ bool is_class_a() const
+ {
+ return IN_CLASSA(to_ulong());
+ }
+
+ /// Determine whether the address is a class B address.
+ bool is_class_b() const
+ {
+ return IN_CLASSB(to_ulong());
+ }
+
+ /// Determine whether the address is a class C address.
+ bool is_class_c() const
+ {
+ return IN_CLASSC(to_ulong());
+ }
+
+ /// Determine whether the address is a multicast address.
+ bool is_multicast() const
+ {
+ return IN_MULTICAST(to_ulong());
+ }
+
+ /// Compare two addresses for equality.
+ friend bool operator==(const address_v4& a1, const address_v4& a2)
+ {
+ return a1.addr_.s_addr == a2.addr_.s_addr;
+ }
+
+ /// Compare two addresses for inequality.
+ friend bool operator!=(const address_v4& a1, const address_v4& a2)
+ {
+ return a1.addr_.s_addr != a2.addr_.s_addr;
+ }
+
+ /// Compare addresses for ordering.
+ friend bool operator<(const address_v4& a1, const address_v4& a2)
+ {
+ return a1.to_ulong() < a2.to_ulong();
+ }
+
+ /// Compare addresses for ordering.
+ friend bool operator>(const address_v4& a1, const address_v4& a2)
+ {
+ return a1.to_ulong() > a2.to_ulong();
+ }
+
+ /// Compare addresses for ordering.
+ friend bool operator<=(const address_v4& a1, const address_v4& a2)
+ {
+ return a1.to_ulong() <= a2.to_ulong();
+ }
+
+ /// Compare addresses for ordering.
+ friend bool operator>=(const address_v4& a1, const address_v4& a2)
+ {
+ return a1.to_ulong() >= a2.to_ulong();
+ }
+
+ /// Obtain an address object that represents any address.
+ static address_v4 any()
+ {
+ return address_v4(static_cast<unsigned long>(INADDR_ANY));
+ }
+
+ /// Obtain an address object that represents the loopback address.
+ static address_v4 loopback()
+ {
+ return address_v4(static_cast<unsigned long>(INADDR_LOOPBACK));
+ }
+
+ /// Obtain an address object that represents the broadcast address.
+ static address_v4 broadcast()
+ {
+ return address_v4(static_cast<unsigned long>(INADDR_BROADCAST));
+ }
+
+ /// Obtain an address object that represents the broadcast address that
+ /// corresponds to the specified address and netmask.
+ static address_v4 broadcast(const address_v4& addr, const address_v4& mask)
+ {
+ return address_v4(addr.to_ulong() | ~mask.to_ulong());
+ }
+
+ /// Obtain the netmask that corresponds to the address, based on its address
+ /// class.
+ static address_v4 netmask(const address_v4& addr)
+ {
+ if (addr.is_class_a())
+ return address_v4(0xFF000000);
+ if (addr.is_class_b())
+ return address_v4(0xFFFF0000);
+ if (addr.is_class_c())
+ return address_v4(0xFFFFFF00);
+ return address_v4(0xFFFFFFFF);
+ }
+
+private:
+ // The underlying IPv4 address.
+ asio::detail::in4_addr_type addr_;
+};
+
+/// Output an address as a string.
+/**
+ * Used to output a human-readable string for a specified address.
+ *
+ * @param os The output stream to which the string will be written.
+ *
+ * @param addr The address to be written.
+ *
+ * @return The output stream.
+ *
+ * @relates asio::ip::address_v4
+ */
+template <typename Elem, typename Traits>
+std::basic_ostream<Elem, Traits>& operator<<(
+ std::basic_ostream<Elem, Traits>& os, const address_v4& addr)
+{
+ asio::error e;
+ std::string s = addr.to_string(asio::assign_error(e));
+ if (e)
+ os.setstate(std::ios_base::failbit);
+ else
+ for (std::string::iterator i = s.begin(); i != s.end(); ++i)
+ os << os.widen(*i);
+ return os;
+}
+
+} // namespace ip
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_IP_ADDRESS_V4_HPP
diff --git a/library/include/libtorrent/asio/ip/address_v6.hpp b/library/include/libtorrent/asio/ip/address_v6.hpp
new file mode 100644
index 000000000..85f0ebc1a
--- /dev/null
+++ b/library/include/libtorrent/asio/ip/address_v6.hpp
@@ -0,0 +1,381 @@
+//
+// address_v6.hpp
+// ~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_IP_ADDRESS_V6_HPP
+#define ASIO_IP_ADDRESS_V6_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/detail/push_options.hpp"
+#include <cstring>
+#include <string>
+#include <stdexcept>
+#include <boost/array.hpp>
+#include <boost/throw_exception.hpp>
+#include "asio/detail/pop_options.hpp"
+
+#include "asio/error.hpp"
+#include "asio/error_handler.hpp"
+#include "asio/detail/socket_ops.hpp"
+#include "asio/detail/socket_types.hpp"
+#include "asio/ip/address_v4.hpp"
+
+namespace asio {
+namespace ip {
+
+/// Implements IP version 6 style addresses.
+/**
+ * The asio::ip::address_v6 class provides the ability to use and
+ * manipulate IP version 6 addresses.
+ *
+ * @par Thread Safety:
+ * @e Distinct @e objects: Safe.@n
+ * @e Shared @e objects: Unsafe.
+ */
+class address_v6
+{
+public:
+ /// The type used to represent an address as an array of bytes.
+ typedef boost::array<unsigned char, 16> bytes_type;
+
+ /// Default constructor.
+ address_v6()
+ : scope_id_(0)
+ {
+ asio::detail::in6_addr_type tmp_addr = IN6ADDR_ANY_INIT;
+ addr_ = tmp_addr;
+ }
+
+ /// Construct an address from raw bytes and scope ID.
+ explicit address_v6(const bytes_type& bytes, unsigned long scope_id = 0)
+ : scope_id_(scope_id)
+ {
+ using namespace std; // For memcpy.
+ memcpy(addr_.s6_addr, bytes.elems, 16);
+ }
+
+ /// Copy constructor.
+ address_v6(const address_v6& other)
+ : addr_(other.addr_),
+ scope_id_(other.scope_id_)
+ {
+ }
+
+ /// Assign from another address.
+ address_v6& operator=(const address_v6& other)
+ {
+ addr_ = other.addr_;
+ scope_id_ = other.scope_id_;
+ return *this;
+ }
+
+ /// Get the scope ID of the address.
+ unsigned long scope_id() const
+ {
+ return scope_id_;
+ }
+
+ /// Set the scope ID of the address.
+ void scope_id(unsigned long id)
+ {
+ scope_id_ = id;
+ }
+
+ /// Get the address in bytes.
+ bytes_type to_bytes() const
+ {
+ using namespace std; // For memcpy.
+ bytes_type bytes;
+ memcpy(bytes.elems, addr_.s6_addr, 16);
+ return bytes;
+ }
+
+ /// Get the address as a string.
+ std::string to_string() const
+ {
+ return to_string(asio::throw_error());
+ }
+
+ /// Get the address as a string.
+ template <typename Error_Handler>
+ std::string to_string(Error_Handler error_handler) const
+ {
+ char addr_str[asio::detail::max_addr_v6_str_len];
+ const char* addr =
+ asio::detail::socket_ops::inet_ntop(AF_INET6, &addr_, addr_str,
+ asio::detail::max_addr_v6_str_len, scope_id_);
+ if (addr == 0)
+ {
+ asio::error e(asio::detail::socket_ops::get_error());
+ error_handler(e);
+ return std::string();
+ }
+ asio::error e;
+ error_handler(e);
+ return addr;
+ }
+
+ /// Create an address from an IP address string.
+ static address_v6 from_string(const char* str)
+ {
+ return from_string(str, asio::throw_error());
+ }
+
+ /// Create an address from an IP address string.
+ template <typename Error_Handler>
+ static address_v6 from_string(const char* str, Error_Handler error_handler)
+ {
+ address_v6 tmp;
+ if (asio::detail::socket_ops::inet_pton(
+ AF_INET6, str, &tmp.addr_, &tmp.scope_id_) <= 0)
+ {
+ asio::error e(asio::detail::socket_ops::get_error());
+ error_handler(e);
+ return address_v6();
+ }
+ asio::error e;
+ error_handler(e);
+ return tmp;
+ }
+
+ /// Create an address from an IP address string.
+ static address_v6 from_string(const std::string& str)
+ {
+ return from_string(str.c_str(), asio::throw_error());
+ }
+
+ /// Create an address from an IP address string.
+ template <typename Error_Handler>
+ static address_v6 from_string(const std::string& str,
+ Error_Handler error_handler)
+ {
+ return from_string(str.c_str(), error_handler);
+ }
+
+ /// Converts an IPv4-mapped or IPv4-compatible address to an IPv4 address.
+ address_v4 to_v4() const
+ {
+ if (!is_v4_mapped() && !is_v4_compatible())
+ throw std::bad_cast();
+ address_v4::bytes_type v4_bytes = { addr_.s6_addr[12],
+ addr_.s6_addr[13], addr_.s6_addr[14], addr_.s6_addr[15] };
+ return address_v4(v4_bytes);
+ }
+
+ /// Determine whether the address is a loopback address.
+ bool is_loopback() const
+ {
+ using namespace asio::detail;
+ return IN6_IS_ADDR_LOOPBACK(&addr_) != 0;
+ }
+
+ /// Determine whether the address is unspecified.
+ bool is_unspecified() const
+ {
+ using namespace asio::detail;
+ return IN6_IS_ADDR_UNSPECIFIED(&addr_) != 0;
+ }
+
+ /// Determine whether the address is link local.
+ bool is_link_local() const
+ {
+ using namespace asio::detail;
+ return IN6_IS_ADDR_LINKLOCAL(&addr_) != 0;
+ }
+
+ /// Determine whether the address is site local.
+ bool is_site_local() const
+ {
+ using namespace asio::detail;
+ return IN6_IS_ADDR_SITELOCAL(&addr_) != 0;
+ }
+
+ /// Determine whether the address is a mapped IPv4 address.
+ bool is_v4_mapped() const
+ {
+ using namespace asio::detail;
+ return IN6_IS_ADDR_V4MAPPED(&addr_) != 0;
+ }
+
+ /// Determine whether the address is an IPv4-compatible address.
+ bool is_v4_compatible() const
+ {
+ using namespace asio::detail;
+ return IN6_IS_ADDR_V4COMPAT(&addr_) != 0;
+ }
+
+ /// Determine whether the address is a multicast address.
+ bool is_multicast() const
+ {
+ using namespace asio::detail;
+ return IN6_IS_ADDR_MULTICAST(&addr_) != 0;
+ }
+
+ /// Determine whether the address is a global multicast address.
+ bool is_multicast_global() const
+ {
+ using namespace asio::detail;
+ return IN6_IS_ADDR_MC_GLOBAL(&addr_) != 0;
+ }
+
+ /// Determine whether the address is a link-local multicast address.
+ bool is_multicast_link_local() const
+ {
+ using namespace asio::detail;
+ return IN6_IS_ADDR_MC_LINKLOCAL(&addr_) != 0;
+ }
+
+ /// Determine whether the address is a node-local multicast address.
+ bool is_multicast_node_local() const
+ {
+ using namespace asio::detail;
+ return IN6_IS_ADDR_MC_NODELOCAL(&addr_) != 0;
+ }
+
+ /// Determine whether the address is a org-local multicast address.
+ bool is_multicast_org_local() const
+ {
+ using namespace asio::detail;
+ return IN6_IS_ADDR_MC_ORGLOCAL(&addr_) != 0;
+ }
+
+ /// Determine whether the address is a site-local multicast address.
+ bool is_multicast_site_local() const
+ {
+ using namespace asio::detail;
+ return IN6_IS_ADDR_MC_SITELOCAL(&addr_) != 0;
+ }
+
+ /// Compare two addresses for equality.
+ friend bool operator==(const address_v6& a1, const address_v6& a2)
+ {
+ using namespace std; // For memcmp.
+ return memcmp(&a1.addr_, &a2.addr_,
+ sizeof(asio::detail::in6_addr_type)) == 0
+ && a1.scope_id_ == a2.scope_id_;
+ }
+
+ /// Compare two addresses for inequality.
+ friend bool operator!=(const address_v6& a1, const address_v6& a2)
+ {
+ using namespace std; // For memcmp.
+ return memcmp(&a1.addr_, &a2.addr_,
+ sizeof(asio::detail::in6_addr_type)) != 0
+ || a1.scope_id_ != a2.scope_id_;
+ }
+
+ /// Compare addresses for ordering.
+ friend bool operator<(const address_v6& a1, const address_v6& a2)
+ {
+ using namespace std; // For memcmp.
+ int memcmp_result = memcmp(&a1.addr_, &a2.addr_,
+ sizeof(asio::detail::in6_addr_type)) < 0;
+ if (memcmp_result < 0)
+ return true;
+ if (memcmp_result > 0)
+ return false;
+ return a1.scope_id_ < a2.scope_id_;
+ }
+
+ /// Compare addresses for ordering.
+ friend bool operator>(const address_v6& a1, const address_v6& a2)
+ {
+ return a2 < a1;
+ }
+
+ /// Compare addresses for ordering.
+ friend bool operator<=(const address_v6& a1, const address_v6& a2)
+ {
+ return !(a2 < a1);
+ }
+
+ /// Compare addresses for ordering.
+ friend bool operator>=(const address_v6& a1, const address_v6& a2)
+ {
+ return !(a1 < a2);
+ }
+
+ /// Obtain an address object that represents any address.
+ static address_v6 any()
+ {
+ return address_v6();
+ }
+
+ /// Obtain an address object that represents the loopback address.
+ static address_v6 loopback()
+ {
+ address_v6 tmp;
+ asio::detail::in6_addr_type tmp_addr = IN6ADDR_LOOPBACK_INIT;
+ tmp.addr_ = tmp_addr;
+ return tmp;
+ }
+
+ /// Create an IPv4-mapped IPv6 address.
+ static address_v6 v4_mapped(const address_v4& addr)
+ {
+ address_v4::bytes_type v4_bytes = addr.to_bytes();
+ bytes_type v6_bytes = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF,
+ v4_bytes[0], v4_bytes[1], v4_bytes[2], v4_bytes[3] };
+ return address_v6(v6_bytes);
+ }
+
+ /// Create an IPv4-compatible IPv6 address.
+ static address_v6 v4_compatible(const address_v4& addr)
+ {
+ address_v4::bytes_type v4_bytes = addr.to_bytes();
+ bytes_type v6_bytes = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ v4_bytes[0], v4_bytes[1], v4_bytes[2], v4_bytes[3] };
+ return address_v6(v6_bytes);
+ }
+
+private:
+ // The underlying IPv6 address.
+ asio::detail::in6_addr_type addr_;
+
+ // The scope ID associated with the address.
+ unsigned long scope_id_;
+};
+
+/// Output an address as a string.
+/**
+ * Used to output a human-readable string for a specified address.
+ *
+ * @param os The output stream to which the string will be written.
+ *
+ * @param addr The address to be written.
+ *
+ * @return The output stream.
+ *
+ * @relates asio::ip::address_v6
+ */
+template <typename Elem, typename Traits>
+std::basic_ostream<Elem, Traits>& operator<<(
+ std::basic_ostream<Elem, Traits>& os, const address_v6& addr)
+{
+ asio::error e;
+ std::string s = addr.to_string(asio::assign_error(e));
+ if (e)
+ os.setstate(std::ios_base::failbit);
+ else
+ for (std::string::iterator i = s.begin(); i != s.end(); ++i)
+ os << os.widen(*i);
+ return os;
+}
+
+} // namespace ip
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_IP_ADDRESS_V6_HPP
diff --git a/library/include/libtorrent/asio/ip/basic_endpoint.hpp b/library/include/libtorrent/asio/ip/basic_endpoint.hpp
new file mode 100644
index 000000000..56ce99eca
--- /dev/null
+++ b/library/include/libtorrent/asio/ip/basic_endpoint.hpp
@@ -0,0 +1,358 @@
+//
+// basic_endpoint.hpp
+// ~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_IP_BASIC_ENDPOINT_HPP
+#define ASIO_IP_BASIC_ENDPOINT_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/detail/push_options.hpp"
+#include <boost/throw_exception.hpp>
+#include <boost/detail/workaround.hpp>
+#include <cstring>
+#if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564))
+# include <iostream>
+#endif // BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564))
+#include "asio/detail/pop_options.hpp"
+
+#include "asio/error.hpp"
+#include "asio/ip/address.hpp"
+#include "asio/detail/socket_ops.hpp"
+#include "asio/detail/socket_types.hpp"
+
+namespace asio {
+namespace ip {
+
+/// Describes an endpoint for a version-independent IP socket.
+/**
+ * The asio::ip::basic_endpoint class template describes an endpoint that
+ * may be associated with a particular socket.
+ *
+ * @par Thread Safety:
+ * @e Distinct @e objects: Safe.@n
+ * @e Shared @e objects: Unsafe.
+ *
+ * @par Concepts:
+ * Endpoint.
+ */
+template <typename Protocol>
+class basic_endpoint
+{
+public:
+ /// The protocol type associated with the endpoint.
+ typedef Protocol protocol_type;
+
+ /// The type of the endpoint structure. This type is dependent on the
+ /// underlying implementation of the socket layer.
+#if defined(GENERATING_DOCUMENTATION)
+ typedef implementation_defined data_type;
+#else
+ typedef asio::detail::socket_addr_type data_type;
+#endif
+
+ /// The type for the size of the endpoint structure. This type is dependent on
+ /// the underlying implementation of the socket layer.
+#if defined(GENERATING_DOCUMENTATION)
+ typedef implementation_defined size_type;
+#else
+ typedef asio::detail::socket_addr_len_type size_type;
+#endif
+
+ /// Default constructor.
+ basic_endpoint()
+ : data_()
+ {
+ asio::detail::sockaddr_in4_type& data
+ = reinterpret_cast<asio::detail::sockaddr_in4_type&>(data_);
+ data.sin_family = AF_INET;
+ data.sin_port = 0;
+ data.sin_addr.s_addr = INADDR_ANY;
+ }
+
+ /// Construct an endpoint using a port number, specified in the host's byte
+ /// order. The IP address will be the any address (i.e. INADDR_ANY or
+ /// in6addr_any). This constructor would typically be used for accepting new
+ /// connections.
+ /**
+ * @par Examples:
+ * To initialise an IPv4 TCP endpoint for port 1234, use:
+ * @code
+ * asio::ip::tcp::endpoint ep(asio::ip::tcp::v4(), 1234);
+ * @endcode
+ *
+ * To specify an IPv6 UDP endpoint for port 9876, use:
+ * @code
+ * asio::ip::udp::endpoint ep(asio::ip::udp::v6(), 9876);
+ * @endcode
+ */
+ basic_endpoint(const Protocol& protocol, unsigned short port_num)
+ : data_()
+ {
+ using namespace std; // For memcpy.
+ if (protocol.family() == PF_INET)
+ {
+ asio::detail::sockaddr_in4_type& data
+ = reinterpret_cast<asio::detail::sockaddr_in4_type&>(data_);
+ data.sin_family = AF_INET;
+ data.sin_port =
+ asio::detail::socket_ops::host_to_network_short(port_num);
+ data.sin_addr.s_addr = INADDR_ANY;
+ }
+ else
+ {
+ asio::detail::sockaddr_in6_type& data
+ = reinterpret_cast<asio::detail::sockaddr_in6_type&>(data_);
+ data.sin6_family = AF_INET6;
+ data.sin6_port =
+ asio::detail::socket_ops::host_to_network_short(port_num);
+ data.sin6_flowinfo = 0;
+ asio::detail::in6_addr_type tmp_addr = IN6ADDR_ANY_INIT;
+ data.sin6_addr = tmp_addr;
+ data.sin6_scope_id = 0;
+ }
+ }
+
+ /// Construct an endpoint using a port number and an IP address. This
+ /// constructor may be used for accepting connections on a specific interface
+ /// or for making a connection to a remote endpoint.
+ basic_endpoint(const asio::ip::address& addr, unsigned short port_num)
+ : data_()
+ {
+ using namespace std; // For memcpy.
+ if (addr.is_v4())
+ {
+ asio::detail::sockaddr_in4_type& data
+ = reinterpret_cast<asio::detail::sockaddr_in4_type&>(data_);
+ data.sin_family = AF_INET;
+ data.sin_port =
+ asio::detail::socket_ops::host_to_network_short(port_num);
+ data.sin_addr.s_addr =
+ asio::detail::socket_ops::host_to_network_long(
+ addr.to_v4().to_ulong());
+ }
+ else
+ {
+ asio::detail::sockaddr_in6_type& data
+ = reinterpret_cast<asio::detail::sockaddr_in6_type&>(data_);
+ data.sin6_family = AF_INET6;
+ data.sin6_port =
+ asio::detail::socket_ops::host_to_network_short(port_num);
+ data.sin6_flowinfo = 0;
+ asio::ip::address_v6 v6_addr = addr.to_v6();
+ asio::ip::address_v6::bytes_type bytes = v6_addr.to_bytes();
+ memcpy(data.sin6_addr.s6_addr, bytes.elems, 16);
+ data.sin6_scope_id = v6_addr.scope_id();
+ }
+ }
+
+ /// Copy constructor.
+ basic_endpoint(const basic_endpoint& other)
+ : data_(other.data_)
+ {
+ }
+
+ /// Assign from another endpoint.
+ basic_endpoint& operator=(const basic_endpoint& other)
+ {
+ data_ = other.data_;
+ return *this;
+ }
+
+ /// The protocol associated with the endpoint.
+ protocol_type protocol() const
+ {
+ if (data_.ss_family == AF_INET)
+ return Protocol::v4();
+ return Protocol::v6();
+ }
+
+ /// Get the underlying endpoint in the native type.
+ data_type* data()
+ {
+ return reinterpret_cast<data_type*>(&data_);
+ }
+
+ /// Get the underlying endpoint in the native type.
+ const data_type* data() const
+ {
+ return reinterpret_cast<const data_type*>(&data_);
+ }
+
+ /// Get the underlying size of the endpoint in the native type.
+ size_type size() const
+ {
+ if (data_.ss_family == AF_INET)
+ return sizeof(asio::detail::sockaddr_in4_type);
+ else
+ return sizeof(asio::detail::sockaddr_in6_type);
+ }
+
+ /// Set the underlying size of the endpoint in the native type.
+ void resize(size_type size)
+ {
+ if (size > size_type(sizeof(data_)))
+ {
+ asio::error e(asio::error::invalid_argument);
+ boost::throw_exception(e);
+ }
+ }
+
+ /// Get the capacity of the endpoint in the native type.
+ size_type capacity() const
+ {
+ return sizeof(data_);
+ }
+
+ /// Get the port associated with the endpoint. The port number is always in
+ /// the host's byte order.
+ unsigned short port() const
+ {
+ if (data_.ss_family == AF_INET)
+ {
+ return asio::detail::socket_ops::network_to_host_short(
+ reinterpret_cast<const asio::detail::sockaddr_in4_type&>(
+ data_).sin_port);
+ }
+ else
+ {
+ return asio::detail::socket_ops::network_to_host_short(
+ reinterpret_cast<const asio::detail::sockaddr_in6_type&>(
+ data_).sin6_port);
+ }
+ }
+
+ /// Set the port associated with the endpoint. The port number is always in
+ /// the host's byte order.
+ void port(unsigned short port_num)
+ {
+ if (data_.ss_family == AF_INET)
+ {
+ reinterpret_cast<asio::detail::sockaddr_in4_type&>(data_).sin_port
+ = asio::detail::socket_ops::host_to_network_short(port_num);
+ }
+ else
+ {
+ reinterpret_cast<asio::detail::sockaddr_in6_type&>(data_).sin6_port
+ = asio::detail::socket_ops::host_to_network_short(port_num);
+ }
+ }
+
+ /// Get the IP address associated with the endpoint.
+ asio::ip::address address() const
+ {
+ using namespace std; // For memcpy.
+ if (data_.ss_family == AF_INET)
+ {
+ const asio::detail::sockaddr_in4_type& data
+ = reinterpret_cast<const asio::detail::sockaddr_in4_type&>(
+ data_);
+ return asio::ip::address_v4(
+ asio::detail::socket_ops::network_to_host_long(
+ data.sin_addr.s_addr));
+ }
+ else
+ {
+ const asio::detail::sockaddr_in6_type& data
+ = reinterpret_cast<const asio::detail::sockaddr_in6_type&>(
+ data_);
+ asio::ip::address_v6::bytes_type bytes;
+ memcpy(bytes.elems, data.sin6_addr.s6_addr, 16);
+ return asio::ip::address_v6(bytes, data.sin6_scope_id);
+ }
+ }
+
+ /// Set the IP address associated with the endpoint.
+ void address(const asio::ip::address& addr)
+ {
+ basic_endpoint<Protocol> tmp_endpoint(addr, port());
+ data_ = tmp_endpoint.data_;
+ }
+
+ /// Compare two endpoints for equality.
+ friend bool operator==(const basic_endpoint<Protocol>& e1,
+ const basic_endpoint<Protocol>& e2)
+ {
+ return e1.address() == e2.address() && e1.port() == e2.port();
+ }
+
+ /// Compare two endpoints for inequality.
+ friend bool operator!=(const basic_endpoint<Protocol>& e1,
+ const basic_endpoint<Protocol>& e2)
+ {
+ return e1.address() != e2.address() || e1.port() != e2.port();
+ }
+
+ /// Compare endpoints for ordering.
+ friend bool operator<(const basic_endpoint<Protocol>& e1,
+ const basic_endpoint<Protocol>& e2)
+ {
+ if (e1.address() < e2.address())
+ return true;
+ if (e1.address() != e2.address())
+ return false;
+ return e1.port() < e2.port();
+ }
+
+private:
+ // The underlying IP socket address.
+ asio::detail::sockaddr_storage_type data_;
+};
+
+/// Output an endpoint as a string.
+/**
+ * Used to output a human-readable string for a specified endpoint.
+ *
+ * @param os The output stream to which the string will be written.
+ *
+ * @param endpoint The endpoint to be written.
+ *
+ * @return The output stream.
+ *
+ * @relates asio::ip::basic_endpoint
+ */
+#if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564))
+template <typename Protocol>
+std::ostream& operator<<(std::ostream& os,
+ const basic_endpoint<Protocol>& endpoint)
+{
+ const address& addr = endpoint.address();
+ if (addr.is_v4())
+ os << addr.to_string();
+ else
+ os << '[' << addr.to_string() << ']';
+ os << ':' << endpoint.port();
+ return os;
+}
+#else // BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564))
+template <typename Elem, typename Traits, typename Protocol>
+std::basic_ostream<Elem, Traits>& operator<<(
+ std::basic_ostream<Elem, Traits>& os,
+ const basic_endpoint<Protocol>& endpoint)
+{
+ const address& addr = endpoint.address();
+ if (addr.is_v4())
+ os << addr.to_string();
+ else
+ os << '[' << addr.to_string() << ']';
+ os << ':' << endpoint.port();
+ return os;
+}
+#endif // BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564))
+
+} // namespace ip
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_IP_BASIC_ENDPOINT_HPP
diff --git a/library/include/libtorrent/asio/ip/basic_resolver_entry.hpp b/library/include/libtorrent/asio/ip/basic_resolver_entry.hpp
new file mode 100644
index 000000000..6198425ab
--- /dev/null
+++ b/library/include/libtorrent/asio/ip/basic_resolver_entry.hpp
@@ -0,0 +1,98 @@
+//
+// basic_resolver_entry.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_IP_BASIC_RESOLVER_ENTRY_HPP
+#define ASIO_IP_BASIC_RESOLVER_ENTRY_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/detail/push_options.hpp"
+#include <string>
+#include "asio/detail/pop_options.hpp"
+
+namespace asio {
+namespace ip {
+
+/// An entry produced by a resolver.
+/**
+ * The asio::ip::basic_resolver_entry class template describes an entry
+ * as returned by a resolver.
+ *
+ * @par Thread Safety:
+ * @e Distinct @e objects: Safe.@n
+ * @e Shared @e objects: Unsafe.
+ *
+ * @par Concepts:
+ * Endpoint.
+ */
+template <typename Protocol>
+class basic_resolver_entry
+{
+public:
+ /// The protocol type associated with the endpoint entry.
+ typedef Protocol protocol_type;
+
+ /// The endpoint type associated with the endpoint entry.
+ typedef typename Protocol::endpoint endpoint_type;
+
+ /// Default constructor.
+ basic_resolver_entry()
+ {
+ }
+
+ /// Construct with specified endpoint, host name and service name.
+ basic_resolver_entry(const endpoint_type& endpoint,
+ const std::string& host_name, const std::string& service_name)
+ : endpoint_(endpoint),
+ host_name_(host_name),
+ service_name_(service_name)
+ {
+ }
+
+ /// Get the endpoint associated with the entry.
+ endpoint_type endpoint() const
+ {
+ return endpoint_;
+ }
+
+ /// Convert to the endpoint associated with the entry.
+ operator endpoint_type() const
+ {
+ return endpoint_;
+ }
+
+ /// Get the host name associated with the entry.
+ std::string host_name() const
+ {
+ return host_name_;
+ }
+
+ /// Get the service name associated with the entry.
+ std::string service_name() const
+ {
+ return service_name_;
+ }
+
+private:
+ endpoint_type endpoint_;
+ std::string host_name_;
+ std::string service_name_;
+};
+
+} // namespace ip
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_IP_BASIC_RESOLVER_ENTRY_HPP
diff --git a/library/include/libtorrent/asio/ip/basic_resolver_iterator.hpp b/library/include/libtorrent/asio/ip/basic_resolver_iterator.hpp
new file mode 100644
index 000000000..12984ee79
--- /dev/null
+++ b/library/include/libtorrent/asio/ip/basic_resolver_iterator.hpp
@@ -0,0 +1,151 @@
+//
+// basic_resolver_iterator.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_IP_BASIC_RESOLVER_ITERATOR_HPP
+#define ASIO_IP_BASIC_RESOLVER_ITERATOR_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/detail/push_options.hpp"
+#include <boost/iterator/iterator_facade.hpp>
+#include <boost/shared_ptr.hpp>
+#include <cstring>
+#include <string>
+#include <vector>
+#include "asio/detail/pop_options.hpp"
+
+#include "asio/detail/socket_ops.hpp"
+#include "asio/detail/socket_types.hpp"
+#include "asio/ip/basic_resolver_entry.hpp"
+
+namespace asio {
+namespace ip {
+
+/// An iterator over the entries produced by a resolver.
+/**
+ * The asio::ip::basic_resolver_iterator class template is used to define
+ * iterators over the results returned by a resolver.
+ *
+ * The iterator's value_type, obtained when the iterator is dereferenced, is:
+ * @code const basic_resolver_entry<Protocol> @endcode
+ *
+ * @par Thread Safety:
+ * @e Distinct @e objects: Safe.@n
+ * @e Shared @e objects: Unsafe.
+ */
+template <typename Protocol>
+class basic_resolver_iterator
+ : public boost::iterator_facade<
+ basic_resolver_iterator<Protocol>,
+ const basic_resolver_entry<Protocol>,
+ boost::forward_traversal_tag>
+{
+public:
+ /// Default constructor creates an end iterator.
+ basic_resolver_iterator()
+ {
+ }
+
+ /// Create an iterator from an addrinfo list returned by getaddrinfo.
+ static basic_resolver_iterator create(
+ asio::detail::addrinfo_type* address_info,
+ const std::string& host_name, const std::string& service_name)
+ {
+ basic_resolver_iterator iter;
+ if (!address_info)
+ return iter;
+
+ std::string actual_host_name = host_name;
+ if (address_info->ai_canonname)
+ actual_host_name = address_info->ai_canonname;
+
+ iter.values_.reset(new values_type);
+
+ while (address_info)
+ {
+ if (address_info->ai_family == PF_INET
+ || address_info->ai_family == PF_INET6)
+ {
+ using namespace std; // For memcpy.
+ typename Protocol::endpoint endpoint;
+ endpoint.resize(address_info->ai_addrlen);
+ memcpy(endpoint.data(), address_info->ai_addr,
+ address_info->ai_addrlen);
+ iter.values_->push_back(
+ basic_resolver_entry<Protocol>(endpoint,
+ actual_host_name, service_name));
+ }
+ address_info = address_info->ai_next;
+ }
+
+ if (iter.values_->size())
+ iter.iter_ = iter.values_->begin();
+ else
+ iter.values_.reset();
+
+ return iter;
+ }
+
+ /// Create an iterator from an endpoint, host name and service name.
+ static basic_resolver_iterator create(
+ const typename Protocol::endpoint& endpoint,
+ const std::string& host_name, const std::string& service_name)
+ {
+ basic_resolver_iterator iter;
+ iter.values_.reset(new values_type);
+ iter.values_->push_back(
+ basic_resolver_entry<Protocol>(endpoint, host_name, service_name));
+ iter.iter_ = iter.values_->begin();
+ return iter;
+ }
+
+private:
+ friend class boost::iterator_core_access;
+
+ void increment()
+ {
+ 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();
+ }
+ }
+
+ bool equal(const basic_resolver_iterator& other) const
+ {
+ if (!values_ && !other.values_)
+ return true;
+ if (values_ != other.values_)
+ return false;
+ return iter_ == other.iter_;
+ }
+
+ const basic_resolver_entry<Protocol>& dereference() const
+ {
+ return *iter_;
+ }
+
+ typedef std::vector<basic_resolver_entry<Protocol> > values_type;
+ boost::shared_ptr<values_type> values_;
+ typename values_type::const_iterator iter_;
+};
+
+} // namespace ip
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_IP_BASIC_RESOLVER_ITERATOR_HPP
diff --git a/library/include/libtorrent/asio/ip/basic_resolver_query.hpp b/library/include/libtorrent/asio/ip/basic_resolver_query.hpp
new file mode 100644
index 000000000..ddee4b1c3
--- /dev/null
+++ b/library/include/libtorrent/asio/ip/basic_resolver_query.hpp
@@ -0,0 +1,152 @@
+//
+// basic_resolver_query.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_IP_BASIC_RESOLVER_QUERY_HPP
+#define ASIO_IP_BASIC_RESOLVER_QUERY_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/detail/push_options.hpp"
+#include <boost/config.hpp>
+#include <string>
+#include "asio/detail/pop_options.hpp"
+
+#include "asio/detail/socket_ops.hpp"
+#include "asio/ip/resolver_query_base.hpp"
+
+namespace asio {
+namespace ip {
+
+/// An query to be passed to a resolver.
+/**
+ * The asio::ip::basic_resolver_query class template describes a query
+ * that can be passed to a resolver.
+ *
+ * @par Thread Safety:
+ * @e Distinct @e objects: Safe.@n
+ * @e Shared @e objects: Unsafe.
+ *
+ * @par Concepts:
+ * Endpoint.
+ */
+template <typename Protocol>
+class basic_resolver_query
+ : public resolver_query_base
+{
+public:
+ /// The protocol type associated with the endpoint query.
+ typedef Protocol protocol_type;
+
+ /// Construct with specified service name for any protocol.
+ basic_resolver_query(const std::string& service_name,
+ int flags = passive | address_configured)
+ : hints_(),
+ host_name_(),
+ service_name_(service_name)
+ {
+ typename Protocol::endpoint endpoint;
+ hints_.ai_flags = flags;
+ hints_.ai_family = PF_UNSPEC;
+ hints_.ai_socktype = endpoint.protocol().type();
+ hints_.ai_protocol = endpoint.protocol().protocol();
+ hints_.ai_addrlen = 0;
+ hints_.ai_canonname = 0;
+ hints_.ai_addr = 0;
+ hints_.ai_next = 0;
+ }
+
+ /// Construct with specified service name for a given protocol.
+ basic_resolver_query(const protocol_type& protocol,
+ const std::string& service_name,
+ int flags = passive | address_configured)
+ : hints_(),
+ host_name_(),
+ service_name_(service_name)
+ {
+ hints_.ai_flags = flags;
+ hints_.ai_family = protocol.family();
+ hints_.ai_socktype = protocol.type();
+ hints_.ai_protocol = protocol.protocol();
+ hints_.ai_addrlen = 0;
+ hints_.ai_canonname = 0;
+ hints_.ai_addr = 0;
+ hints_.ai_next = 0;
+ }
+
+ /// Construct with specified host name and service name for any protocol.
+ basic_resolver_query(const std::string& host_name,
+ const std::string& service_name, int flags = address_configured)
+ : hints_(),
+ host_name_(host_name),
+ service_name_(service_name)
+ {
+ typename Protocol::endpoint endpoint;
+ hints_.ai_flags = flags;
+ hints_.ai_family = PF_UNSPEC;
+ hints_.ai_socktype = endpoint.protocol().type();
+ hints_.ai_protocol = endpoint.protocol().protocol();
+ hints_.ai_addrlen = 0;
+ hints_.ai_canonname = 0;
+ hints_.ai_addr = 0;
+ hints_.ai_next = 0;
+ }
+
+ /// Construct with specified host name and service name for a given protocol.
+ basic_resolver_query(const protocol_type& protocol,
+ const std::string& host_name, const std::string& service_name,
+ int flags = address_configured)
+ : hints_(),
+ host_name_(host_name),
+ service_name_(service_name)
+ {
+ hints_.ai_flags = flags;
+ hints_.ai_family = protocol.family();
+ hints_.ai_socktype = protocol.type();
+ hints_.ai_protocol = protocol.protocol();
+ hints_.ai_addrlen = 0;
+ hints_.ai_canonname = 0;
+ hints_.ai_addr = 0;
+ hints_.ai_next = 0;
+ }
+
+ /// Get the hints associated with the query.
+ const asio::detail::addrinfo_type& hints() const
+ {
+ return hints_;
+ }
+
+ /// Get the host name associated with the query.
+ std::string host_name() const
+ {
+ return host_name_;
+ }
+
+ /// Get the service name associated with the query.
+ std::string service_name() const
+ {
+ return service_name_;
+ }
+
+private:
+ asio::detail::addrinfo_type hints_;
+ std::string host_name_;
+ std::string service_name_;
+};
+
+} // namespace ip
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_IP_BASIC_RESOLVER_QUERY_HPP
diff --git a/library/include/libtorrent/asio/ip/detail/socket_option.hpp b/library/include/libtorrent/asio/ip/detail/socket_option.hpp
new file mode 100644
index 000000000..ae733e987
--- /dev/null
+++ b/library/include/libtorrent/asio/ip/detail/socket_option.hpp
@@ -0,0 +1,406 @@
+//
+// socket_option.hpp
+// ~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_IP_DETAIL_SOCKET_OPTION_HPP
+#define ASIO_IP_DETAIL_SOCKET_OPTION_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/detail/push_options.hpp"
+#include <cstddef>
+#include <cstring>
+#include <boost/config.hpp>
+#include "asio/detail/pop_options.hpp"
+
+#include "asio/ip/address.hpp"
+#include "asio/detail/socket_ops.hpp"
+#include "asio/detail/socket_types.hpp"
+
+namespace asio {
+namespace ip {
+namespace detail {
+namespace socket_option {
+
+// Helper template for implementing boolean-based options.
+template <int IPv4_Level, int IPv4_Name, int IPv6_Level, int IPv6_Name>
+class boolean
+{
+public:
+ // Default constructor.
+ boolean()
+ : value_(0)
+ {
+ }
+
+ // Construct with a specific option value.
+ boolean(bool value)
+ : value_(value ? 1 : 0)
+ {
+ }
+
+ // Set the value of the boolean.
+ void set(bool value)
+ {
+ value_ = value ? 1 : 0;
+ }
+
+ // Get the current value of the boolean.
+ bool get() const
+ {
+ return value_;
+ }
+
+ // Get the level of the socket option.
+ template <typename Protocol>
+ int level(const Protocol& protocol) const
+ {
+ if (protocol.family() == PF_INET6)
+ return IPv6_Level;
+ return IPv4_Level;
+ }
+
+ // Get the name of the socket option.
+ template <typename Protocol>
+ int name(const Protocol& protocol) const
+ {
+ if (protocol.family() == PF_INET6)
+ return IPv6_Name;
+ return IPv4_Name;
+ }
+
+ // Get the address of the boolean data.
+ template <typename Protocol>
+ int* data(const Protocol&)
+ {
+ return &value_;
+ }
+
+ // Get the address of the boolean data.
+ template <typename Protocol>
+ const int* data(const Protocol&) const
+ {
+ return &value_;
+ }
+
+ // Get the size of the boolean data.
+ template <typename Protocol>
+ std::size_t size(const Protocol&) const
+ {
+ return sizeof(value_);
+ }
+
+private:
+ int value_;
+};
+
+// Helper template for implementing integer options.
+template <int IPv4_Level, int IPv4_Name, int IPv6_Level, int IPv6_Name>
+class integer
+{
+public:
+ // Default constructor.
+ integer()
+ : value_(0)
+ {
+ }
+
+ // Construct with a specific option value.
+ integer(int value)
+ : value_(value)
+ {
+ }
+
+ // Set the value of the int option.
+ void set(int value)
+ {
+ value_ = value;
+ }
+
+ // Get the current value of the int option.
+ int get() const
+ {
+ return value_;
+ }
+
+ // Get the level of the socket option.
+ template <typename Protocol>
+ int level(const Protocol& protocol) const
+ {
+ if (protocol.family() == PF_INET6)
+ return IPv6_Level;
+ return IPv4_Level;
+ }
+
+ // Get the name of the socket option.
+ template <typename Protocol>
+ int name(const Protocol& protocol) const
+ {
+ if (protocol.family() == PF_INET6)
+ return IPv6_Name;
+ return IPv4_Name;
+ }
+
+ // Get the address of the int data.
+ template <typename Protocol>
+ int* data(const Protocol&)
+ {
+ return &value_;
+ }
+
+ // Get the address of the int data.
+ template <typename Protocol>
+ const int* data(const Protocol&) const
+ {
+ return &value_;
+ }
+
+ // Get the size of the int data.
+ template <typename Protocol>
+ std::size_t size(const Protocol&) const
+ {
+ return sizeof(value_);
+ }
+
+private:
+ int value_;
+};
+
+// Helper template for implementing ip_mreq-based options.
+template <int IPv4_Level, int IPv4_Name, int IPv6_Level, int IPv6_Name>
+class multicast_request
+{
+public:
+ // Default constructor.
+ multicast_request()
+ {
+ ipv4_value_.imr_multiaddr.s_addr =
+ asio::detail::socket_ops::host_to_network_long(
+ asio::ip::address_v4::any().to_ulong());
+ ipv4_value_.imr_interface.s_addr =
+ asio::detail::socket_ops::host_to_network_long(
+ asio::ip::address_v4::any().to_ulong());
+
+ asio::detail::in6_addr_type tmp_addr = IN6ADDR_ANY_INIT;
+ ipv6_value_.ipv6mr_multiaddr = tmp_addr;
+ ipv6_value_.ipv6mr_interface = 0;
+ }
+
+ // Construct with multicast address only.
+ multicast_request(const asio::ip::address& multicast_address)
+ {
+ if (multicast_address.is_v6())
+ {
+ ipv4_value_.imr_multiaddr.s_addr =
+ asio::detail::socket_ops::host_to_network_long(
+ asio::ip::address_v4::any().to_ulong());
+ ipv4_value_.imr_interface.s_addr =
+ asio::detail::socket_ops::host_to_network_long(
+ asio::ip::address_v4::any().to_ulong());
+
+ using namespace std; // For memcpy.
+ asio::ip::address_v6 ipv6_address = multicast_address.to_v6();
+ asio::ip::address_v6::bytes_type bytes = ipv6_address.to_bytes();
+ memcpy(ipv6_value_.ipv6mr_multiaddr.s6_addr, bytes.elems, 16);
+ ipv6_value_.ipv6mr_interface = 0;
+ }
+ else
+ {
+ ipv4_value_.imr_multiaddr.s_addr =
+ asio::detail::socket_ops::host_to_network_long(
+ multicast_address.to_v4().to_ulong());
+ ipv4_value_.imr_interface.s_addr =
+ asio::detail::socket_ops::host_to_network_long(
+ asio::ip::address_v4::any().to_ulong());
+
+ asio::detail::in6_addr_type tmp_addr = IN6ADDR_ANY_INIT;
+ ipv6_value_.ipv6mr_multiaddr = tmp_addr;
+ ipv6_value_.ipv6mr_interface = 0;
+ }
+ }
+
+ // Construct with multicast address and IPv4 address specifying an interface.
+ multicast_request(const asio::ip::address_v4& multicast_address,
+ const asio::ip::address_v4& network_interface
+ = asio::ip::address_v4::any())
+ {
+ ipv4_value_.imr_multiaddr.s_addr =
+ asio::detail::socket_ops::host_to_network_long(
+ multicast_address.to_ulong());
+ ipv4_value_.imr_interface.s_addr =
+ asio::detail::socket_ops::host_to_network_long(
+ network_interface.to_ulong());
+
+ asio::detail::in6_addr_type tmp_addr = IN6ADDR_ANY_INIT;
+ ipv6_value_.ipv6mr_multiaddr = tmp_addr;
+ ipv6_value_.ipv6mr_interface = 0;
+ }
+
+ // Construct with multicast address and IPv6 network interface index.
+ multicast_request(const asio::ip::address_v6& multicast_address,
+ unsigned long network_interface = 0)
+ {
+ ipv4_value_.imr_multiaddr.s_addr =
+ asio::detail::socket_ops::host_to_network_long(
+ asio::ip::address_v4::any().to_ulong());
+ ipv4_value_.imr_interface.s_addr =
+ asio::detail::socket_ops::host_to_network_long(
+ asio::ip::address_v4::any().to_ulong());
+
+ using namespace std; // For memcpy.
+ asio::ip::address_v6::bytes_type bytes =
+ multicast_address.to_bytes();
+ memcpy(ipv6_value_.ipv6mr_multiaddr.s6_addr, bytes.elems, 16);
+ ipv6_value_.ipv6mr_interface = network_interface;
+ }
+
+ // Get the level of the socket option.
+ template <typename Protocol>
+ int level(const Protocol& protocol) const
+ {
+ if (protocol.family() == PF_INET6)
+ return IPv6_Level;
+ return IPv4_Level;
+ }
+
+ // Get the name of the socket option.
+ template <typename Protocol>
+ int name(const Protocol& protocol) const
+ {
+ if (protocol.family() == PF_INET6)
+ return IPv6_Name;
+ return IPv4_Name;
+ }
+
+ // Get the address of the option data.
+ template <typename Protocol>
+ void* data(const Protocol& protocol)
+ {
+ if (protocol.family() == PF_INET6)
+ return &ipv6_value_;
+ return &ipv4_value_;
+ }
+
+ // Get the address of the option data.
+ template <typename Protocol>
+ const void* data(const Protocol& protocol) const
+ {
+ if (protocol.family() == PF_INET6)
+ return &ipv6_value_;
+ return &ipv4_value_;
+ }
+
+ // Get the size of the option data.
+ template <typename Protocol>
+ std::size_t size(const Protocol& protocol) const
+ {
+ if (protocol.family() == PF_INET6)
+ return sizeof(ipv6_value_);
+ return sizeof(ipv4_value_);
+ }
+
+private:
+ asio::detail::in4_mreq_type ipv4_value_;
+ asio::detail::in6_mreq_type ipv6_value_;
+};
+
+// Helper template for implementing options that specify a network interface.
+template <int IPv4_Level, int IPv4_Name, int IPv6_Level, int IPv6_Name>
+class network_interface
+{
+public:
+ // Default constructor.
+ network_interface()
+ {
+ ipv4_value_.s_addr =
+ asio::detail::socket_ops::host_to_network_long(
+ asio::ip::address_v4::any().to_ulong());
+ ipv6_value_ = 0;
+ }
+
+ // Construct with IPv4 interface.
+ network_interface(const asio::ip::address_v4& ipv4_interface)
+ {
+ ipv4_value_.s_addr =
+ asio::detail::socket_ops::host_to_network_long(
+ ipv4_interface.to_ulong());
+ ipv6_value_ = 0;
+ }
+
+ // Construct with IPv6 interface.
+ network_interface(unsigned long ipv6_interface)
+ {
+ ipv4_value_.s_addr =
+ asio::detail::socket_ops::host_to_network_long(
+ asio::ip::address_v4::any().to_ulong());
+ ipv6_value_ = ipv6_interface;
+ }
+
+ // Get the level of the socket option.
+ template <typename Protocol>
+ int level(const Protocol& protocol) const
+ {
+ if (protocol.family() == PF_INET6)
+ return IPv6_Level;
+ return IPv4_Level;
+ }
+
+ // Get the name of the socket option.
+ template <typename Protocol>
+ int name(const Protocol& protocol) const
+ {
+ if (protocol.family() == PF_INET6)
+ return IPv6_Name;
+ return IPv4_Name;
+ }
+
+ // Get the address of the option data.
+ template <typename Protocol>
+ void* data(const Protocol& protocol)
+ {
+ if (protocol.family() == PF_INET6)
+ return &ipv6_value_;
+ return &ipv4_value_;
+ }
+
+ // Get the address of the option data.
+ template <typename Protocol>
+ const void* data(const Protocol& protocol) const
+ {
+ if (protocol.family() == PF_INET6)
+ return &ipv6_value_;
+ return &ipv4_value_;
+ }
+
+ // Get the size of the option data.
+ template <typename Protocol>
+ std::size_t size(const Protocol& protocol) const
+ {
+ if (protocol.family() == PF_INET6)
+ return sizeof(ipv6_value_);
+ return sizeof(ipv4_value_);
+ }
+
+private:
+ asio::detail::in4_addr_type ipv4_value_;
+ unsigned long ipv6_value_;
+};
+
+} // namespace socket_option
+} // namespace detail
+} // namespace ip
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_IP_DETAIL_SOCKET_OPTION_HPP
diff --git a/library/include/libtorrent/asio/ip/host_name.hpp b/library/include/libtorrent/asio/ip/host_name.hpp
new file mode 100644
index 000000000..23c906a6a
--- /dev/null
+++ b/library/include/libtorrent/asio/ip/host_name.hpp
@@ -0,0 +1,60 @@
+//
+// host_name.hpp
+// ~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_IP_HOST_NAME_HPP
+#define ASIO_IP_HOST_NAME_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/detail/push_options.hpp"
+#include <string>
+#include "asio/detail/pop_options.hpp"
+
+#include "asio/error_handler.hpp"
+#include "asio/detail/socket_ops.hpp"
+
+namespace asio {
+namespace ip {
+
+/// Get the current host name.
+std::string host_name();
+
+/// Get the current host name.
+template <typename Error_Handler>
+std::string host_name(Error_Handler error_handler);
+
+inline std::string host_name()
+{
+ return host_name(asio::throw_error());
+}
+
+template <typename Error_Handler>
+std::string host_name(Error_Handler error_handler)
+{
+ char name[1024];
+ if (asio::detail::socket_ops::gethostname(name, sizeof(name)) != 0)
+ {
+ asio::error error(asio::detail::socket_ops::get_error());
+ error_handler(error);
+ return std::string();
+ }
+ return std::string(name);
+}
+
+} // namespace ip
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_IP_HOST_NAME_HPP
diff --git a/library/include/libtorrent/asio/ip/multicast.hpp b/library/include/libtorrent/asio/ip/multicast.hpp
new file mode 100644
index 000000000..616e9020c
--- /dev/null
+++ b/library/include/libtorrent/asio/ip/multicast.hpp
@@ -0,0 +1,181 @@
+//
+// multicast.hpp
+// ~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_IP_MULTICAST_HPP
+#define ASIO_IP_MULTICAST_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/detail/push_options.hpp"
+#include <cstddef>
+#include <boost/config.hpp>
+#include "asio/detail/pop_options.hpp"
+
+#include "asio/ip/detail/socket_option.hpp"
+
+namespace asio {
+namespace ip {
+namespace multicast {
+
+/// Socket option to join a multicast group on a specified interface.
+/**
+ * Implements the IPPROTO_IP/IP_ADD_MEMBERSHIP socket option.
+ *
+ * @par Examples:
+ * Setting the option to join a multicast group:
+ * @code
+ * asio::ip::udp::socket socket(io_service);
+ * ...
+ * asio::ip::address multicast_address =
+ * asio::ip::address::from_string("225.0.0.1");
+ * asio::ip::multicast::join_group option(multicast_address);
+ * socket.set_option(option);
+ * @endcode
+ *
+ * @par Concepts:
+ * Socket_Option, IP_MReq_Socket_Option.
+ */
+#if defined(GENERATING_DOCUMENTATION)
+typedef implementation_defined join_group;
+#else
+typedef asio::ip::detail::socket_option::multicast_request<
+ IPPROTO_IP, IP_ADD_MEMBERSHIP, IPPROTO_IPV6, IPV6_JOIN_GROUP> join_group;
+#endif
+
+/// Socket option to leave a multicast group on a specified interface.
+/**
+ * Implements the IPPROTO_IP/IP_DROP_MEMBERSHIP socket option.
+ *
+ * @par Examples:
+ * Setting the option to leave a multicast group:
+ * @code
+ * asio::ip::udp::socket socket(io_service);
+ * ...
+ * asio::ip::address multicast_address =
+ * asio::ip::address::from_string("225.0.0.1");
+ * asio::ip::multicast::leave_group option(multicast_address);
+ * socket.set_option(option);
+ * @endcode
+ *
+ * @par Concepts:
+ * Socket_Option, IP_MReq_Socket_Option.
+ */
+#if defined(GENERATING_DOCUMENTATION)
+typedef implementation_defined leave_group;
+#else
+typedef asio::ip::detail::socket_option::multicast_request<
+ IPPROTO_IP, IP_DROP_MEMBERSHIP, IPPROTO_IPV6, IPV6_LEAVE_GROUP> leave_group;
+#endif
+
+/// Socket option for local interface to use for outgoing multicast packets.
+/**
+ * Implements the IPPROTO_IP/IP_MULTICAST_IF socket option.
+ *
+ * @par Examples:
+ * Setting the option:
+ * @code
+ * asio::ip::udp::socket socket(io_service);
+ * ...
+ * asio::ip::address_v4 local_interface =
+ * asio::ip::address_v4::from_string("1.2.3.4");
+ * asio::ip::multicast::outbound_interface option(local_interface);
+ * socket.set_option(option);
+ * @endcode
+ *
+ * @par Concepts:
+ * Socket_Option, IP_Network_Interface_Socket_Option.
+ */
+#if defined(GENERATING_DOCUMENTATION)
+typedef implementation_defined outbound_interface;
+#else
+typedef asio::ip::detail::socket_option::network_interface<
+ IPPROTO_IP, IP_MULTICAST_IF, IPPROTO_IPV6, IPV6_MULTICAST_IF>
+ outbound_interface;
+#endif
+
+/// Socket option for time-to-live associated with outgoing multicast packets.
+/**
+ * Implements the IPPROTO_IP/IP_MULTICAST_TTL socket option.
+ *
+ * @par Examples:
+ * Setting the option:
+ * @code
+ * asio::ip::udp::socket socket(io_service);
+ * ...
+ * asio::ip::multicast::hops option(4);
+ * socket.set_option(option);
+ * @endcode
+ *
+ * @par
+ * Getting the current option value:
+ * @code
+ * asio::ip::udp::socket socket(io_service);
+ * ...
+ * asio::ip::multicast::hops option;
+ * socket.get_option(option);
+ * int ttl = option.get();
+ * @endcode
+ *
+ * @par Concepts:
+ * Socket_Option, Integer_Socket_Option.
+ */
+#if defined(GENERATING_DOCUMENTATION)
+typedef implementation_defined hops;
+#else
+typedef asio::ip::detail::socket_option::integer<
+ IPPROTO_IP, IP_MULTICAST_TTL, IPPROTO_IPV6, IPV6_MULTICAST_HOPS> hops;
+#endif
+
+/// Socket option determining whether outgoing multicast packets will be
+/// received on the same socket if it is a member of the multicast group.
+/**
+ * Implements the IPPROTO_IP/IP_MULTICAST_LOOP socket option.
+ *
+ * @par Examples:
+ * Setting the option:
+ * @code
+ * asio::ip::udp::socket socket(io_service);
+ * ...
+ * asio::ip::multicast::enable_loopback option(true);
+ * socket.set_option(option);
+ * @endcode
+ *
+ * @par
+ * Getting the current option value:
+ * @code
+ * asio::ip::udp::socket socket(io_service);
+ * ...
+ * asio::ip::multicast::enable_loopback option;
+ * socket.get_option(option);
+ * bool is_set = option.get();
+ * @endcode
+ *
+ * @par Concepts:
+ * Socket_Option, Boolean_Socket_Option.
+ */
+#if defined(GENERATING_DOCUMENTATION)
+typedef implementation_defined enable_loopback;
+#else
+typedef asio::ip::detail::socket_option::boolean<
+ IPPROTO_IP, IP_MULTICAST_LOOP, IPPROTO_IPV6, IPV6_MULTICAST_LOOP>
+ enable_loopback;
+#endif
+
+} // namespace multicast
+} // namespace ip
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_IP_MULTICAST_HPP
diff --git a/library/include/libtorrent/asio/ip/resolver_query_base.hpp b/library/include/libtorrent/asio/ip/resolver_query_base.hpp
new file mode 100644
index 000000000..a8c7ad6dc
--- /dev/null
+++ b/library/include/libtorrent/asio/ip/resolver_query_base.hpp
@@ -0,0 +1,107 @@
+//
+// resolver_query_base.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_IP_RESOLVER_QUERY_BASE_HPP
+#define ASIO_IP_RESOLVER_QUERY_BASE_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/detail/push_options.hpp"
+#include <boost/config.hpp>
+#include <boost/detail/workaround.hpp>
+#include "asio/detail/pop_options.hpp"
+
+#include "asio/detail/socket_types.hpp"
+
+namespace asio {
+namespace ip {
+
+/// The resolver_query_base class is used as a base for the
+/// basic_resolver_query class templates to provide a common place to define
+/// the flag constants.
+class resolver_query_base
+{
+public:
+#if defined(GENERATING_DOCUMENTATION)
+ /// Determine the canonical name of the host specified in the query.
+ static const int canonical_name = implementation_defined;
+
+ /// Indicate that returned endpoint is intended for use as a locally bound
+ /// socket endpoint.
+ static const int passive = implementation_defined;
+
+ /// Host name should be treated as a numeric string defining an IPv4 or IPv6
+ /// address and no name resolution should be attempted.
+ static const int numeric_host = implementation_defined;
+
+ /// Service name should be treated as a numeric string defining a port number
+ /// and no name resolution should be attempted.
+ static const int numeric_service = implementation_defined;
+
+ /// If the query protocol family is specified as IPv6, return IPv4-mapped
+ /// IPv6 addresses on finding no IPv6 addresses.
+ static const int v4_mapped = implementation_defined;
+
+ /// If used with v4_mapped, return all matching IPv6 and IPv4 addresses.
+ static const int all_matching = implementation_defined;
+
+ /// Only return IPv4 addresses if a non-loopback IPv4 address is configured
+ /// for the system. Only return IPv6 addresses if a non-loopback IPv6 address
+ /// is configured for the system.
+ static const int address_configured = implementation_defined;
+#else
+ BOOST_STATIC_CONSTANT(int, canonical_name = AI_CANONNAME);
+ BOOST_STATIC_CONSTANT(int, passive = AI_PASSIVE);
+ BOOST_STATIC_CONSTANT(int, numeric_host = AI_NUMERICHOST);
+# if defined(AI_NUMERICSERV)
+ BOOST_STATIC_CONSTANT(int, numeric_service = AI_NUMERICSERV);
+# else
+ BOOST_STATIC_CONSTANT(int, numeric_service = 0);
+# endif
+# if defined(AI_V4MAPPED)
+ BOOST_STATIC_CONSTANT(int, v4_mapped = AI_V4MAPPED);
+# else
+ BOOST_STATIC_CONSTANT(int, v4_mapped = 0);
+# endif
+# if defined(AI_ALL)
+ BOOST_STATIC_CONSTANT(int, all_matching = AI_ALL);
+# else
+ BOOST_STATIC_CONSTANT(int, all_matching = 0);
+# endif
+# if defined(AI_ADDRCONFIG)
+ BOOST_STATIC_CONSTANT(int, address_configured = AI_ADDRCONFIG);
+# else
+ BOOST_STATIC_CONSTANT(int, address_configured = 0);
+# endif
+#endif
+
+protected:
+ /// Protected destructor to prevent deletion through this type.
+ ~resolver_query_base()
+ {
+ }
+
+#if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564))
+private:
+ // Workaround to enable the empty base optimisation with Borland C++.
+ char dummy_;
+#endif
+};
+
+} // namespace ip
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_IP_RESOLVER_QUERY_BASE_HPP
diff --git a/library/include/libtorrent/asio/ip/tcp.hpp b/library/include/libtorrent/asio/ip/tcp.hpp
new file mode 100644
index 000000000..92532f12e
--- /dev/null
+++ b/library/include/libtorrent/asio/ip/tcp.hpp
@@ -0,0 +1,146 @@
+//
+// tcp.hpp
+// ~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_IP_TCP_HPP
+#define ASIO_IP_TCP_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/basic_resolver.hpp"
+#include "asio/basic_socket_acceptor.hpp"
+#include "asio/basic_socket_iostream.hpp"
+#include "asio/basic_stream_socket.hpp"
+#include "asio/ip/basic_endpoint.hpp"
+#include "asio/ip/basic_resolver_iterator.hpp"
+#include "asio/ip/basic_resolver_query.hpp"
+#include "asio/detail/socket_option.hpp"
+#include "asio/detail/socket_types.hpp"
+
+namespace asio {
+namespace ip {
+
+/// Encapsulates the flags needed for TCP.
+/**
+ * The asio::ip::tcp class contains flags necessary for TCP sockets.
+ *
+ * @par Thread Safety:
+ * @e Distinct @e objects: Safe.@n
+ * @e Shared @e objects: Safe.
+ *
+ * @par Concepts:
+ * Protocol.
+ */
+class tcp
+{
+public:
+ /// The type of a TCP endpoint.
+ typedef basic_endpoint<tcp> endpoint;
+
+ /// The type of a resolver query.
+ typedef basic_resolver_query<tcp> resolver_query;
+
+ /// The type of a resolver iterator.
+ typedef basic_resolver_iterator<tcp> resolver_iterator;
+
+ /// Construct to represent the IPv4 TCP protocol.
+ static tcp v4()
+ {
+ return tcp(PF_INET);
+ }
+
+ /// Construct to represent the IPv4 TCP protocol.
+ static tcp v6()
+ {
+ return tcp(PF_INET6);
+ }
+
+ /// Obtain an identifier for the type of the protocol.
+ int type() const
+ {
+ return SOCK_STREAM;
+ }
+
+ /// Obtain an identifier for the protocol.
+ int protocol() const
+ {
+ return IPPROTO_TCP;
+ }
+
+ /// Obtain an identifier for the protocol family.
+ int family() const
+ {
+ return family_;
+ }
+
+ /// The TCP socket type.
+ typedef basic_stream_socket<tcp> socket;
+
+ /// The TCP acceptor type.
+ typedef basic_socket_acceptor<tcp> acceptor;
+
+ /// The TCP resolver type.
+ typedef basic_resolver<tcp> resolver;
+
+ /// The TCP iostream type.
+ typedef basic_socket_iostream<tcp> iostream;
+
+ /// Socket option for disabling the Nagle algorithm.
+ /**
+ * Implements the IPPROTO_TCP/TCP_NODELAY socket option.
+ *
+ * @par Examples:
+ * Setting the option:
+ * @code
+ * asio::ipv6::tcp::socket socket(io_service);
+ * ...
+ * asio::ipv6::tcp::no_delay option(true);
+ * socket.set_option(option);
+ * @endcode
+ *
+ * @par
+ * Getting the current option value:
+ * @code
+ * asio::ipv6::tcp::socket socket(io_service);
+ * ...
+ * asio::ipv6::tcp::no_delay option;
+ * socket.get_option(option);
+ * bool is_set = option.get();
+ * @endcode
+ *
+ * @par Concepts:
+ * Socket_Option, Boolean_Socket_Option.
+ */
+#if defined(GENERATING_DOCUMENTATION)
+ typedef implementation_defined no_delay;
+#else
+ typedef asio::detail::socket_option::boolean<
+ IPPROTO_TCP, TCP_NODELAY> no_delay;
+#endif
+
+private:
+ // Construct with a specific family.
+ explicit tcp(int family)
+ : family_(family)
+ {
+ }
+
+ int family_;
+};
+
+} // namespace ip
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_IP_TCP_HPP
diff --git a/library/include/libtorrent/asio/ip/udp.hpp b/library/include/libtorrent/asio/ip/udp.hpp
new file mode 100644
index 000000000..7cc822615
--- /dev/null
+++ b/library/include/libtorrent/asio/ip/udp.hpp
@@ -0,0 +1,104 @@
+//
+// udp.hpp
+// ~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_IP_UDP_HPP
+#define ASIO_IP_UDP_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/basic_datagram_socket.hpp"
+#include "asio/basic_resolver.hpp"
+#include "asio/ip/basic_endpoint.hpp"
+#include "asio/ip/basic_resolver_iterator.hpp"
+#include "asio/ip/basic_resolver_query.hpp"
+#include "asio/detail/socket_types.hpp"
+
+namespace asio {
+namespace ip {
+
+/// Encapsulates the flags needed for UDP.
+/**
+ * The asio::ip::udp class contains flags necessary for UDP sockets.
+ *
+ * @par Thread Safety:
+ * @e Distinct @e objects: Safe.@n
+ * @e Shared @e objects: Safe.
+ *
+ * @par Concepts:
+ * Protocol.
+ */
+class udp
+{
+public:
+ /// The type of a UDP endpoint.
+ typedef basic_endpoint<udp> endpoint;
+
+ /// The type of a resolver query.
+ typedef basic_resolver_query<udp> resolver_query;
+
+ /// The type of a resolver iterator.
+ typedef basic_resolver_iterator<udp> resolver_iterator;
+
+ /// Construct to represent the IPv4 UDP protocol.
+ static udp v4()
+ {
+ return udp(PF_INET);
+ }
+
+ /// Construct to represent the IPv4 UDP protocol.
+ static udp v6()
+ {
+ return udp(PF_INET6);
+ }
+
+ /// Obtain an identifier for the type of the protocol.
+ int type() const
+ {
+ return SOCK_DGRAM;
+ }
+
+ /// Obtain an identifier for the protocol.
+ int protocol() const
+ {
+ return IPPROTO_UDP;
+ }
+
+ /// Obtain an identifier for the protocol family.
+ int family() const
+ {
+ return family_;
+ }
+
+ /// The IPv4 UDP socket type.
+ typedef basic_datagram_socket<udp> socket;
+
+ /// The UDP resolver type.
+ typedef basic_resolver<udp> resolver;
+
+private:
+ // Construct with a specific family.
+ explicit udp(int family)
+ : family_(family)
+ {
+ }
+
+ int family_;
+};
+
+} // namespace ip
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_IP_UDP_HPP
diff --git a/library/include/libtorrent/asio/is_read_buffered.hpp b/library/include/libtorrent/asio/is_read_buffered.hpp
new file mode 100644
index 000000000..7c9c2b70f
--- /dev/null
+++ b/library/include/libtorrent/asio/is_read_buffered.hpp
@@ -0,0 +1,62 @@
+//
+// is_read_buffered.hpp
+// ~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_IS_READ_BUFFERED_HPP
+#define ASIO_IS_READ_BUFFERED_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/detail/push_options.hpp"
+#include <boost/config.hpp>
+#include "asio/detail/pop_options.hpp"
+
+#include "asio/buffered_read_stream_fwd.hpp"
+#include "asio/buffered_stream_fwd.hpp"
+
+namespace asio {
+
+namespace detail {
+
+template <typename Stream>
+char is_read_buffered_helper(buffered_stream<Stream>* s);
+
+template <typename Stream>
+char is_read_buffered_helper(buffered_read_stream<Stream>* s);
+
+struct is_read_buffered_big_type { char data[10]; };
+is_read_buffered_big_type is_read_buffered_helper(...);
+
+} // namespace detail
+
+/// The is_read_buffered class is a traits class that may be used to determine
+/// whether a stream type supports buffering of read data.
+template <typename Stream>
+class is_read_buffered
+{
+public:
+#if defined(GENERATING_DOCUMENTATION)
+ /// The value member is true only if the Stream type supports buffering of
+ /// read data.
+ static const bool value;
+#else
+ BOOST_STATIC_CONSTANT(bool,
+ value = sizeof(detail::is_read_buffered_helper((Stream*)0)) == 1);
+#endif
+};
+
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_IS_READ_BUFFERED_HPP
diff --git a/library/include/libtorrent/asio/is_write_buffered.hpp b/library/include/libtorrent/asio/is_write_buffered.hpp
new file mode 100644
index 000000000..d0221180d
--- /dev/null
+++ b/library/include/libtorrent/asio/is_write_buffered.hpp
@@ -0,0 +1,62 @@
+//
+// is_write_buffered.hpp
+// ~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_IS_WRITE_BUFFERED_HPP
+#define ASIO_IS_WRITE_BUFFERED_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/detail/push_options.hpp"
+#include <boost/config.hpp>
+#include "asio/detail/pop_options.hpp"
+
+#include "asio/buffered_stream_fwd.hpp"
+#include "asio/buffered_write_stream_fwd.hpp"
+
+namespace asio {
+
+namespace detail {
+
+template <typename Stream>
+char is_write_buffered_helper(buffered_stream<Stream>* s);
+
+template <typename Stream>
+char is_write_buffered_helper(buffered_write_stream<Stream>* s);
+
+struct is_write_buffered_big_type { char data[10]; };
+is_write_buffered_big_type is_write_buffered_helper(...);
+
+} // namespace detail
+
+/// The is_write_buffered class is a traits class that may be used to determine
+/// whether a stream type supports buffering of written data.
+template <typename Stream>
+class is_write_buffered
+{
+public:
+#if defined(GENERATING_DOCUMENTATION)
+ /// The value member is true only if the Stream type supports buffering of
+ /// written data.
+ static const bool value;
+#else
+ BOOST_STATIC_CONSTANT(bool,
+ value = sizeof(detail::is_write_buffered_helper((Stream*)0)) == 1);
+#endif
+};
+
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_IS_WRITE_BUFFERED_HPP
diff --git a/library/include/libtorrent/asio/placeholders.hpp b/library/include/libtorrent/asio/placeholders.hpp
new file mode 100644
index 000000000..04727d786
--- /dev/null
+++ b/library/include/libtorrent/asio/placeholders.hpp
@@ -0,0 +1,80 @@
+//
+// placeholders.hpp
+// ~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_ARG_HPP
+#define ASIO_ARG_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/detail/push_options.hpp"
+#include <boost/bind/arg.hpp>
+#include "asio/detail/pop_options.hpp"
+
+namespace asio {
+
+namespace placeholders {
+
+namespace {
+
+#if defined(__BORLANDC__)
+
+static inline boost::arg<1> error()
+{
+ return boost::arg<1>();
+}
+
+static inline boost::arg<2> bytes_transferred()
+{
+ return boost::arg<2>();
+}
+
+static inline boost::arg<2> iterator()
+{
+ return boost::arg<2>();
+}
+
+#elif defined(_MSC_VER) && (_MSC_VER < 1400)
+
+static boost::arg<1> error;
+static boost::arg<2> bytes_transferred;
+static boost::arg<2> iterator;
+
+#else
+
+/// An argument placeholder, for use with @ref boost_bind, that corresponds to
+/// the error argument of a handler for any of the asynchronous functions.
+boost::arg<1> error;
+
+/// An argument placeholder, for use with @ref boost_bind, that corresponds to
+/// the bytes_transferred argument of a handler for asynchronous functions such
+/// as asio::basic_stream_socket::async_write_some or
+/// asio::async_write.
+boost::arg<2> bytes_transferred;
+
+/// An argument placeholder, for use with @ref boost_bind, that corresponds to
+/// the iterator argument of a handler for asynchronous functions such as
+/// asio::basic_resolver::resolve.
+boost::arg<2> iterator;
+
+#endif
+
+} // namespace
+
+} // namespace placeholders
+
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_ARG_HPP
diff --git a/library/include/libtorrent/asio/read.hpp b/library/include/libtorrent/asio/read.hpp
new file mode 100644
index 000000000..612dfcae8
--- /dev/null
+++ b/library/include/libtorrent/asio/read.hpp
@@ -0,0 +1,547 @@
+//
+// read.hpp
+// ~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_READ_HPP
+#define ASIO_READ_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/detail/push_options.hpp"
+#include <cstddef>
+#include <boost/config.hpp>
+#include "asio/detail/pop_options.hpp"
+
+#include "asio/basic_streambuf.hpp"
+
+namespace asio {
+
+/**
+ * @defgroup read asio::read
+ */
+/*@{*/
+
+/// Attempt to read a certain amount of data from a stream before returning.
+/**
+ * This function is used to read a certain number of bytes of data from a
+ * stream. The call will block until one of the following conditions is true:
+ *
+ * @li The supplied buffers are full. That is, the bytes transferred is equal to
+ * the sum of the buffer sizes.
+ *
+ * @li An error occurred.
+ *
+ * This operation is implemented in terms of one or more calls to the stream's
+ * read_some function.
+ *
+ * @param s The stream from which the data is to be read. The type must support
+ * the Sync_Read_Stream concept.
+ *
+ * @param buffers One or more buffers into which the data will be read. The sum
+ * of the buffer sizes indicates the maximum number of bytes to read from the
+ * stream.
+ *
+ * @returns The number of bytes transferred.
+ *
+ * @throws Sync_Read_Stream::error_type Thrown on failure.
+ *
+ * @par Example:
+ * To read into a single data buffer use the @ref buffer function as follows:
+ * @code asio::read(s, asio::buffer(data, size)); @endcode
+ * See the @ref buffer documentation for information on reading into multiple
+ * buffers in one go, and how to use it with arrays, boost::array or
+ * std::vector.
+ *
+ * @note This overload is equivalent to calling:
+ * @code asio::read(
+ * s, buffers,
+ * asio::transfer_all(),
+ * asio::throw_error()); @endcode
+ */
+template <typename Sync_Read_Stream, typename Mutable_Buffers>
+std::size_t read(Sync_Read_Stream& s, const Mutable_Buffers& buffers);
+
+/// Attempt to read a certain amount of data from a stream before returning.
+/**
+ * This function is used to read a certain number of bytes of data from a
+ * stream. The call will block until one of the following conditions is true:
+ *
+ * @li The supplied buffers are full. That is, the bytes transferred is equal to
+ * the sum of the buffer sizes.
+ *
+ * @li The completion_condition function object returns true.
+ *
+ * This operation is implemented in terms of one or more calls to the stream's
+ * read_some function.
+ *
+ * @param s The stream from which the data is to be read. The type must support
+ * the Sync_Read_Stream concept.
+ *
+ * @param buffers One or more buffers into which the data will be read. The sum
+ * of the buffer sizes indicates the maximum number of bytes to read from the
+ * stream.
+ *
+ * @param completion_condition The function object to be called to determine
+ * whether the read operation is complete. The signature of the function object
+ * must be:
+ * @code bool completion_condition(
+ * const Sync_Read_Stream::error_type& error, // Result of latest read_some
+ * // operation.
+ *
+ * std::size_t bytes_transferred // Number of bytes transferred
+ * // so far.
+ * ); @endcode
+ * A return value of true indicates that the read operation is complete. False
+ * indicates that further calls to the stream's read_some function are required.
+ *
+ * @returns The number of bytes transferred.
+ *
+ * @throws Sync_Read_Stream::error_type Thrown on failure.
+ *
+ * @par Example:
+ * To read into a single data buffer use the @ref buffer function as follows:
+ * @code asio::read(s, asio::buffer(data, size),
+ * asio::transfer_at_least(32)); @endcode
+ * See the @ref buffer documentation for information on reading into multiple
+ * buffers in one go, and how to use it with arrays, boost::array or
+ * std::vector.
+ *
+ * @note This overload is equivalent to calling:
+ * @code asio::read(
+ * s, buffers, completion_condition,
+ * asio::throw_error()); @endcode
+ */
+template <typename Sync_Read_Stream, typename Mutable_Buffers,
+ typename Completion_Condition>
+std::size_t read(Sync_Read_Stream& s, const Mutable_Buffers& buffers,
+ Completion_Condition completion_condition);
+
+/// Attempt to read a certain amount of data from a stream before returning.
+/**
+ * This function is used to read a certain number of bytes of data from a
+ * stream. The call will block until one of the following conditions is true:
+ *
+ * @li The supplied buffers are full. That is, the bytes transferred is equal to
+ * the sum of the buffer sizes.
+ *
+ * @li The completion_condition function object returns true.
+ *
+ * This operation is implemented in terms of one or more calls to the stream's
+ * read_some function.
+ *
+ * @param s The stream from which the data is to be read. The type must support
+ * the Sync_Read_Stream concept.
+ *
+ * @param buffers One or more buffers into which the data will be read. The sum
+ * of the buffer sizes indicates the maximum number of bytes to read from the
+ * stream.
+ *
+ * @param completion_condition The function object to be called to determine
+ * whether the read operation is complete. The signature of the function object
+ * must be:
+ * @code bool completion_condition(
+ * const Sync_Read_Stream::error_type& error, // Result of latest read_some
+ * // operation.
+ *
+ * std::size_t bytes_transferred // Number of bytes transferred
+ * // so far.
+ * ); @endcode
+ * A return value of true indicates that the read operation is complete. False
+ * indicates that further calls to the stream's read_some function are required.
+ *
+ * @param error_handler A handler to be called when the operation completes,
+ * to indicate whether or not an error has occurred. Copies will be made of
+ * the handler as required. The function signature of the handler must be:
+ * @code void error_handler(
+ * const Sync_Read_Stream::error_type& error // Result of operation.
+ * ); @endcode
+ * The error handler is only called if the completion_condition indicates that
+ * the operation is complete.
+ *
+ * @returns The number of bytes read. If an error occurs, and the error handler
+ * does not throw an exception, returns the total number of bytes successfully
+ * transferred prior to the error.
+ */
+template <typename Sync_Read_Stream, typename Mutable_Buffers,
+ typename Completion_Condition, typename Error_Handler>
+std::size_t read(Sync_Read_Stream& s, const Mutable_Buffers& buffers,
+ Completion_Condition completion_condition, Error_Handler error_handler);
+
+/// Attempt to read a certain amount of data from a stream before returning.
+/**
+ * This function is used to read a certain number of bytes of data from a
+ * stream. The call will block until one of the following conditions is true:
+ *
+ * @li An error occurred.
+ *
+ * This operation is implemented in terms of one or more calls to the stream's
+ * read_some function.
+ *
+ * @param s The stream from which the data is to be read. The type must support
+ * the Sync_Read_Stream concept.
+ *
+ * @param b The basic_streambuf object into which the data will be read.
+ *
+ * @returns The number of bytes transferred.
+ *
+ * @throws Sync_Read_Stream::error_type Thrown on failure.
+ *
+ * @note This overload is equivalent to calling:
+ * @code asio::read(
+ * s, b,
+ * asio::transfer_all(),
+ * asio::throw_error()); @endcode
+ */
+template <typename Sync_Read_Stream, typename Allocator>
+std::size_t read(Sync_Read_Stream& s, basic_streambuf<Allocator>& b);
+
+/// Attempt to read a certain amount of data from a stream before returning.
+/**
+ * This function is used to read a certain number of bytes of data from a
+ * stream. The call will block until one of the following conditions is true:
+ *
+ * @li The completion_condition function object returns true.
+ *
+ * This operation is implemented in terms of one or more calls to the stream's
+ * read_some function.
+ *
+ * @param s The stream from which the data is to be read. The type must support
+ * the Sync_Read_Stream concept.
+ *
+ * @param b The basic_streambuf object into which the data will be read.
+ *
+ * @param completion_condition The function object to be called to determine
+ * whether the read operation is complete. The signature of the function object
+ * must be:
+ * @code bool completion_condition(
+ * const Sync_Read_Stream::error_type& error, // Result of latest read_some
+ * // operation.
+ *
+ * std::size_t bytes_transferred // Number of bytes transferred
+ * // so far.
+ * ); @endcode
+ * A return value of true indicates that the read operation is complete. False
+ * indicates that further calls to the stream's read_some function are required.
+ *
+ * @returns The number of bytes transferred.
+ *
+ * @throws Sync_Read_Stream::error_type Thrown on failure.
+ *
+ * @note This overload is equivalent to calling:
+ * @code asio::read(
+ * s, b, completion_condition,
+ * asio::throw_error()); @endcode
+ */
+template <typename Sync_Read_Stream, typename Allocator,
+ typename Completion_Condition>
+std::size_t read(Sync_Read_Stream& s, basic_streambuf<Allocator>& b,
+ Completion_Condition completion_condition);
+
+/// Attempt to read a certain amount of data from a stream before returning.
+/**
+ * This function is used to read a certain number of bytes of data from a
+ * stream. The call will block until one of the following conditions is true:
+ *
+ * @li The completion_condition function object returns true.
+ *
+ * This operation is implemented in terms of one or more calls to the stream's
+ * read_some function.
+ *
+ * @param s The stream from which the data is to be read. The type must support
+ * the Sync_Read_Stream concept.
+ *
+ * @param b The basic_streambuf object into which the data will be read.
+ *
+ * @param completion_condition The function object to be called to determine
+ * whether the read operation is complete. The signature of the function object
+ * must be:
+ * @code bool completion_condition(
+ * const Sync_Read_Stream::error_type& error, // Result of latest read_some
+ * // operation.
+ *
+ * std::size_t bytes_transferred // Number of bytes transferred
+ * // so far.
+ * ); @endcode
+ * A return value of true indicates that the read operation is complete. False
+ * indicates that further calls to the stream's read_some function are required.
+ *
+ * @param error_handler A handler to be called when the operation completes,
+ * to indicate whether or not an error has occurred. Copies will be made of
+ * the handler as required. The function signature of the handler must be:
+ * @code void error_handler(
+ * const Sync_Read_Stream::error_type& error // Result of operation.
+ * ); @endcode
+ * The error handler is only called if the completion_condition indicates that
+ * the operation is complete.
+ *
+ * @returns The number of bytes read. If an error occurs, and the error handler
+ * does not throw an exception, returns the total number of bytes successfully
+ * transferred prior to the error.
+ */
+template <typename Sync_Read_Stream, typename Allocator,
+ typename Completion_Condition, typename Error_Handler>
+std::size_t read(Sync_Read_Stream& s, basic_streambuf<Allocator>& b,
+ Completion_Condition completion_condition, Error_Handler error_handler);
+
+/*@}*/
+/**
+ * @defgroup async_read asio::async_read
+ */
+/*@{*/
+
+/// Start an asynchronous operation to read a certain amount of data from a
+/// stream.
+/**
+ * This function is used to asynchronously read a certain number of bytes of
+ * data from a stream. The function call always returns immediately. The
+ * asynchronous operation will continue until one of the following conditions is
+ * true:
+ *
+ * @li The supplied buffers are full. That is, the bytes transferred is equal to
+ * the sum of the buffer sizes.
+ *
+ * @li An error occurred.
+ *
+ * This operation is implemented in terms of one or more calls to the stream's
+ * async_read_some function.
+ *
+ * @param s The stream from which the data is to be read. The type must support
+ * the Async_Read_Stream concept.
+ *
+ * @param buffers One or more buffers into which the data will be read. The sum
+ * of the buffer sizes indicates the maximum number of bytes to read from the
+ * stream. Although the buffers object may be copied as necessary, ownership of
+ * the underlying memory blocks is retained by the caller, which must guarantee
+ * that they remain valid until the handler is called.
+ *
+ * @param handler The handler to be called when the read operation completes.
+ * Copies will be made of the handler as required. The function signature of the
+ * handler must be:
+ * @code void handler(
+ * const Async_Read_Stream::error_type& error, // Result of operation.
+ *
+ * std::size_t bytes_transferred // Number of bytes copied into
+ * // the buffers. If an error
+ * // occurred, this will be the
+ * // number of bytes successfully
+ * // transferred prior to the
+ * // error.
+ * ); @endcode
+ * Regardless of whether the asynchronous operation completes immediately or
+ * not, the handler will not be invoked from within this function. Invocation of
+ * the handler will be performed in a manner equivalent to using
+ * asio::io_service::post().
+ *
+ * @par Example:
+ * To read into a single data buffer use the @ref buffer function as follows:
+ * @code
+ * asio::async_read(s, asio::buffer(data, size), handler);
+ * @endcode
+ * See the @ref buffer documentation for information on reading into multiple
+ * buffers in one go, and how to use it with arrays, boost::array or
+ * std::vector.
+ *
+ * @note This overload is equivalent to calling:
+ * @code asio::async_read(
+ * s, buffers,
+ * asio::transfer_all(),
+ * handler); @endcode
+ */
+template <typename Async_Read_Stream, typename Mutable_Buffers,
+ typename Handler>
+void async_read(Async_Read_Stream& s, const Mutable_Buffers& buffers,
+ Handler handler);
+
+/// Start an asynchronous operation to read a certain amount of data from a
+/// stream.
+/**
+ * This function is used to asynchronously read a certain number of bytes of
+ * data from a stream. The function call always returns immediately. The
+ * asynchronous operation will continue until one of the following conditions is
+ * true:
+ *
+ * @li The supplied buffers are full. That is, the bytes transferred is equal to
+ * the sum of the buffer sizes.
+ *
+ * @li The completion_condition function object returns true.
+ *
+ * @param s The stream from which the data is to be read. The type must support
+ * the Async_Read_Stream concept.
+ *
+ * @param buffers One or more buffers into which the data will be read. The sum
+ * of the buffer sizes indicates the maximum number of bytes to read from the
+ * stream. Although the buffers object may be copied as necessary, ownership of
+ * the underlying memory blocks is retained by the caller, which must guarantee
+ * that they remain valid until the handler is called.
+ *
+ * @param completion_condition The function object to be called to determine
+ * whether the read operation is complete. The signature of the function object
+ * must be:
+ * @code bool completion_condition(
+ * const Async_Read_Stream::error_type& error, // Result of latest read_some
+ * // operation.
+ *
+ * std::size_t bytes_transferred // Number of bytes transferred
+ * // so far.
+ * ); @endcode
+ * A return value of true indicates that the read operation is complete. False
+ * indicates that further calls to the stream's async_read_some function are
+ * required.
+ *
+ * @param handler The handler to be called when the read operation completes.
+ * Copies will be made of the handler as required. The function signature of the
+ * handler must be:
+ * @code void handler(
+ * const Async_Read_Stream::error_type& error, // Result of operation.
+ *
+ * std::size_t bytes_transferred // Number of bytes copied into
+ * // the buffers. If an error
+ * // occurred, this will be the
+ * // number of bytes successfully
+ * // transferred prior to the
+ * // error.
+ * ); @endcode
+ * Regardless of whether the asynchronous operation completes immediately or
+ * not, the handler will not be invoked from within this function. Invocation of
+ * the handler will be performed in a manner equivalent to using
+ * asio::io_service::post().
+ *
+ * @par Example:
+ * To read into a single data buffer use the @ref buffer function as follows:
+ * @code asio::async_read(s,
+ * asio::buffer(data, size),
+ * asio::transfer_at_least(32),
+ * handler); @endcode
+ * See the @ref buffer documentation for information on reading into multiple
+ * buffers in one go, and how to use it with arrays, boost::array or
+ * std::vector.
+ */
+template <typename Async_Read_Stream, typename Mutable_Buffers,
+ typename Completion_Condition, typename Handler>
+void async_read(Async_Read_Stream& s, const Mutable_Buffers& buffers,
+ Completion_Condition completion_condition, Handler handler);
+
+/// Start an asynchronous operation to read a certain amount of data from a
+/// stream.
+/**
+ * This function is used to asynchronously read a certain number of bytes of
+ * data from a stream. The function call always returns immediately. The
+ * asynchronous operation will continue until one of the following conditions is
+ * true:
+ *
+ * @li An error occurred.
+ *
+ * @param s The stream from which the data is to be read. The type must support
+ * the Async_Read_Stream concept.
+ *
+ * This operation is implemented in terms of one or more calls to the stream's
+ * async_read_some function.
+ *
+ * @param b A basic_streambuf object into which the data will be read. Ownership
+ * of the streambuf is retained by the caller, which must guarantee that it
+ * remains valid until the handler is called.
+ *
+ * @param handler The handler to be called when the read operation completes.
+ * Copies will be made of the handler as required. The function signature of the
+ * handler must be:
+ * @code void handler(
+ * const Async_Read_Stream::error_type& error, // Result of operation.
+ *
+ * std::size_t bytes_transferred // Number of bytes copied into
+ * // the buffers. If an error
+ * // occurred, this will be the
+ * // number of bytes successfully
+ * // transferred prior to the
+ * // error.
+ * ); @endcode
+ * Regardless of whether the asynchronous operation completes immediately or
+ * not, the handler will not be invoked from within this function. Invocation of
+ * the handler will be performed in a manner equivalent to using
+ * asio::io_service::post().
+ *
+ * @note This overload is equivalent to calling:
+ * @code asio::async_read(
+ * s, b,
+ * asio::transfer_all(),
+ * handler); @endcode
+ */
+template <typename Async_Read_Stream, typename Allocator, typename Handler>
+void async_read(Async_Read_Stream& s, basic_streambuf<Allocator>& b,
+ Handler handler);
+
+/// Start an asynchronous operation to read a certain amount of data from a
+/// stream.
+/**
+ * This function is used to asynchronously read a certain number of bytes of
+ * data from a stream. The function call always returns immediately. The
+ * asynchronous operation will continue until one of the following conditions is
+ * true:
+ *
+ * @li The completion_condition function object returns true.
+ *
+ * This operation is implemented in terms of one or more calls to the stream's
+ * async_read_some function.
+ *
+ * @param s The stream from which the data is to be read. The type must support
+ * the Async_Read_Stream concept.
+ *
+ * @param b A basic_streambuf object into which the data will be read. Ownership
+ * of the streambuf is retained by the caller, which must guarantee that it
+ * remains valid until the handler is called.
+ *
+ * @param completion_condition The function object to be called to determine
+ * whether the read operation is complete. The signature of the function object
+ * must be:
+ * @code bool completion_condition(
+ * const Async_Read_Stream::error_type& error, // Result of latest read_some
+ * // operation.
+ *
+ * std::size_t bytes_transferred // Number of bytes transferred
+ * // so far.
+ * ); @endcode
+ * A return value of true indicates that the read operation is complete. False
+ * indicates that further calls to the stream's async_read_some function are
+ * required.
+ *
+ * @param handler The handler to be called when the read operation completes.
+ * Copies will be made of the handler as required. The function signature of the
+ * handler must be:
+ * @code void handler(
+ * const Async_Read_Stream::error_type& error, // Result of operation.
+ *
+ * std::size_t bytes_transferred // Number of bytes copied into
+ * // the buffers. If an error
+ * // occurred, this will be the
+ * // number of bytes successfully
+ * // transferred prior to the
+ * // error.
+ * ); @endcode
+ * Regardless of whether the asynchronous operation completes immediately or
+ * not, the handler will not be invoked from within this function. Invocation of
+ * the handler will be performed in a manner equivalent to using
+ * asio::io_service::post().
+ */
+template <typename Async_Read_Stream, typename Allocator,
+ typename Completion_Condition, typename Handler>
+void async_read(Async_Read_Stream& s, basic_streambuf<Allocator>& b,
+ Completion_Condition completion_condition, Handler handler);
+
+/*@}*/
+
+} // namespace asio
+
+#include "asio/impl/read.ipp"
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_READ_HPP
diff --git a/library/include/libtorrent/asio/read_until.hpp b/library/include/libtorrent/asio/read_until.hpp
new file mode 100644
index 000000000..473b02613
--- /dev/null
+++ b/library/include/libtorrent/asio/read_until.hpp
@@ -0,0 +1,487 @@
+//
+// read_until.hpp
+// ~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_READ_UNTIL_HPP
+#define ASIO_READ_UNTIL_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/detail/push_options.hpp"
+#include <cstddef>
+#include <boost/config.hpp>
+#include <boost/regex.hpp>
+#include <string>
+#include "asio/detail/pop_options.hpp"
+
+#include "asio/basic_streambuf.hpp"
+
+namespace asio {
+
+/**
+ * @defgroup read_until asio::read_until
+ */
+/*@{*/
+
+/// Read data into a streambuf until a delimiter is encountered.
+/**
+ * This function is used to read data into the specified streambuf until the
+ * streambuf's get area contains the specified delimiter. The call will block
+ * until one of the following conditions is true:
+ *
+ * @li The get area of the streambuf contains the specified delimiter.
+ *
+ * @li An error occurred.
+ *
+ * This operation is implemented in terms of zero or more calls to the stream's
+ * read_some function. If the streambuf's get area already contains the
+ * delimiter, the function returns immediately.
+ *
+ * @param s The stream from which the data is to be read. The type must support
+ * the Sync_Read_Stream concept.
+ *
+ * @param b A streambuf object into which the data will be read.
+ *
+ * @param delim The delimiter character.
+ *
+ * @returns The number of bytes in the streambuf's get area up to and including
+ * the delimiter.
+ *
+ * @throws Sync_Read_Stream::error_type Thrown on failure.
+ *
+ * @par Example:
+ * To read data into a streambuf until a newline is encountered:
+ * @code asio::streambuf b;
+ * asio::read_until(s, b, '\n');
+ * std::istream is(&b);
+ * std::string line;
+ * std::getline(is, line); @endcode
+ *
+ * @note This overload is equivalent to calling:
+ * @code asio::read_until(
+ * s, b, delim,
+ * asio::throw_error()); @endcode
+ */
+template <typename Sync_Read_Stream, typename Allocator>
+std::size_t read_until(Sync_Read_Stream& s,
+ asio::basic_streambuf<Allocator>& b, char delim);
+
+/// Read data into a streambuf until a delimiter is encountered.
+/**
+ * This function is used to read data into the specified streambuf until the
+ * streambuf's get area contains the specified delimiter. The call will block
+ * until one of the following conditions is true:
+ *
+ * @li The get area of the streambuf contains the specified delimiter.
+ *
+ * @li An error occurred.
+ *
+ * This operation is implemented in terms of zero or more calls to the stream's
+ * read_some function. If the streambuf's get area already contains the
+ * delimiter, the function returns immediately.
+ *
+ * @param s The stream from which the data is to be read. The type must support
+ * the Sync_Read_Stream concept.
+ *
+ * @param b A streambuf object into which the data will be read.
+ *
+ * @param delim The delimiter character.
+ *
+ * @param error_handler A handler to be called when the operation completes,
+ * to indicate whether or not an error has occurred. Copies will be made of
+ * the handler as required. The function signature of the handler must be:
+ * @code void error_handler(
+ * const Sync_Read_Stream::error_type& error // Result of operation.
+ * ); @endcode
+ * The error handler is only called if the completion_condition indicates that
+ * the operation is complete.
+ *
+ * @returns The number of bytes in the streambuf's get area up to and including
+ * the delimiter. Returns 0 if an error occurred and the error handler did not
+ * throw an exception.
+ */
+template <typename Sync_Read_Stream, typename Allocator, typename Error_Handler>
+std::size_t read_until(Sync_Read_Stream& s,
+ asio::basic_streambuf<Allocator>& b, char delim,
+ Error_Handler error_handler);
+
+/// Read data into a streambuf until a delimiter is encountered.
+/**
+ * This function is used to read data into the specified streambuf until the
+ * streambuf's get area contains the specified delimiter. The call will block
+ * until one of the following conditions is true:
+ *
+ * @li The get area of the streambuf contains the specified delimiter.
+ *
+ * @li An error occurred.
+ *
+ * This operation is implemented in terms of zero or more calls to the stream's
+ * read_some function. If the streambuf's get area already contains the
+ * delimiter, the function returns immediately.
+ *
+ * @param s The stream from which the data is to be read. The type must support
+ * the Sync_Read_Stream concept.
+ *
+ * @param b A streambuf object into which the data will be read.
+ *
+ * @param delim The delimiter string.
+ *
+ * @returns The number of bytes in the streambuf's get area up to and including
+ * the delimiter.
+ *
+ * @throws Sync_Read_Stream::error_type Thrown on failure.
+ *
+ * @par Example:
+ * To read data into a streambuf until a newline is encountered:
+ * @code asio::streambuf b;
+ * asio::read_until(s, b, "\r\n");
+ * std::istream is(&b);
+ * std::string line;
+ * std::getline(is, line); @endcode
+ *
+ * @note This overload is equivalent to calling:
+ * @code asio::read_until(
+ * s, b, delim,
+ * asio::throw_error()); @endcode
+ */
+template <typename Sync_Read_Stream, typename Allocator>
+std::size_t read_until(Sync_Read_Stream& s,
+ asio::basic_streambuf<Allocator>& b, const std::string& delim);
+
+/// Read data into a streambuf until a delimiter is encountered.
+/**
+ * This function is used to read data into the specified streambuf until the
+ * streambuf's get area contains the specified delimiter. The call will block
+ * until one of the following conditions is true:
+ *
+ * @li The get area of the streambuf contains the specified delimiter.
+ *
+ * @li An error occurred.
+ *
+ * This operation is implemented in terms of zero or more calls to the stream's
+ * read_some function. If the streambuf's get area already contains the
+ * delimiter, the function returns immediately.
+ *
+ * @param s The stream from which the data is to be read. The type must support
+ * the Sync_Read_Stream concept.
+ *
+ * @param b A streambuf object into which the data will be read.
+ *
+ * @param delim The delimiter string.
+ *
+ * @param error_handler A handler to be called when the operation completes,
+ * to indicate whether or not an error has occurred. Copies will be made of
+ * the handler as required. The function signature of the handler must be:
+ * @code void error_handler(
+ * const Sync_Read_Stream::error_type& error // Result of operation.
+ * ); @endcode
+ * The error handler is only called if the completion_condition indicates that
+ * the operation is complete.
+ *
+ * @returns The number of bytes in the streambuf's get area up to and including
+ * the delimiter. Returns 0 if an error occurred and the error handler did not
+ * throw an exception.
+ */
+template <typename Sync_Read_Stream, typename Allocator, typename Error_Handler>
+std::size_t read_until(Sync_Read_Stream& s,
+ asio::basic_streambuf<Allocator>& b, const std::string& delim,
+ Error_Handler error_handler);
+
+/// Read data into a streambuf until a regular expression is located.
+/**
+ * This function is used to read data into the specified streambuf until the
+ * streambuf's get area contains some data that matches a regular expression.
+ * The call will block until one of the following conditions is true:
+ *
+ * @li A substring of the streambuf's get area matches the regular expression.
+ *
+ * @li An error occurred.
+ *
+ * This operation is implemented in terms of zero or more calls to the stream's
+ * read_some function. If the streambuf's get area already contains data that
+ * matches the regular expression, the function returns immediately.
+ *
+ * @param s The stream from which the data is to be read. The type must support
+ * the Sync_Read_Stream concept.
+ *
+ * @param b A streambuf object into which the data will be read.
+ *
+ * @param expr The regular expression.
+ *
+ * @returns The number of bytes in the streambuf's get area up to and including
+ * the substring that matches the regular expression.
+ *
+ * @throws Sync_Read_Stream::error_type Thrown on failure.
+ *
+ * @par Example:
+ * To read data into a streambuf until a CR-LF sequence is encountered:
+ * @code asio::streambuf b;
+ * asio::read_until(s, b, boost::regex("\r\n"));
+ * std::istream is(&b);
+ * std::string line;
+ * std::getline(is, line); @endcode
+ *
+ * @note This overload is equivalent to calling:
+ * @code asio::read_until(
+ * s, b, expr,
+ * asio::throw_error()); @endcode
+ */
+template <typename Sync_Read_Stream, typename Allocator>
+std::size_t read_until(Sync_Read_Stream& s,
+ asio::basic_streambuf<Allocator>& b, const boost::regex& expr);
+
+/// Read data into a streambuf until a regular expression is located.
+/**
+ * This function is used to read data into the specified streambuf until the
+ * streambuf's get area contains some data that matches a regular expression.
+ * The call will block until one of the following conditions is true:
+ *
+ * @li A substring of the streambuf's get area matches the regular expression.
+ *
+ * @li An error occurred.
+ *
+ * This operation is implemented in terms of zero or more calls to the stream's
+ * read_some function. If the streambuf's get area already contains data that
+ * matches the regular expression, the function returns immediately.
+ *
+ * @param s The stream from which the data is to be read. The type must support
+ * the Sync_Read_Stream concept.
+ *
+ * @param b A streambuf object into which the data will be read.
+ *
+ * @param expr The regular expression.
+ *
+ * @param error_handler A handler to be called when the operation completes,
+ * to indicate whether or not an error has occurred. Copies will be made of
+ * the handler as required. The function signature of the handler must be:
+ * @code void error_handler(
+ * const Sync_Read_Stream::error_type& error // Result of operation.
+ * ); @endcode
+ * The error handler is only called if the completion_condition indicates that
+ * the operation is complete.
+ *
+ * @returns The number of bytes in the streambuf's get area up to and including
+* the substring that matches the regular expression.
+*/
+template <typename Sync_Read_Stream, typename Allocator, typename Error_Handler>
+std::size_t read_until(Sync_Read_Stream& s,
+ asio::basic_streambuf<Allocator>& b, const boost::regex& expr,
+ Error_Handler error_handler);
+
+/*@}*/
+/**
+* @defgroup async_read_until asio::async_read_until
+*/
+/*@{*/
+
+/// Start an asynchronous operation to read data into a streambuf until a
+/// delimiter is encountered.
+/**
+ * This function is used to asynchronously read data into the specified
+ * streambuf until the streambuf's get area contains the specified delimiter.
+ * The function call always returns immediately. The asynchronous operation
+ * will continue until one of the following conditions is true:
+ *
+ * @li The get area of the streambuf contains the specified delimiter.
+ *
+ * @li An error occurred.
+ *
+ * This operation is implemented in terms of zero or more calls to the stream's
+ * async_read_some function. If the streambuf's get area already contains the
+ * delimiter, the asynchronous operation completes immediately.
+ *
+ * @param s The stream from which the data is to be read. The type must support
+ * the Async_Read_Stream concept.
+ *
+ * @param b A streambuf object into which the data will be read. Ownership of
+ * the streambuf is retained by the caller, which must guarantee that it remains
+ * valid until the handler is called.
+ *
+ * @param delim The delimiter character.
+ *
+ * @param handler The handler to be called when the read operation completes.
+ * Copies will be made of the handler as required. The function signature of the
+ * handler must be:
+ * @code void handler(
+ * const Async_Read_Stream::error_type& error, // Result of operation.
+ *
+ * std::size_t bytes_transferred // The number of bytes in the
+ * // streambuf's get area up to
+ * // and including the delimiter.
+ * // 0 if an error occurred.
+ * ); @endcode
+ * Regardless of whether the asynchronous operation completes immediately or
+ * not, the handler will not be invoked from within this function. Invocation of
+ * the handler will be performed in a manner equivalent to using
+ * asio::io_service::post().
+ *
+ * @par Example:
+ * To asynchronously read data into a streambuf until a newline is encountered:
+ * @code asio::streambuf b;
+ * ...
+ * void handler(const asio::error& e, std::size_t size)
+ * {
+ * if (!e)
+ * {
+ * std::istream is(&b);
+ * std::string line;
+ * std::getline(is, line);
+ * ...
+ * }
+ * }
+ * ...
+ * asio::async_read_until(s, b, '\n', handler); @endcode
+ */
+template <typename Async_Read_Stream, typename Allocator, typename Handler>
+void async_read_until(Async_Read_Stream& s,
+ asio::basic_streambuf<Allocator>& b, char delim, Handler handler);
+
+/// Start an asynchronous operation to read data into a streambuf until a
+/// delimiter is encountered.
+/**
+ * This function is used to asynchronously read data into the specified
+ * streambuf until the streambuf's get area contains the specified delimiter.
+ * The function call always returns immediately. The asynchronous operation
+ * will continue until one of the following conditions is true:
+ *
+ * @li The get area of the streambuf contains the specified delimiter.
+ *
+ * @li An error occurred.
+ *
+ * This operation is implemented in terms of zero or more calls to the stream's
+ * async_read_some function. If the streambuf's get area already contains the
+ * delimiter, the asynchronous operation completes immediately.
+ *
+ * @param s The stream from which the data is to be read. The type must support
+ * the Async_Read_Stream concept.
+ *
+ * @param b A streambuf object into which the data will be read. Ownership of
+ * the streambuf is retained by the caller, which must guarantee that it remains
+ * valid until the handler is called.
+ *
+ * @param delim The delimiter string.
+ *
+ * @param handler The handler to be called when the read operation completes.
+ * Copies will be made of the handler as required. The function signature of the
+ * handler must be:
+ * @code void handler(
+ * const Async_Read_Stream::error_type& error, // Result of operation.
+ *
+ * std::size_t bytes_transferred // The number of bytes in the
+ * // streambuf's get area up to
+ * // and including the delimiter.
+ * // 0 if an error occurred.
+ * ); @endcode
+ * Regardless of whether the asynchronous operation completes immediately or
+ * not, the handler will not be invoked from within this function. Invocation of
+ * the handler will be performed in a manner equivalent to using
+ * asio::io_service::post().
+ *
+ * @par Example:
+ * To asynchronously read data into a streambuf until a newline is encountered:
+ * @code asio::streambuf b;
+ * ...
+ * void handler(const asio::error& e, std::size_t size)
+ * {
+ * if (!e)
+ * {
+ * std::istream is(&b);
+ * std::string line;
+ * std::getline(is, line);
+ * ...
+ * }
+ * }
+ * ...
+ * asio::async_read_until(s, b, "\r\n", handler); @endcode
+ */
+template <typename Async_Read_Stream, typename Allocator, typename Handler>
+void async_read_until(Async_Read_Stream& s,
+ asio::basic_streambuf<Allocator>& b, const std::string& delim,
+ Handler handler);
+
+/// Start an asynchronous operation to read data into a streambuf until a
+/// regular expression is located.
+/**
+ * This function is used to asynchronously read data into the specified
+ * streambuf until the streambuf's get area contains some data that matches a
+ * regular expression. The function call always returns immediately. The
+ * asynchronous operation will continue until one of the following conditions
+ * is true:
+ *
+ * @li A substring of the streambuf's get area matches the regular expression.
+ *
+ * @li An error occurred.
+ *
+ * This operation is implemented in terms of zero or more calls to the stream's
+ * async_read_some function. If the streambuf's get area already contains data
+ * that matches the regular expression, the function returns immediately.
+ *
+ * @param s The stream from which the data is to be read. The type must support
+ * the Async_Read_Stream concept.
+ *
+ * @param b A streambuf object into which the data will be read. Ownership of
+ * the streambuf is retained by the caller, which must guarantee that it remains
+ * valid until the handler is called.
+ *
+ * @param expr The regular expression.
+ *
+ * @param handler The handler to be called when the read operation completes.
+ * Copies will be made of the handler as required. The function signature of the
+ * handler must be:
+ * @code void handler(
+ * const Async_Read_Stream::error_type& error, // Result of operation.
+ *
+ * std::size_t bytes_transferred // The number of bytes in the
+ * // streambuf's get area up to
+ * // and including the substring
+ * // that matches the regular
+ * // expression. 0 if an error
+ * // occurred.
+ * ); @endcode
+ * Regardless of whether the asynchronous operation completes immediately or
+ * not, the handler will not be invoked from within this function. Invocation of
+ * the handler will be performed in a manner equivalent to using
+ * asio::io_service::post().
+ *
+ * @par Example:
+ * To asynchronously read data into a streambuf until a CR-LF sequence is
+ * encountered:
+ * @code asio::streambuf b;
+ * ...
+ * void handler(const asio::error& e, std::size_t size)
+ * {
+ * if (!e)
+ * {
+ * std::istream is(&b);
+ * std::string line;
+ * std::getline(is, line);
+ * ...
+ * }
+ * }
+ * ...
+ * asio::async_read_until(s, b, boost::regex("\r\n"), handler); @endcode
+ */
+template <typename Async_Read_Stream, typename Allocator, typename Handler>
+void async_read_until(Async_Read_Stream& s,
+ asio::basic_streambuf<Allocator>& b, const boost::regex& expr,
+ Handler handler);
+
+/*@}*/
+
+} // namespace asio
+
+#include "asio/impl/read_until.ipp"
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_READ_UNTIL_HPP
diff --git a/library/include/libtorrent/asio/resolver_service.hpp b/library/include/libtorrent/asio/resolver_service.hpp
new file mode 100644
index 000000000..bdd8dbfbd
--- /dev/null
+++ b/library/include/libtorrent/asio/resolver_service.hpp
@@ -0,0 +1,126 @@
+//
+// resolver_service.hpp
+// ~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_RESOLVER_SERVICE_HPP
+#define ASIO_RESOLVER_SERVICE_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/io_service.hpp"
+#include "asio/detail/resolver_service.hpp"
+
+namespace asio {
+
+/// Default service implementation for a resolver.
+template <typename Protocol>
+class resolver_service
+ : public asio::io_service::service
+{
+public:
+ /// The protocol type.
+ typedef Protocol protocol_type;
+
+ /// The endpoint type.
+ typedef typename Protocol::endpoint endpoint_type;
+
+ /// The query type.
+ typedef typename Protocol::resolver_query query_type;
+
+ /// The iterator type.
+ typedef typename Protocol::resolver_iterator iterator_type;
+
+private:
+ // The type of the platform-specific implementation.
+ typedef detail::resolver_service<Protocol> service_impl_type;
+
+public:
+ /// The type of a resolver implementation.
+#if defined(GENERATING_DOCUMENTATION)
+ typedef implementation_defined implementation_type;
+#else
+ typedef typename service_impl_type::implementation_type implementation_type;
+#endif
+
+ /// Construct a new resolver service for the specified io_service.
+ explicit resolver_service(asio::io_service& io_service)
+ : asio::io_service::service(io_service),
+ service_impl_(asio::use_service<service_impl_type>(io_service))
+ {
+ }
+
+ /// Destroy all user-defined handler objects owned by the service.
+ void shutdown_service()
+ {
+ }
+
+ /// Construct a new resolver implementation.
+ void construct(implementation_type& impl)
+ {
+ service_impl_.construct(impl);
+ }
+
+ /// Destroy a resolver implementation.
+ void destroy(implementation_type& impl)
+ {
+ service_impl_.destroy(impl);
+ }
+
+ /// Cancel pending asynchronous operations.
+ void cancel(implementation_type& impl)
+ {
+ service_impl_.cancel(impl);
+ }
+
+ /// Resolve a query to a list of entries.
+ template <typename Error_Handler>
+ iterator_type resolve(implementation_type& impl, const query_type& query,
+ Error_Handler error_handler)
+ {
+ return service_impl_.resolve(impl, query, error_handler);
+ }
+
+ /// Asynchronously resolve a query to a list of entries.
+ template <typename Handler>
+ void async_resolve(implementation_type& impl, const query_type& query,
+ Handler handler)
+ {
+ service_impl_.async_resolve(impl, query, handler);
+ }
+
+ /// Resolve an endpoint to a list of entries.
+ template <typename Error_Handler>
+ iterator_type resolve(implementation_type& impl,
+ const endpoint_type& endpoint, Error_Handler error_handler)
+ {
+ return service_impl_.resolve(impl, endpoint, error_handler);
+ }
+
+ /// Asynchronously resolve an endpoint to a list of entries.
+ template <typename Handler>
+ void async_resolve(implementation_type& impl, const endpoint_type& endpoint,
+ Handler handler)
+ {
+ return service_impl_.async_resolve(impl, endpoint, handler);
+ }
+
+private:
+ // The service that provides the platform-specific implementation.
+ service_impl_type& service_impl_;
+};
+
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_RESOLVER_SERVICE_HPP
diff --git a/library/include/libtorrent/asio/socket_acceptor_service.hpp b/library/include/libtorrent/asio/socket_acceptor_service.hpp
new file mode 100644
index 000000000..8c7eb43c9
--- /dev/null
+++ b/library/include/libtorrent/asio/socket_acceptor_service.hpp
@@ -0,0 +1,219 @@
+//
+// socket_acceptor_service.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_SOCKET_ACCEPTOR_SERVICE_HPP
+#define ASIO_SOCKET_ACCEPTOR_SERVICE_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/basic_socket.hpp"
+#include "asio/io_service.hpp"
+#include "asio/detail/epoll_reactor.hpp"
+#include "asio/detail/kqueue_reactor.hpp"
+#include "asio/detail/select_reactor.hpp"
+#include "asio/detail/reactive_socket_service.hpp"
+#include "asio/detail/win_iocp_socket_service.hpp"
+
+namespace asio {
+
+/// Default service implementation for a socket acceptor.
+template <typename Protocol>
+class socket_acceptor_service
+ : public asio::io_service::service
+{
+public:
+ /// The protocol type.
+ typedef Protocol protocol_type;
+
+ /// The endpoint type.
+ typedef typename protocol_type::endpoint endpoint_type;
+
+private:
+ // The type of the platform-specific implementation.
+#if defined(ASIO_HAS_IOCP)
+ typedef detail::win_iocp_socket_service<Protocol> service_impl_type;
+#elif defined(ASIO_HAS_EPOLL)
+ typedef detail::reactive_socket_service<
+ Protocol, detail::epoll_reactor<false> > service_impl_type;
+#elif defined(ASIO_HAS_KQUEUE)
+ typedef detail::reactive_socket_service<
+ Protocol, detail::kqueue_reactor<false> > service_impl_type;
+#else
+ typedef detail::reactive_socket_service<
+ Protocol, detail::select_reactor<false> > service_impl_type;
+#endif
+
+public:
+ /// The native type of the socket acceptor.
+#if defined(GENERATING_DOCUMENTATION)
+ typedef implementation_defined implementation_type;
+#else
+ typedef typename service_impl_type::implementation_type implementation_type;
+#endif
+
+ /// The native acceptor type.
+#if defined(GENERATING_DOCUMENTATION)
+ typedef implementation_defined native_type;
+#else
+ typedef typename service_impl_type::native_type native_type;
+#endif
+
+ /// Construct a new socket acceptor service for the specified io_service.
+ explicit socket_acceptor_service(asio::io_service& io_service)
+ : asio::io_service::service(io_service),
+ service_impl_(asio::use_service<service_impl_type>(io_service))
+ {
+ }
+
+ /// Destroy all user-defined handler objects owned by the service.
+ void shutdown_service()
+ {
+ }
+
+ /// Construct a new socket acceptor implementation.
+ void construct(implementation_type& impl)
+ {
+ service_impl_.construct(impl);
+ }
+
+ /// Destroy a socket acceptor implementation.
+ void destroy(implementation_type& impl)
+ {
+ service_impl_.destroy(impl);
+ }
+
+ /// Open a new socket acceptor implementation.
+ template <typename Error_Handler>
+ void open(implementation_type& impl, const protocol_type& protocol,
+ Error_Handler error_handler)
+ {
+ service_impl_.open(impl, protocol, error_handler);
+ }
+
+ /// Assign an existing native acceptor to a socket acceptor.
+ template <typename Error_Handler>
+ void assign(implementation_type& impl, const protocol_type& protocol,
+ const native_type& native_acceptor, Error_Handler error_handler)
+ {
+ service_impl_.assign(impl, protocol, native_acceptor, error_handler);
+ }
+
+ /// Cancel all asynchronous operations associated with the acceptor.
+ template <typename Error_Handler>
+ void cancel(implementation_type& impl, Error_Handler error_handler)
+ {
+ service_impl_.cancel(impl, error_handler);
+ }
+
+ /// Bind the socket acceptor to the specified local endpoint.
+ template <typename Error_Handler>
+ void bind(implementation_type& impl, const endpoint_type& endpoint,
+ Error_Handler error_handler)
+ {
+ service_impl_.bind(impl, endpoint, error_handler);
+ }
+
+ /// Place the socket acceptor into the state where it will listen for new
+ /// connections.
+ template <typename Error_Handler>
+ void listen(implementation_type& impl, int backlog,
+ Error_Handler error_handler)
+ {
+ service_impl_.listen(impl, backlog, error_handler);
+ }
+
+ /// Close a socket acceptor implementation.
+ template <typename Error_Handler>
+ void close(implementation_type& impl, Error_Handler error_handler)
+ {
+ service_impl_.close(impl, error_handler);
+ }
+
+ /// Get the native acceptor implementation.
+ native_type native(implementation_type& impl)
+ {
+ return service_impl_.native(impl);
+ }
+
+ /// Set a socket option.
+ template <typename Option, typename Error_Handler>
+ void set_option(implementation_type& impl, const Option& option,
+ Error_Handler error_handler)
+ {
+ service_impl_.set_option(impl, option, error_handler);
+ }
+
+ /// Set a socket option.
+ template <typename Option, typename Error_Handler>
+ void get_option(implementation_type& impl, Option& option,
+ Error_Handler error_handler)
+ {
+ service_impl_.get_option(impl, option, error_handler);
+ }
+
+ /// Get the local endpoint.
+ template <typename Error_Handler>
+ endpoint_type local_endpoint(const implementation_type& impl,
+ Error_Handler error_handler) const
+ {
+ endpoint_type endpoint;
+ service_impl_.get_local_endpoint(impl, endpoint, error_handler);
+ return endpoint;
+ }
+
+ /// Accept a new connection.
+ template <typename Socket_Service, typename Error_Handler>
+ void accept(implementation_type& impl,
+ basic_socket<protocol_type, Socket_Service>& peer,
+ Error_Handler error_handler)
+ {
+ service_impl_.accept(impl, peer, error_handler);
+ }
+
+ /// Accept a new connection.
+ template <typename Socket_Service, typename Error_Handler>
+ void accept_endpoint(implementation_type& impl,
+ basic_socket<protocol_type, Socket_Service>& peer,
+ endpoint_type& peer_endpoint, Error_Handler error_handler)
+ {
+ service_impl_.accept_endpoint(impl, peer, peer_endpoint, error_handler);
+ }
+
+ /// Start an asynchronous accept.
+ template <typename Socket_Service, typename Handler>
+ void async_accept(implementation_type& impl,
+ basic_socket<protocol_type, Socket_Service>& peer, Handler handler)
+ {
+ service_impl_.async_accept(impl, peer, handler);
+ }
+
+ /// Start an asynchronous accept.
+ template <typename Socket_Service, typename Handler>
+ void async_accept_endpoint(implementation_type& impl,
+ basic_socket<protocol_type, Socket_Service>& peer,
+ endpoint_type& peer_endpoint, Handler handler)
+ {
+ service_impl_.async_accept_endpoint(impl, peer, peer_endpoint, handler);
+ }
+
+private:
+ // The service that provides the platform-specific implementation.
+ service_impl_type& service_impl_;
+};
+
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_SOCKET_ACCEPTOR_SERVICE_HPP
diff --git a/library/include/libtorrent/asio/socket_base.hpp b/library/include/libtorrent/asio/socket_base.hpp
new file mode 100644
index 000000000..48e37357c
--- /dev/null
+++ b/library/include/libtorrent/asio/socket_base.hpp
@@ -0,0 +1,482 @@
+//
+// socket_base.hpp
+// ~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_SOCKET_BASE_HPP
+#define ASIO_SOCKET_BASE_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/detail/push_options.hpp"
+#include <boost/config.hpp>
+#include <boost/detail/workaround.hpp>
+#include "asio/detail/pop_options.hpp"
+
+#include "asio/detail/io_control.hpp"
+#include "asio/detail/socket_option.hpp"
+#include "asio/detail/socket_types.hpp"
+
+namespace asio {
+
+/// The socket_base class is used as a base for the basic_stream_socket and
+/// basic_datagram_socket class templates so that we have a common place to
+/// define the shutdown_type and enum.
+class socket_base
+{
+public:
+ /// Different ways a socket may be shutdown.
+ enum shutdown_type
+ {
+#if defined(GENERATING_DOCUMENTATION)
+ /// Shutdown the receive side of the socket.
+ shutdown_receive = implementation_defined,
+
+ /// Shutdown the send side of the socket.
+ shutdown_send = implementation_defined,
+
+ /// Shutdown both send and receive on the socket.
+ shutdown_both = implementation_defined
+#else
+ shutdown_receive = asio::detail::shutdown_receive,
+ shutdown_send = asio::detail::shutdown_send,
+ shutdown_both = asio::detail::shutdown_both
+#endif
+ };
+
+ /// Bitmask type for flags that can be passed to send and receive operations.
+ typedef int message_flags;
+
+#if defined(GENERATING_DOCUMENTATION)
+ /// Peek at incoming data without removing it from the input queue.
+ static const int message_peek = implementation_defined;
+
+ /// Process out-of-band data.
+ static const int message_out_of_band = implementation_defined;
+
+ /// Specify that the data should not be subject to routing.
+ static const int message_do_not_route = implementation_defined;
+#else
+ BOOST_STATIC_CONSTANT(int,
+ message_peek = asio::detail::message_peek);
+ BOOST_STATIC_CONSTANT(int,
+ message_out_of_band = asio::detail::message_out_of_band);
+ BOOST_STATIC_CONSTANT(int,
+ message_do_not_route = asio::detail::message_do_not_route);
+#endif
+
+ /// Socket option to permit sending of broadcast messages.
+ /**
+ * Implements the SOL_SOCKET/SO_BROADCAST socket option.
+ *
+ * @par Examples:
+ * Setting the option:
+ * @code
+ * asio::ip::udp::socket socket(io_service);
+ * ...
+ * asio::socket_base::broadcast option(true);
+ * socket.set_option(option);
+ * @endcode
+ *
+ * @par
+ * Getting the current option value:
+ * @code
+ * asio::ip::udp::socket socket(io_service);
+ * ...
+ * asio::socket_base::broadcast option;
+ * socket.get_option(option);
+ * bool is_set = option.get();
+ * @endcode
+ *
+ * @par Concepts:
+ * Socket_Option, Boolean_Socket_Option.
+ */
+#if defined(GENERATING_DOCUMENTATION)
+ typedef implementation_defined broadcast;
+#else
+ typedef asio::detail::socket_option::boolean<
+ SOL_SOCKET, SO_BROADCAST> broadcast;
+#endif
+
+ /// Socket option to prevent routing, use local interfaces only.
+ /**
+ * Implements the SOL_SOCKET/SO_DONTROUTE socket option.
+ *
+ * @par Examples:
+ * Setting the option:
+ * @code
+ * asio::ip::udp::socket socket(io_service);
+ * ...
+ * asio::socket_base::do_not_route option(true);
+ * socket.set_option(option);
+ * @endcode
+ *
+ * @par
+ * Getting the current option value:
+ * @code
+ * asio::ip::udp::socket socket(io_service);
+ * ...
+ * asio::socket_base::do_not_route option;
+ * socket.get_option(option);
+ * bool is_set = option.get();
+ * @endcode
+ *
+ * @par Concepts:
+ * Socket_Option, Boolean_Socket_Option.
+ */
+#if defined(GENERATING_DOCUMENTATION)
+ typedef implementation_defined do_not_route;
+#else
+ typedef asio::detail::socket_option::boolean<
+ SOL_SOCKET, SO_DONTROUTE> do_not_route;
+#endif
+
+ /// Socket option to send keep-alives.
+ /**
+ * Implements the SOL_SOCKET/SO_KEEPALIVE socket option.
+ *
+ * @par Examples:
+ * Setting the option:
+ * @code
+ * asio::ip::tcp::socket socket(io_service);
+ * ...
+ * asio::socket_base::keep_alive option(true);
+ * socket.set_option(option);
+ * @endcode
+ *
+ * @par
+ * Getting the current option value:
+ * @code
+ * asio::ip::tcp::socket socket(io_service);
+ * ...
+ * asio::socket_base::keep_alive option;
+ * socket.get_option(option);
+ * bool is_set = option.get();
+ * @endcode
+ *
+ * @par Concepts:
+ * Socket_Option, Boolean_Socket_Option.
+ */
+#if defined(GENERATING_DOCUMENTATION)
+ typedef implementation_defined keep_alive;
+#else
+ typedef asio::detail::socket_option::boolean<
+ SOL_SOCKET, SO_KEEPALIVE> keep_alive;
+#endif
+
+ /// Socket option for the send buffer size of a socket.
+ /**
+ * Implements the SOL_SOCKET/SO_SNDBUF socket option.
+ *
+ * @par Examples:
+ * Setting the option:
+ * @code
+ * asio::ip::tcp::socket socket(io_service);
+ * ...
+ * asio::socket_base::send_buffer_size option(8192);
+ * socket.set_option(option);
+ * @endcode
+ *
+ * @par
+ * Getting the current option value:
+ * @code
+ * asio::ip::tcp::socket socket(io_service);
+ * ...
+ * asio::socket_base::send_buffer_size option;
+ * socket.get_option(option);
+ * int size = option.get();
+ * @endcode
+ *
+ * @par Concepts:
+ * Socket_Option, Integer_Socket_Option.
+ */
+#if defined(GENERATING_DOCUMENTATION)
+ typedef implementation_defined send_buffer_size;
+#else
+ typedef asio::detail::socket_option::integer<
+ SOL_SOCKET, SO_SNDBUF> send_buffer_size;
+#endif
+
+ /// Socket option for the send low watermark.
+ /**
+ * Implements the SOL_SOCKET/SO_SNDLOWAT socket option.
+ *
+ * @par Examples:
+ * Setting the option:
+ * @code
+ * asio::ip::tcp::socket socket(io_service);
+ * ...
+ * asio::socket_base::send_low_watermark option(1024);
+ * socket.set_option(option);
+ * @endcode
+ *
+ * @par
+ * Getting the current option value:
+ * @code
+ * asio::ip::tcp::socket socket(io_service);
+ * ...
+ * asio::socket_base::send_low_watermark option;
+ * socket.get_option(option);
+ * int size = option.get();
+ * @endcode
+ *
+ * @par Concepts:
+ * Socket_Option, Integer_Socket_Option.
+ */
+#if defined(GENERATING_DOCUMENTATION)
+ typedef implementation_defined send_low_watermark;
+#else
+ typedef asio::detail::socket_option::integer<
+ SOL_SOCKET, SO_SNDLOWAT> send_low_watermark;
+#endif
+
+ /// Socket option for the receive buffer size of a socket.
+ /**
+ * Implements the SOL_SOCKET/SO_RCVBUF socket option.
+ *
+ * @par Examples:
+ * Setting the option:
+ * @code
+ * asio::ip::tcp::socket socket(io_service);
+ * ...
+ * asio::socket_base::receive_buffer_size option(8192);
+ * socket.set_option(option);
+ * @endcode
+ *
+ * @par
+ * Getting the current option value:
+ * @code
+ * asio::ip::tcp::socket socket(io_service);
+ * ...
+ * asio::socket_base::receive_buffer_size option;
+ * socket.get_option(option);
+ * int size = option.get();
+ * @endcode
+ *
+ * @par Concepts:
+ * Socket_Option, Integer_Socket_Option.
+ */
+#if defined(GENERATING_DOCUMENTATION)
+ typedef implementation_defined receive_buffer_size;
+#else
+ typedef asio::detail::socket_option::integer<
+ SOL_SOCKET, SO_RCVBUF> receive_buffer_size;
+#endif
+
+ /// Socket option for the receive low watermark.
+ /**
+ * Implements the SOL_SOCKET/SO_RCVLOWAT socket option.
+ *
+ * @par Examples:
+ * Setting the option:
+ * @code
+ * asio::ip::tcp::socket socket(io_service);
+ * ...
+ * asio::socket_base::receive_low_watermark option(1024);
+ * socket.set_option(option);
+ * @endcode
+ *
+ * @par
+ * Getting the current option value:
+ * @code
+ * asio::ip::tcp::socket socket(io_service);
+ * ...
+ * asio::socket_base::receive_low_watermark option;
+ * socket.get_option(option);
+ * int size = option.get();
+ * @endcode
+ *
+ * @par Concepts:
+ * Socket_Option, Integer_Socket_Option.
+ */
+#if defined(GENERATING_DOCUMENTATION)
+ typedef implementation_defined receive_low_watermark;
+#else
+ typedef asio::detail::socket_option::integer<
+ SOL_SOCKET, SO_RCVLOWAT> receive_low_watermark;
+#endif
+
+ /// Socket option to allow the socket to be bound to an address that is
+ /// already in use.
+ /**
+ * Implements the SOL_SOCKET/SO_REUSEADDR socket option.
+ *
+ * @par Examples:
+ * Setting the option:
+ * @code
+ * asio::ip::tcp::acceptor acceptor(io_service);
+ * ...
+ * asio::socket_base::reuse_address option(true);
+ * acceptor.set_option(option);
+ * @endcode
+ *
+ * @par
+ * Getting the current option value:
+ * @code
+ * asio::ip::tcp::acceptor acceptor(io_service);
+ * ...
+ * asio::socket_base::reuse_address option;
+ * acceptor.get_option(option);
+ * bool is_set = option.get();
+ * @endcode
+ *
+ * @par Concepts:
+ * Socket_Option, Boolean_Socket_Option.
+ */
+#if defined(GENERATING_DOCUMENTATION)
+ typedef implementation_defined reuse_address;
+#else
+ typedef asio::detail::socket_option::boolean<
+ SOL_SOCKET, SO_REUSEADDR> reuse_address;
+#endif
+
+ /// Socket option to specify whether the socket lingers on close if unsent
+ /// data is present.
+ /**
+ * Implements the SOL_SOCKET/SO_LINGER socket option.
+ *
+ * @par Examples:
+ * Setting the option:
+ * @code
+ * asio::ip::tcp::socket socket(io_service);
+ * ...
+ * asio::socket_base::linger option(true, 30);
+ * socket.set_option(option);
+ * @endcode
+ *
+ * @par
+ * Getting the current option value:
+ * @code
+ * asio::ip::tcp::socket socket(io_service);
+ * ...
+ * asio::socket_base::linger option;
+ * socket.get_option(option);
+ * bool is_set = option.enabled();
+ * unsigned short timeout = option.timeout();
+ * @endcode
+ *
+ * @par Concepts:
+ * Socket_Option, Linger_Socket_Option.
+ */
+#if defined(GENERATING_DOCUMENTATION)
+ typedef implementation_defined linger;
+#else
+ typedef asio::detail::socket_option::linger<
+ SOL_SOCKET, SO_LINGER> linger;
+#endif
+
+ /// Socket option to report aborted connections on accept.
+ /**
+ * Implements a custom socket option that determines whether or not an accept
+ * operation is permitted to fail with asio::error::connection_aborted.
+ * By default the option is false.
+ *
+ * @par Examples:
+ * Setting the option:
+ * @code
+ * asio::ip::tcp::acceptor acceptor(io_service);
+ * ...
+ * asio::socket_base::enable_connection_aborted option(true);
+ * acceptor.set_option(option);
+ * @endcode
+ *
+ * @par
+ * Getting the current option value:
+ * @code
+ * asio::ip::tcp::acceptor acceptor(io_service);
+ * ...
+ * asio::socket_base::enable_connection_aborted option;
+ * acceptor.get_option(option);
+ * bool is_set = option.get();
+ * @endcode
+ *
+ * @par Concepts:
+ * Socket_Option, Boolean_Socket_Option.
+ */
+#if defined(GENERATING_DOCUMENTATION)
+ typedef implementation_defined enable_connection_aborted;
+#else
+ typedef asio::detail::socket_option::boolean<
+ asio::detail::custom_socket_option_level,
+ asio::detail::enable_connection_aborted_option>
+ enable_connection_aborted;
+#endif
+
+ /// IO control command to set the blocking mode of the socket.
+ /**
+ * Implements the FIONBIO IO control command.
+ *
+ * @par Example:
+ * @code
+ * asio::ip::tcp::socket socket(io_service);
+ * ...
+ * asio::socket_base::non_blocking_io command(true);
+ * socket.io_control(command);
+ * @endcode
+ *
+ * @par Concepts:
+ * IO_Control_Command, Boolean_IO_Control_Command.
+ */
+#if defined(GENERATING_DOCUMENTATION)
+ typedef implementation_defined non_blocking_io;
+#else
+ typedef asio::detail::io_control::non_blocking_io non_blocking_io;
+#endif
+
+ /// IO control command to get the amount of data that can be read without
+ /// blocking.
+ /**
+ * Implements the FIONREAD IO control command.
+ *
+ * @par Example:
+ * @code
+ * asio::ip::tcp::socket socket(io_service);
+ * ...
+ * asio::socket_base::bytes_readable command(true);
+ * socket.io_control(command);
+ * std::size_t bytes_readable = command.get();
+ * @endcode
+ *
+ * @par Concepts:
+ * IO_Control_Command, Size_IO_Control_Command.
+ */
+#if defined(GENERATING_DOCUMENTATION)
+ typedef implementation_defined bytes_readable;
+#else
+ typedef asio::detail::io_control::bytes_readable bytes_readable;
+#endif
+
+ /// The maximum length of the queue of pending incoming connections.
+#if defined(GENERATING_DOCUMENTATION)
+ static const int max_connections = implementation_defined;
+#else
+ BOOST_STATIC_CONSTANT(int, max_connections = SOMAXCONN);
+#endif
+
+protected:
+ /// Protected destructor to prevent deletion through this type.
+ ~socket_base()
+ {
+ }
+
+#if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564))
+private:
+ // Workaround to enable the empty base optimisation with Borland C++.
+ char dummy_;
+#endif
+};
+
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_SOCKET_BASE_HPP
diff --git a/library/include/libtorrent/asio/ssl.hpp b/library/include/libtorrent/asio/ssl.hpp
new file mode 100644
index 000000000..2d4621340
--- /dev/null
+++ b/library/include/libtorrent/asio/ssl.hpp
@@ -0,0 +1,26 @@
+//
+// ssl.hpp
+// ~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_SSL_HPP
+#define ASIO_SSL_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/ssl/basic_context.hpp"
+#include "asio/ssl/context.hpp"
+#include "asio/ssl/context_base.hpp"
+#include "asio/ssl/context_service.hpp"
+#include "asio/ssl/stream.hpp"
+#include "asio/ssl/stream_base.hpp"
+#include "asio/ssl/stream_service.hpp"
+
+#endif // ASIO_SSL_HPP
diff --git a/library/include/libtorrent/asio/ssl/basic_context.hpp b/library/include/libtorrent/asio/ssl/basic_context.hpp
new file mode 100755
index 000000000..2c25e81a8
--- /dev/null
+++ b/library/include/libtorrent/asio/ssl/basic_context.hpp
@@ -0,0 +1,467 @@
+//
+// basic_context.hpp
+// ~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2005 Voipster / Indrek dot Juhani at voipster dot com
+// Copyright (c) 2005 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_SSL_BASIC_CONTEXT_HPP
+#define ASIO_SSL_BASIC_CONTEXT_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/detail/push_options.hpp"
+#include <string>
+#include <boost/noncopyable.hpp>
+#include "asio/detail/pop_options.hpp"
+
+#include "asio/error_handler.hpp"
+#include "asio/io_service.hpp"
+#include "asio/ssl/context_base.hpp"
+
+namespace asio {
+namespace ssl {
+
+/// SSL context.
+template <typename Service>
+class basic_context
+ : public context_base,
+ private boost::noncopyable
+{
+public:
+ /// The type of the service that will be used to provide context operations.
+ typedef Service service_type;
+
+ /// The native implementation type of the locking dispatcher.
+ typedef typename service_type::impl_type impl_type;
+
+ /// Constructor.
+ basic_context(asio::io_service& io_service, method m)
+ : service_(asio::use_service<Service>(io_service)),
+ impl_(service_.null())
+ {
+ service_.create(impl_, m);
+ }
+
+ /// Destructor.
+ ~basic_context()
+ {
+ service_.destroy(impl_);
+ }
+
+ /// Get the underlying implementation in the native type.
+ /**
+ * This function may be used to obtain the underlying implementation of the
+ * context. This is intended to allow access to context functionality that is
+ * not otherwise provided.
+ */
+ impl_type impl()
+ {
+ return impl_;
+ }
+
+ /// Set options on the context.
+ /**
+ * This function may be used to configure the SSL options used by the context.
+ *
+ * @param o A bitmask of options. The available option values are defined in
+ * the context_base class. The options are bitwise-ored with any existing
+ * value for the options.
+ *
+ * @throws asio::error Thrown on failure.
+ */
+ void set_options(options o)
+ {
+ service_.set_options(impl_, o, throw_error());
+ }
+
+ /// Set options on the context.
+ /**
+ * This function may be used to configure the SSL options used by the context.
+ *
+ * @param o A bitmask of options. The available option values are defined in
+ * the context_base class. The options are bitwise-ored with any existing
+ * value for the options.
+ *
+ * @param error_handler A handler to be called when the operation completes,
+ * to indicate whether or not an error has occurred. Copies will be made of
+ * the handler as required. The function signature of the handler must be:
+ * @code void error_handler(
+ * const asio::error& error // Result of operation
+ * ); @endcode
+ */
+ template <typename Error_Handler>
+ void set_options(options o, Error_Handler error_handler)
+ {
+ service_.set_options(impl_, o, error_handler);
+ }
+
+ /// Set the peer verification mode.
+ /**
+ * This function may be used to configure the peer verification mode used by
+ * the context.
+ *
+ * @param v A bitmask of peer verification modes. The available verify_mode
+ * values are defined in the context_base class.
+ *
+ * @throws asio::error Thrown on failure.
+ */
+ void set_verify_mode(verify_mode v)
+ {
+ service_.set_verify_mode(impl_, v, throw_error());
+ }
+
+ /// Set the peer verification mode.
+ /**
+ * This function may be used to configure the peer verification mode used by
+ * the context.
+ *
+ * @param v A bitmask of peer verification modes. The available verify_mode
+ * values are defined in the context_base class.
+ *
+ * @param error_handler A handler to be called when the operation completes,
+ * to indicate whether or not an error has occurred. Copies will be made of
+ * the handler as required. The function signature of the handler must be:
+ * @code void error_handler(
+ * const asio::error& error // Result of operation
+ * ); @endcode
+ */
+ template <typename Error_Handler>
+ void set_verify_mode(verify_mode v, Error_Handler error_handler)
+ {
+ service_.set_verify_mode(impl_, v, error_handler);
+ }
+
+ /// Load a certification authority file for performing verification.
+ /**
+ * This function is used to load one or more trusted certification authorities
+ * from a file.
+ *
+ * @param filename The name of a file containing certification authority
+ * certificates in PEM format.
+ *
+ * @throws asio::error Thrown on failure.
+ */
+ void load_verify_file(const std::string& filename)
+ {
+ service_.load_verify_file(impl_, filename, throw_error());
+ }
+
+ /// Load a certification authority file for performing verification.
+ /**
+ * This function is used to load the certificates for one or more trusted
+ * certification authorities from a file.
+ *
+ * @param filename The name of a file containing certification authority
+ * certificates in PEM format.
+ *
+ * @param error_handler A handler to be called when the operation completes,
+ * to indicate whether or not an error has occurred. Copies will be made of
+ * the handler as required. The function signature of the handler must be:
+ * @code void error_handler(
+ * const asio::error& error // Result of operation
+ * ); @endcode
+ */
+ template <typename Error_Handler>
+ void load_verify_file(const std::string& filename,
+ Error_Handler error_handler)
+ {
+ service_.load_verify_file(impl_, filename, error_handler);
+ }
+
+ /// Add a directory containing certificate authority files to be used for
+ /// performing verification.
+ /**
+ * This function is used to specify the name of a directory containing
+ * certification authority certificates. Each file in the directory must
+ * contain a single certificate. The files must be named using the subject
+ * name's hash and an extension of ".0".
+ *
+ * @param path The name of a directory containing the certificates.
+ *
+ * @throws asio::error Thrown on failure.
+ */
+ void add_verify_path(const std::string& path)
+ {
+ service_.add_verify_path(impl_, path, throw_error());
+ }
+
+ /// Add a directory containing certificate authority files to be used for
+ /// performing verification.
+ /**
+ * This function is used to specify the name of a directory containing
+ * certification authority certificates. Each file in the directory must
+ * contain a single certificate. The files must be named using the subject
+ * name's hash and an extension of ".0".
+ *
+ * @param path The name of a directory containing the certificates.
+ *
+ * @param error_handler A handler to be called when the operation completes,
+ * to indicate whether or not an error has occurred. Copies will be made of
+ * the handler as required. The function signature of the handler must be:
+ * @code void error_handler(
+ * const asio::error& error // Result of operation
+ * ); @endcode
+ */
+ template <typename Error_Handler>
+ void add_verify_path(const std::string& path, Error_Handler error_handler)
+ {
+ service_.add_verify_path(impl_, path, error_handler);
+ }
+
+ /// Use a certificate from a file.
+ /**
+ * This function is used to load a certificate into the context from a file.
+ *
+ * @param filename The name of the file containing the certificate.
+ *
+ * @param format The file format (ASN.1 or PEM).
+ *
+ * @throws asio::error Thrown on failure.
+ */
+ void use_certificate_file(const std::string& filename, file_format format)
+ {
+ service_.use_certificate_file(impl_, filename, format, throw_error());
+ }
+
+ /// Use a certificate from a file.
+ /**
+ * This function is used to load a certificate into the context from a file.
+ *
+ * @param filename The name of the file containing the certificate.
+ *
+ * @param format The file format (ASN.1 or PEM).
+ *
+ * @param error_handler A handler to be called when the operation completes,
+ * to indicate whether or not an error has occurred. Copies will be made of
+ * the handler as required. The function signature of the handler must be:
+ * @code void error_handler(
+ * const asio::error& error // Result of operation
+ * ); @endcode
+ */
+ template <typename Error_Handler>
+ void use_certificate_file(const std::string& filename, file_format format,
+ Error_Handler error_handler)
+ {
+ service_.use_certificate_file(impl_, filename, format, error_handler);
+ }
+
+ /// Use a certificate chain from a file.
+ /**
+ * This function is used to load a certificate chain into the context from a
+ * file.
+ *
+ * @param filename The name of the file containing the certificate. The file
+ * must use the PEM format.
+ *
+ * @throws asio::error Thrown on failure.
+ */
+ void use_certificate_chain_file(const std::string& filename)
+ {
+ service_.use_certificate_chain_file(impl_, filename, throw_error());
+ }
+
+ /// Use a certificate chain from a file.
+ /**
+ * This function is used to load a certificate chain into the context from a
+ * file.
+ *
+ * @param filename The name of the file containing the certificate. The file
+ * must use the PEM format.
+ *
+ * @param error_handler A handler to be called when the operation completes,
+ * to indicate whether or not an error has occurred. Copies will be made of
+ * the handler as required. The function signature of the handler must be:
+ * @code void error_handler(
+ * const asio::error& error // Result of operation
+ * ); @endcode
+ */
+ template <typename Error_Handler>
+ void use_certificate_chain_file(const std::string& filename,
+ Error_Handler error_handler)
+ {
+ service_.use_certificate_chain_file(impl_, filename, error_handler);
+ }
+
+ /// Use a private key from a file.
+ /**
+ * This function is used to load a private key into the context from a file.
+ *
+ * @param filename The name of the file containing the private key.
+ *
+ * @param format The file format (ASN.1 or PEM).
+ *
+ * @throws asio::error Thrown on failure.
+ */
+ void use_private_key_file(const std::string& filename, file_format format)
+ {
+ service_.use_private_key_file(impl_, filename, format, throw_error());
+ }
+
+ /// Use a private key from a file.
+ /**
+ * This function is used to load a private key into the context from a file.
+ *
+ * @param filename The name of the file containing the private key.
+ *
+ * @param format The file format (ASN.1 or PEM).
+ *
+ * @param error_handler A handler to be called when the operation completes,
+ * to indicate whether or not an error has occurred. Copies will be made of
+ * the handler as required. The function signature of the handler must be:
+ * @code void error_handler(
+ * const asio::error& error // Result of operation
+ * ); @endcode
+ */
+ template <typename Error_Handler>
+ void use_private_key_file(const std::string& filename, file_format format,
+ Error_Handler error_handler)
+ {
+ service_.use_private_key_file(impl_, filename, format, error_handler);
+ }
+
+ /// Use an RSA private key from a file.
+ /**
+ * This function is used to load an RSA private key into the context from a
+ * file.
+ *
+ * @param filename The name of the file containing the RSA private key.
+ *
+ * @param format The file format (ASN.1 or PEM).
+ *
+ * @throws asio::error Thrown on failure.
+ */
+ void use_rsa_private_key_file(const std::string& filename, file_format format)
+ {
+ service_.use_rsa_private_key_file(impl_, filename, format, throw_error());
+ }
+
+ /// Use an RSA private key from a file.
+ /**
+ * This function is used to load an RSA private key into the context from a
+ * file.
+ *
+ * @param filename The name of the file containing the RSA private key.
+ *
+ * @param format The file format (ASN.1 or PEM).
+ *
+ * @param error_handler A handler to be called when the operation completes,
+ * to indicate whether or not an error has occurred. Copies will be made of
+ * the handler as required. The function signature of the handler must be:
+ * @code void error_handler(
+ * const asio::error& error // Result of operation
+ * ); @endcode
+ */
+ template <typename Error_Handler>
+ void use_rsa_private_key_file(const std::string& filename, file_format format,
+ Error_Handler error_handler)
+ {
+ service_.use_rsa_private_key_file(impl_, filename, format, error_handler);
+ }
+
+ /// Use the specified file to obtain the temporary Diffie-Hellman parameters.
+ /**
+ * This function is used to load Diffie-Hellman parameters into the context
+ * from a file.
+ *
+ * @param filename The name of the file containing the Diffie-Hellman
+ * parameters. The file must use the PEM format.
+ *
+ * @throws asio::error Thrown on failure.
+ */
+ void use_tmp_dh_file(const std::string& filename)
+ {
+ service_.use_tmp_dh_file(impl_, filename, throw_error());
+ }
+
+ /// Use the specified file to obtain the temporary Diffie-Hellman parameters.
+ /**
+ * This function is used to load Diffie-Hellman parameters into the context
+ * from a file.
+ *
+ * @param filename The name of the file containing the Diffie-Hellman
+ * parameters. The file must use the PEM format.
+ *
+ * @param error_handler A handler to be called when the operation completes,
+ * to indicate whether or not an error has occurred. Copies will be made of
+ * the handler as required. The function signature of the handler must be:
+ * @code void error_handler(
+ * const asio::error& error // Result of operation
+ * ); @endcode
+ */
+ template <typename Error_Handler>
+ void use_tmp_dh_file(const std::string& filename, Error_Handler error_handler)
+ {
+ service_.use_tmp_dh_file(impl_, filename, error_handler);
+ }
+
+ /// Set the password callback.
+ /**
+ * This function is used to specify a callback function to obtain password
+ * information about an encrypted key in PEM format.
+ *
+ * @param callback The function object to be used for obtaining the password.
+ * The function signature of the handler must be:
+ * @code std::string password_callback(
+ * std::size_t max_length, // The maximum size for a password.
+ * password_purpose purpose // Whether password is for reading or writing.
+ * ); @endcode
+ * The return value of the callback is a string containing the password.
+ *
+ * @throws asio::error Thrown on failure.
+ */
+ template <typename Password_Callback>
+ void set_password_callback(Password_Callback callback)
+ {
+ service_.set_password_callback(impl_, callback, throw_error());
+ }
+
+ /// Set the password callback.
+ /**
+ * This function is used to specify a callback function to obtain password
+ * information about an encrypted key in PEM format.
+ *
+ * @param callback The function object to be used for obtaining the password.
+ * The function signature of the handler must be:
+ * @code std::string password_callback(
+ * std::size_t max_length, // The maximum size for a password.
+ * password_purpose purpose // Whether password is for reading or writing.
+ * ); @endcode
+ * The return value of the callback is a string containing the password.
+ *
+ * @param error_handler A handler to be called when the operation completes,
+ * to indicate whether or not an error has occurred. Copies will be made of
+ * the handler as required. The function signature of the handler must be:
+ * @code void error_handler(
+ * const asio::error& error // Result of operation
+ * ); @endcode
+ */
+ template <typename Password_Callback, typename Error_Handler>
+ void set_password_callback(Password_Callback callback,
+ Error_Handler error_handler)
+ {
+ service_.set_password_callback(impl_, callback, error_handler);
+ }
+
+private:
+ /// The backend service implementation.
+ service_type& service_;
+
+ /// The underlying native implementation.
+ impl_type impl_;
+};
+
+} // namespace ssl
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_SSL_BASIC_CONTEXT_HPP
diff --git a/library/include/libtorrent/asio/ssl/context.hpp b/library/include/libtorrent/asio/ssl/context.hpp
new file mode 100644
index 000000000..86e249cbc
--- /dev/null
+++ b/library/include/libtorrent/asio/ssl/context.hpp
@@ -0,0 +1,35 @@
+//
+// context.hpp
+// ~~~~~~~~~~~
+//
+// Copyright (c) 2005 Voipster / Indrek dot Juhani at voipster dot com
+// Copyright (c) 2005 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_SSL_CONTEXT_HPP
+#define ASIO_SSL_CONTEXT_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/ssl/basic_context.hpp"
+#include "asio/ssl/context_service.hpp"
+
+namespace asio {
+namespace ssl {
+
+/// Typedef for the typical usage of context.
+typedef basic_context<context_service> context;
+
+} // namespace ssl
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_SSL_CONTEXT_HPP
diff --git a/library/include/libtorrent/asio/ssl/context_base.hpp b/library/include/libtorrent/asio/ssl/context_base.hpp
new file mode 100755
index 000000000..0811d8ba5
--- /dev/null
+++ b/library/include/libtorrent/asio/ssl/context_base.hpp
@@ -0,0 +1,164 @@
+//
+// context_base.hpp
+// ~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2005 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_SSL_CONTEXT_BASE_HPP
+#define ASIO_SSL_CONTEXT_BASE_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/detail/push_options.hpp"
+#include <boost/config.hpp>
+#include <boost/detail/workaround.hpp>
+#include "asio/detail/pop_options.hpp"
+
+#include "asio/ssl/detail/openssl_types.hpp"
+
+namespace asio {
+namespace ssl {
+
+/// The context_base class is used as a base for the basic_context class
+/// template so that we have a common place to define various enums.
+class context_base
+{
+public:
+ /// Different methods supported by a context.
+ enum method
+ {
+ /// Generic SSL version 2.
+ sslv2,
+
+ /// SSL version 2 client.
+ sslv2_client,
+
+ /// SSL version 2 server.
+ sslv2_server,
+
+ /// Generic SSL version 3.
+ sslv3,
+
+ /// SSL version 3 client.
+ sslv3_client,
+
+ /// SSL version 3 server.
+ sslv3_server,
+
+ /// Generic TLS version 1.
+ tlsv1,
+
+ /// TLS version 1 client.
+ tlsv1_client,
+
+ /// TLS version 1 server.
+ tlsv1_server,
+
+ /// Generic SSL/TLS.
+ sslv23,
+
+ /// SSL/TLS client.
+ sslv23_client,
+
+ /// SSL/TLS server.
+ sslv23_server
+ };
+
+ /// Bitmask type for SSL options.
+ typedef int options;
+
+#if defined(GENERATING_DOCUMENTATION)
+ /// Implement various bug workarounds.
+ static const int default_workarounds = implementation_defined;
+
+ /// Always create a new key when using tmp_dh parameters.
+ static const int single_dh_use = implementation_defined;
+
+ /// Disable SSL v2.
+ static const int no_sslv2 = implementation_defined;
+
+ /// Disable SSL v3.
+ static const int no_sslv3 = implementation_defined;
+
+ /// Disable TLS v1.
+ static const int no_tlsv1 = implementation_defined;
+#else
+ BOOST_STATIC_CONSTANT(int, default_workarounds = SSL_OP_ALL);
+ BOOST_STATIC_CONSTANT(int, single_dh_use = SSL_OP_SINGLE_DH_USE);
+ BOOST_STATIC_CONSTANT(int, no_sslv2 = SSL_OP_NO_SSLv2);
+ BOOST_STATIC_CONSTANT(int, no_sslv3 = SSL_OP_NO_SSLv3);
+ BOOST_STATIC_CONSTANT(int, no_tlsv1 = SSL_OP_NO_TLSv1);
+#endif
+
+ /// File format types.
+ enum file_format
+ {
+ /// ASN.1 file.
+ asn1,
+
+ /// PEM file.
+ pem
+ };
+
+ /// Bitmask type for peer verification.
+ typedef int verify_mode;
+
+#if defined(GENERATING_DOCUMENTATION)
+ /// No verification.
+ static const int verify_none = implementation_defined;
+
+ /// Verify the peer.
+ static const int verify_peer = implementation_defined;
+
+ /// Fail verification if the peer has no certificate. Ignored unless
+ /// verify_peer is set.
+ static const int verify_fail_if_no_peer_cert = implementation_defined;
+
+ /// Do not request client certificate on renegotiation. Ignored unless
+ /// verify_peer is set.
+ static const int verify_client_once = implementation_defined;
+#else
+ BOOST_STATIC_CONSTANT(int, verify_none = SSL_VERIFY_NONE);
+ BOOST_STATIC_CONSTANT(int, verify_peer = SSL_VERIFY_PEER);
+ BOOST_STATIC_CONSTANT(int,
+ verify_fail_if_no_peer_cert = SSL_VERIFY_FAIL_IF_NO_PEER_CERT);
+ BOOST_STATIC_CONSTANT(int, verify_client_once = SSL_VERIFY_CLIENT_ONCE);
+#endif
+
+ /// Purpose of PEM password.
+ enum password_purpose
+ {
+ /// The password is needed for reading/decryption.
+ for_reading,
+
+ /// The password is needed for writing/encryption.
+ for_writing
+ };
+
+protected:
+ /// Protected destructor to prevent deletion through this type.
+ ~context_base()
+ {
+ }
+
+#if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564))
+private:
+ // Workaround to enable the empty base optimisation with Borland C++.
+ char dummy_;
+#endif
+};
+
+} // namespace ssl
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_SSL_CONTEXT_BASE_HPP
diff --git a/library/include/libtorrent/asio/ssl/context_service.hpp b/library/include/libtorrent/asio/ssl/context_service.hpp
new file mode 100755
index 000000000..636b970c3
--- /dev/null
+++ b/library/include/libtorrent/asio/ssl/context_service.hpp
@@ -0,0 +1,171 @@
+//
+// context_service.hpp
+// ~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2005 Voipster / Indrek dot Juhani at voipster dot com
+// Copyright (c) 2005 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_SSL_CONTEXT_SERVICE_HPP
+#define ASIO_SSL_CONTEXT_SERVICE_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/detail/push_options.hpp"
+#include <string>
+#include <boost/noncopyable.hpp>
+#include "asio/detail/pop_options.hpp"
+
+#include "asio/io_service.hpp"
+#include "asio/ssl/context_base.hpp"
+#include "asio/ssl/detail/openssl_context_service.hpp"
+
+namespace asio {
+namespace ssl {
+
+/// Default service implementation for a context.
+class context_service
+ : public asio::io_service::service
+{
+private:
+ // The type of the platform-specific implementation.
+ typedef detail::openssl_context_service service_impl_type;
+
+public:
+ /// The type of the context.
+#if defined(GENERATING_DOCUMENTATION)
+ typedef implementation_defined impl_type;
+#else
+ typedef service_impl_type::impl_type impl_type;
+#endif
+
+ /// Constructor.
+ explicit context_service(asio::io_service& io_service)
+ : asio::io_service::service(io_service),
+ service_impl_(asio::use_service<service_impl_type>(io_service))
+ {
+ }
+
+ /// Destroy all user-defined handler objects owned by the service.
+ void shutdown_service()
+ {
+ }
+
+ /// Return a null context implementation.
+ impl_type null() const
+ {
+ return service_impl_.null();
+ }
+
+ /// Create a new context implementation.
+ void create(impl_type& impl, context_base::method m)
+ {
+ service_impl_.create(impl, m);
+ }
+
+ /// Destroy a context implementation.
+ void destroy(impl_type& impl)
+ {
+ service_impl_.destroy(impl);
+ }
+
+ /// Set options on the context.
+ template <typename Error_Handler>
+ void set_options(impl_type& impl, context_base::options o,
+ Error_Handler error_handler)
+ {
+ service_impl_.set_options(impl, o, error_handler);
+ }
+
+ /// Set peer verification mode.
+ template <typename Error_Handler>
+ void set_verify_mode(impl_type& impl, context_base::verify_mode v,
+ Error_Handler error_handler)
+ {
+ service_impl_.set_verify_mode(impl, v, error_handler);
+ }
+
+ /// Load a certification authority file for performing verification.
+ template <typename Error_Handler>
+ void load_verify_file(impl_type& impl, const std::string& filename,
+ Error_Handler error_handler)
+ {
+ service_impl_.load_verify_file(impl, filename, error_handler);
+ }
+
+ /// Add a directory containing certification authority files to be used for
+ /// performing verification.
+ template <typename Error_Handler>
+ void add_verify_path(impl_type& impl, const std::string& path,
+ Error_Handler error_handler)
+ {
+ service_impl_.add_verify_path(impl, path, error_handler);
+ }
+
+ /// Use a certificate from a file.
+ template <typename Error_Handler>
+ void use_certificate_file(impl_type& impl, const std::string& filename,
+ context_base::file_format format, Error_Handler error_handler)
+ {
+ service_impl_.use_certificate_file(impl, filename, format, error_handler);
+ }
+
+ /// Use a certificate chain from a file.
+ template <typename Error_Handler>
+ void use_certificate_chain_file(impl_type& impl, const std::string& filename,
+ Error_Handler error_handler)
+ {
+ service_impl_.use_certificate_chain_file(impl, filename, error_handler);
+ }
+
+ /// Use a private key from a file.
+ template <typename Error_Handler>
+ void use_private_key_file(impl_type& impl, const std::string& filename,
+ context_base::file_format format, Error_Handler error_handler)
+ {
+ service_impl_.use_private_key_file(impl, filename, format, error_handler);
+ }
+
+ /// Use an RSA private key from a file.
+ template <typename Error_Handler>
+ void use_rsa_private_key_file(impl_type& impl, const std::string& filename,
+ context_base::file_format format, Error_Handler error_handler)
+ {
+ service_impl_.use_rsa_private_key_file(impl, filename, format,
+ error_handler);
+ }
+
+ /// Use the specified file to obtain the temporary Diffie-Hellman parameters.
+ template <typename Error_Handler>
+ void use_tmp_dh_file(impl_type& impl, const std::string& filename,
+ Error_Handler error_handler)
+ {
+ service_impl_.use_tmp_dh_file(impl, filename, error_handler);
+ }
+
+ /// Set the password callback.
+ template <typename Password_Callback, typename Error_Handler>
+ void set_password_callback(impl_type& impl, Password_Callback callback,
+ Error_Handler error_handler)
+ {
+ service_impl_.set_password_callback(impl, callback, error_handler);
+ }
+
+private:
+ // The service that provides the platform-specific implementation.
+ service_impl_type& service_impl_;
+};
+
+} // namespace ssl
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_SSL_CONTEXT_SERVICE_HPP
diff --git a/library/include/libtorrent/asio/ssl/detail/openssl_context_service.hpp b/library/include/libtorrent/asio/ssl/detail/openssl_context_service.hpp
new file mode 100755
index 000000000..723bcadf2
--- /dev/null
+++ b/library/include/libtorrent/asio/ssl/detail/openssl_context_service.hpp
@@ -0,0 +1,396 @@
+//
+// openssl_context_service.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2005 Voipster / Indrek dot Juhani at voipster dot com
+// Copyright (c) 2005 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_SSL_DETAIL_OPENSSL_CONTEXT_SERVICE_HPP
+#define ASIO_SSL_DETAIL_OPENSSL_CONTEXT_SERVICE_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/detail/push_options.hpp"
+#include <cstring>
+#include <string>
+#include <boost/function.hpp>
+#include "asio/detail/pop_options.hpp"
+
+#include "asio/error.hpp"
+#include "asio/io_service.hpp"
+#include "asio/ssl/context_base.hpp"
+#include "asio/ssl/detail/openssl_init.hpp"
+#include "asio/ssl/detail/openssl_types.hpp"
+
+namespace asio {
+namespace ssl {
+namespace detail {
+
+class openssl_context_service
+ : public asio::io_service::service
+{
+public:
+ // The native type of the context.
+ typedef ::SSL_CTX* impl_type;
+
+ // The type for the password callback function object.
+ typedef boost::function<std::string(std::size_t,
+ context_base::password_purpose)> password_callback_type;
+
+ // Constructor.
+ openssl_context_service(asio::io_service& io_service)
+ : asio::io_service::service(io_service)
+ {
+ }
+
+ // Destroy all user-defined handler objects owned by the service.
+ void shutdown_service()
+ {
+ }
+
+ // Return a null context implementation.
+ static impl_type null()
+ {
+ return 0;
+ }
+
+ // Create a new context implementation.
+ void create(impl_type& impl, context_base::method m)
+ {
+ ::SSL_METHOD* ssl_method = 0;
+ switch (m)
+ {
+ case context_base::sslv2:
+ ssl_method = ::SSLv2_method();
+ break;
+ case context_base::sslv2_client:
+ ssl_method = ::SSLv2_client_method();
+ break;
+ case context_base::sslv2_server:
+ ssl_method = ::SSLv2_server_method();
+ break;
+ case context_base::sslv3:
+ ssl_method = ::SSLv3_method();
+ break;
+ case context_base::sslv3_client:
+ ssl_method = ::SSLv3_client_method();
+ break;
+ case context_base::sslv3_server:
+ ssl_method = ::SSLv3_server_method();
+ break;
+ case context_base::tlsv1:
+ ssl_method = ::TLSv1_method();
+ break;
+ case context_base::tlsv1_client:
+ ssl_method = ::TLSv1_client_method();
+ break;
+ case context_base::tlsv1_server:
+ ssl_method = ::TLSv1_server_method();
+ break;
+ case context_base::sslv23:
+ ssl_method = ::SSLv23_method();
+ break;
+ case context_base::sslv23_client:
+ ssl_method = ::SSLv23_client_method();
+ break;
+ case context_base::sslv23_server:
+ ssl_method = ::SSLv23_server_method();
+ break;
+ default:
+ break;
+ }
+ impl = ::SSL_CTX_new(ssl_method);
+ }
+
+ // Destroy a context implementation.
+ void destroy(impl_type& impl)
+ {
+ if (impl != null())
+ {
+ if (impl->default_passwd_callback_userdata)
+ {
+ password_callback_type* callback =
+ static_cast<password_callback_type*>(
+ impl->default_passwd_callback_userdata);
+ delete callback;
+ impl->default_passwd_callback_userdata = 0;
+ }
+
+ ::SSL_CTX_free(impl);
+ impl = null();
+ }
+ }
+
+ // Set options on the context.
+ template <typename Error_Handler>
+ void set_options(impl_type& impl, context_base::options o,
+ Error_Handler error_handler)
+ {
+ ::SSL_CTX_set_options(impl, o);
+
+ asio::error e;
+ error_handler(e);
+ }
+
+ // Set peer verification mode.
+ template <typename Error_Handler>
+ void set_verify_mode(impl_type& impl, context_base::verify_mode v,
+ Error_Handler error_handler)
+ {
+ ::SSL_CTX_set_verify(impl, v, 0);
+
+ asio::error e;
+ error_handler(e);
+ }
+
+ // Load a certification authority file for performing verification.
+ template <typename Error_Handler>
+ void load_verify_file(impl_type& impl, const std::string& filename,
+ Error_Handler error_handler)
+ {
+ if (::SSL_CTX_load_verify_locations(impl, filename.c_str(), 0) != 1)
+ {
+ asio::error e(asio::error::invalid_argument);
+ error_handler(e);
+ return;
+ }
+
+ asio::error e;
+ error_handler(e);
+ }
+
+ // Add a directory containing certification authority files to be used for
+ // performing verification.
+ template <typename Error_Handler>
+ void add_verify_path(impl_type& impl, const std::string& path,
+ Error_Handler error_handler)
+ {
+ if (::SSL_CTX_load_verify_locations(impl, 0, path.c_str()) != 1)
+ {
+ asio::error e(asio::error::invalid_argument);
+ error_handler(e);
+ return;
+ }
+
+ asio::error e;
+ error_handler(e);
+ }
+
+ // Use a certificate from a file.
+ template <typename Error_Handler>
+ void use_certificate_file(impl_type& impl, const std::string& filename,
+ context_base::file_format format, Error_Handler error_handler)
+ {
+ int file_type;
+ switch (format)
+ {
+ case context_base::asn1:
+ file_type = SSL_FILETYPE_ASN1;
+ break;
+ case context_base::pem:
+ file_type = SSL_FILETYPE_PEM;
+ break;
+ default:
+ {
+ asio::error e(asio::error::invalid_argument);
+ error_handler(e);
+ return;
+ }
+ }
+
+ if (::SSL_CTX_use_certificate_file(impl, filename.c_str(), file_type) != 1)
+ {
+ asio::error e(asio::error::invalid_argument);
+ error_handler(e);
+ return;
+ }
+
+ asio::error e;
+ error_handler(e);
+ }
+
+ // Use a certificate chain from a file.
+ template <typename Error_Handler>
+ void use_certificate_chain_file(impl_type& impl, const std::string& filename,
+ Error_Handler error_handler)
+ {
+ if (::SSL_CTX_use_certificate_chain_file(impl, filename.c_str()) != 1)
+ {
+ asio::error e(asio::error::invalid_argument);
+ error_handler(e);
+ return;
+ }
+
+ asio::error e;
+ error_handler(e);
+ }
+
+ // Use a private key from a file.
+ template <typename Error_Handler>
+ void use_private_key_file(impl_type& impl, const std::string& filename,
+ context_base::file_format format, Error_Handler error_handler)
+ {
+ int file_type;
+ switch (format)
+ {
+ case context_base::asn1:
+ file_type = SSL_FILETYPE_ASN1;
+ break;
+ case context_base::pem:
+ file_type = SSL_FILETYPE_PEM;
+ break;
+ default:
+ {
+ asio::error e(asio::error::invalid_argument);
+ error_handler(e);
+ return;
+ }
+ }
+
+ if (::SSL_CTX_use_PrivateKey_file(impl, filename.c_str(), file_type) != 1)
+ {
+ asio::error e(asio::error::invalid_argument);
+ error_handler(e);
+ return;
+ }
+
+ asio::error e;
+ error_handler(e);
+ }
+
+ // Use an RSA private key from a file.
+ template <typename Error_Handler>
+ void use_rsa_private_key_file(impl_type& impl, const std::string& filename,
+ context_base::file_format format, Error_Handler error_handler)
+ {
+ int file_type;
+ switch (format)
+ {
+ case context_base::asn1:
+ file_type = SSL_FILETYPE_ASN1;
+ break;
+ case context_base::pem:
+ file_type = SSL_FILETYPE_PEM;
+ break;
+ default:
+ {
+ asio::error e(asio::error::invalid_argument);
+ error_handler(e);
+ return;
+ }
+ }
+
+ if (::SSL_CTX_use_RSAPrivateKey_file(
+ impl, filename.c_str(), file_type) != 1)
+ {
+ asio::error e(asio::error::invalid_argument);
+ error_handler(e);
+ return;
+ }
+
+ asio::error e;
+ error_handler(e);
+ }
+
+ // Use the specified file to obtain the temporary Diffie-Hellman parameters.
+ template <typename Error_Handler>
+ void use_tmp_dh_file(impl_type& impl, const std::string& filename,
+ Error_Handler error_handler)
+ {
+ ::BIO* bio = ::BIO_new_file(filename.c_str(), "r");
+ if (!bio)
+ {
+ asio::error e(asio::error::invalid_argument);
+ error_handler(e);
+ return;
+ }
+
+ ::DH* dh = ::PEM_read_bio_DHparams(bio, 0, 0, 0);
+ if (!dh)
+ {
+ ::BIO_free(bio);
+ asio::error e(asio::error::invalid_argument);
+ error_handler(e);
+ return;
+ }
+
+ ::BIO_free(bio);
+ int result = ::SSL_CTX_set_tmp_dh(impl, dh);
+ if (result != 1)
+ {
+ ::DH_free(dh);
+ asio::error e(asio::error::invalid_argument);
+ error_handler(e);
+ return;
+ }
+
+ asio::error e;
+ error_handler(e);
+ }
+
+ static int password_callback(char* buf, int size, int purpose, void* data)
+ {
+ using namespace std; // For strncat and strlen.
+
+ if (data)
+ {
+ password_callback_type* callback =
+ static_cast<password_callback_type*>(data);
+ std::string passwd = (*callback)(static_cast<std::size_t>(size),
+ purpose ? context_base::for_writing : context_base::for_reading);
+ *buf = '\0';
+ strncat(buf, passwd.c_str(), size);
+ return strlen(buf);
+ }
+
+ return 0;
+ }
+
+ // Set the password callback.
+ template <typename Password_Callback, typename Error_Handler>
+ void set_password_callback(impl_type& impl, Password_Callback callback,
+ Error_Handler error_handler)
+ {
+ // Allocate callback function object if not already present.
+ if (impl->default_passwd_callback_userdata)
+ {
+ password_callback_type* callback_function =
+ static_cast<password_callback_type*>(
+ impl->default_passwd_callback_userdata);
+ *callback_function = callback;
+ }
+ else
+ {
+ password_callback_type* callback_function =
+ new password_callback_type(callback);
+ impl->default_passwd_callback_userdata = callback_function;
+ }
+
+ // Set the password callback.
+ SSL_CTX_set_default_passwd_cb(impl,
+ &openssl_context_service::password_callback);
+
+ asio::error e;
+ error_handler(e);
+ }
+
+private:
+ // Ensure openssl is initialised.
+ openssl_init<> init_;
+};
+
+} // namespace detail
+} // namespace ssl
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_SSL_DETAIL_OPENSSL_CONTEXT_SERVICE_HPP
diff --git a/library/include/libtorrent/asio/ssl/detail/openssl_init.hpp b/library/include/libtorrent/asio/ssl/detail/openssl_init.hpp
new file mode 100755
index 000000000..60d19b787
--- /dev/null
+++ b/library/include/libtorrent/asio/ssl/detail/openssl_init.hpp
@@ -0,0 +1,128 @@
+//
+// openssl_init.hpp
+// ~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2005 Voipster / Indrek dot Juhani at voipster dot com
+// Copyright (c) 2005 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_SSL_DETAIL_OPENSSL_INIT_HPP
+#define ASIO_SSL_DETAIL_OPENSSL_INIT_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/detail/push_options.hpp"
+#include <vector>
+#include <boost/shared_ptr.hpp>
+#include "asio/detail/pop_options.hpp"
+
+#include "asio/detail/mutex.hpp"
+#include "asio/ssl/detail/openssl_types.hpp"
+
+namespace asio {
+namespace ssl {
+namespace detail {
+
+template <bool Do_Init = true>
+class openssl_init
+ : private boost::noncopyable
+{
+private:
+ // Structure to perform the actual initialisation.
+ class do_init
+ {
+ public:
+ do_init()
+ {
+ if (Do_Init)
+ {
+ ::SSL_library_init();
+ ::SSL_load_error_strings();
+
+ mutexes_.resize(::CRYPTO_num_locks());
+ for (size_t i = 0; i < mutexes_.size(); ++i)
+ mutexes_[i].reset(new asio::detail::mutex);
+ ::CRYPTO_set_locking_callback(&do_init::openssl_locking_func);
+
+ ::OpenSSL_add_ssl_algorithms();
+ }
+ }
+
+ ~do_init()
+ {
+ if (Do_Init)
+ {
+ ::CRYPTO_set_locking_callback(0);
+ ::ERR_free_strings();
+ ::ERR_remove_state(0);
+ ::EVP_cleanup();
+ ::CRYPTO_cleanup_all_ex_data();
+ ::CONF_modules_unload(1);
+ ::ENGINE_cleanup();
+ }
+ }
+
+ // Helper function to manage a do_init singleton. The static instance of the
+ // openssl_init object ensures that this function is always called before
+ // main, and therefore before any other threads can get started. The do_init
+ // instance must be static in this function to ensure that it gets
+ // initialised before any other global objects try to use it.
+ static boost::shared_ptr<do_init> instance()
+ {
+ static boost::shared_ptr<do_init> init(new do_init);
+ return init;
+ }
+
+ private:
+ static void openssl_locking_func(int mode, int n,
+ const char *file, int line)
+ {
+ if (mode & CRYPTO_LOCK)
+ instance()->mutexes_[n]->lock();
+ else
+ instance()->mutexes_[n]->unlock();
+ }
+
+ // Mutexes to be used in locking callbacks.
+ std::vector<boost::shared_ptr<asio::detail::mutex> > mutexes_;
+ };
+
+public:
+ // Constructor.
+ openssl_init()
+ : ref_(do_init::instance())
+ {
+ while (&instance_ == 0); // Ensure openssl_init::instance_ is linked in.
+ }
+
+ // Destructor.
+ ~openssl_init()
+ {
+ }
+
+private:
+ // Instance to force initialisation of openssl at global scope.
+ static openssl_init instance_;
+
+ // Reference to singleton do_init object to ensure that openssl does not get
+ // cleaned up until the last user has finished with it.
+ boost::shared_ptr<do_init> ref_;
+};
+
+template <bool Do_Init>
+openssl_init<Do_Init> openssl_init<Do_Init>::instance_;
+
+} // namespace detail
+} // namespace ssl
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_SSL_DETAIL_OPENSSL_INIT_HPP
diff --git a/library/include/libtorrent/asio/ssl/detail/openssl_operation.hpp b/library/include/libtorrent/asio/ssl/detail/openssl_operation.hpp
new file mode 100755
index 000000000..1497638f1
--- /dev/null
+++ b/library/include/libtorrent/asio/ssl/detail/openssl_operation.hpp
@@ -0,0 +1,474 @@
+//
+// openssl_operation.hpp
+// ~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2005 Voipster / Indrek dot Juhani at voipster dot com
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_SSL_DETAIL_OPENSSL_OPERATION_HPP
+#define ASIO_SSL_DETAIL_OPENSSL_OPERATION_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/detail/push_options.hpp"
+#include <boost/function.hpp>
+#include <boost/bind.hpp>
+#include "asio/detail/pop_options.hpp"
+
+#include "asio/buffer.hpp"
+#include "asio/placeholders.hpp"
+#include "asio/write.hpp"
+#include "asio/detail/socket_ops.hpp"
+#include "asio/ssl/detail/openssl_types.hpp"
+
+namespace asio {
+namespace ssl {
+namespace detail {
+
+typedef boost::function<int (::SSL*)> ssl_primitive_func;
+typedef boost::function<void (const error&, int)> user_handler_func;
+
+// Network send_/recv buffer implementation
+//
+//
+class net_buffer
+{
+ static const int NET_BUF_SIZE = 16*1024 + 256; // SSL record size + spare
+
+ unsigned char buf_[NET_BUF_SIZE];
+ unsigned char* data_start_;
+ unsigned char* data_end_;
+
+public:
+ net_buffer()
+ {
+ data_start_ = data_end_ = buf_;
+ }
+ unsigned char* get_unused_start() { return data_end_; }
+ unsigned char* get_data_start() { return data_start_; }
+ size_t get_unused_len() { return (NET_BUF_SIZE - (data_end_ - buf_)); }
+ size_t get_data_len() { return (data_end_ - data_start_); }
+ void data_added(size_t count)
+ {
+ data_end_ += count;
+ data_end_ = data_end_ > (buf_ + NET_BUF_SIZE)?
+ (buf_ + NET_BUF_SIZE):
+ data_end_;
+ }
+ void data_removed(size_t count)
+ {
+ data_start_ += count;
+ if (data_start_ >= data_end_) reset();
+ }
+ void reset() { data_start_ = buf_; data_end_ = buf_; }
+ bool has_data() { return (data_start_ < data_end_); }
+}; // class net_buffer
+
+//
+// Operation class
+//
+//
+template <typename Stream>
+class openssl_operation
+{
+public:
+
+ // Constructor for asynchronous operations
+ openssl_operation(ssl_primitive_func primitive,
+ Stream& socket,
+ net_buffer& recv_buf,
+ SSL* session,
+ BIO* ssl_bio,
+ user_handler_func handler
+ )
+ : primitive_(primitive)
+ , user_handler_(handler)
+ , recv_buf_(recv_buf)
+ , socket_(socket)
+ , ssl_bio_(ssl_bio)
+ , session_(session)
+ {
+ write_ = boost::bind(
+ &openssl_operation::do_async_write,
+ this, boost::arg<1>(), boost::arg<2>()
+ );
+ handler_= boost::bind(
+ &openssl_operation::async_user_handler,
+ this, boost::arg<1>(), boost::arg<2>()
+ );
+ }
+
+ // Constructor for synchronous operations
+ openssl_operation(ssl_primitive_func primitive,
+ Stream& socket,
+ net_buffer& recv_buf,
+ SSL* session,
+ BIO* ssl_bio)
+ : primitive_(primitive)
+ , recv_buf_(recv_buf)
+ , socket_(socket)
+ , ssl_bio_(ssl_bio)
+ , session_(session)
+ {
+ write_ = boost::bind(
+ &openssl_operation::do_sync_write,
+ this, boost::arg<1>(), boost::arg<2>()
+ );
+ handler_ = boost::bind(
+ &openssl_operation::sync_user_handler,
+ this, boost::arg<1>(), boost::arg<2>()
+ );
+ }
+
+ // Start operation
+ // In case of asynchronous it returns 0, in sync mode returns success code
+ // or throws an error...
+ int start()
+ {
+ int rc = primitive_( session_ );
+ int sys_error_code = ERR_get_error();
+ bool is_operation_done = (rc > 0);
+ // For connect/accept/shutdown, the operation
+ // is done, when return code is 1
+ // for write, it is done, when is retcode > 0
+ // for read, is is done when retcode > 0
+
+ int error_code = !is_operation_done ?
+ ::SSL_get_error( session_, rc ) :
+ 0;
+ bool is_read_needed = (error_code == SSL_ERROR_WANT_READ);
+ bool is_write_needed = (error_code == SSL_ERROR_WANT_WRITE ||
+ ::BIO_ctrl_pending( ssl_bio_ ));
+ bool is_shut_down_received =
+ ((::SSL_get_shutdown( session_ ) & SSL_RECEIVED_SHUTDOWN) ==
+ SSL_RECEIVED_SHUTDOWN);
+ bool is_shut_down_sent =
+ ((::SSL_get_shutdown( session_ ) & SSL_SENT_SHUTDOWN) ==
+ SSL_SENT_SHUTDOWN);
+
+ if (is_shut_down_sent && is_shut_down_received && is_operation_done)
+ // SSL connection is shut down cleanly
+ return handler_(asio::error(), 1);
+
+ if (is_shut_down_received && !is_write_needed)
+ return handler_(asio::error(asio::error::eof), 0);
+
+ if (is_shut_down_received)
+ // Shutdown has been requested, while we were reading or writing...
+ // abort our action...
+ return handler_(asio::error(asio::error::shut_down), 0);
+
+ if (!is_operation_done && !is_read_needed && !is_write_needed
+ && !is_shut_down_sent)
+ {
+ // The operation has failed... It is not completed and does
+ // not want network communication nor does want to send shutdown out...
+ if (error_code == SSL_ERROR_SYSCALL)
+ return handler_(asio::error(sys_error_code), rc);
+ else
+ return handler_(asio::error(error_code + 1000000), rc);
+ }
+
+ if (!is_operation_done && !is_write_needed)
+ {
+ // We may have left over data that we can pass to SSL immediately
+ if (recv_buf_.get_data_len() > 0)
+ {
+ // Pass the buffered data to SSL
+ int written = ::BIO_write
+ (
+ ssl_bio_,
+ recv_buf_.get_data_start(),
+ recv_buf_.get_data_len()
+ );
+
+ if (written > 0)
+ {
+ recv_buf_.data_removed(written);
+ }
+ else if (written < 0)
+ {
+ if (!BIO_should_retry(ssl_bio_))
+ {
+ // Some serios error with BIO....
+ return handler_(asio::error(asio::error::no_recovery), 0);
+ }
+ }
+
+ return start();
+ }
+ }
+
+ // Continue with operation, flush any SSL data out to network...
+ return write_(is_operation_done, rc);
+ }
+
+// Private implementation
+private:
+ typedef boost::function<int (const asio::error&, int)>
+ int_handler_func;
+ typedef boost::function<int (bool, int)> write_func;
+
+ ssl_primitive_func primitive_;
+ user_handler_func user_handler_;
+ write_func write_;
+ int_handler_func handler_;
+
+ net_buffer send_buf_; // buffers for network IO
+
+ // The recv buffer is owned by the stream, not the operation, since there can
+ // be left over bytes after passing the data up to the application, and these
+ // bytes need to be kept around for the next read operation issued by the
+ // application.
+ net_buffer& recv_buf_;
+
+ Stream& socket_;
+ BIO* ssl_bio_;
+ SSL* session_;
+
+ //
+ int sync_user_handler(const asio::error& error, int rc)
+ {
+ if (!error)
+ return rc;
+
+ throw error;
+ }
+
+ int async_user_handler(const asio::error& error, int rc)
+ {
+ user_handler_(error, rc);
+ return 0;
+ }
+
+ // Writes bytes asynchronously from SSL to NET
+ int do_async_write(bool is_operation_done, int rc)
+ {
+ int len = ::BIO_ctrl_pending( ssl_bio_ );
+ if ( len )
+ {
+ // There is something to write into net, do it...
+ len = (int)send_buf_.get_unused_len() > len?
+ len:
+ send_buf_.get_unused_len();
+
+ if (len == 0)
+ {
+ // In case our send buffer is full, we have just to wait until
+ // previous send to complete...
+ return 0;
+ }
+
+ // Read outgoing data from bio
+ len = ::BIO_read( ssl_bio_, send_buf_.get_unused_start(), len);
+
+ if (len > 0)
+ {
+ unsigned char *data_start = send_buf_.get_unused_start();
+ send_buf_.data_added(len);
+
+ asio::async_write
+ (
+ socket_,
+ asio::buffer(data_start, len),
+ boost::bind
+ (
+ &openssl_operation::async_write_handler,
+ this,
+ is_operation_done,
+ rc,
+ asio::placeholders::error,
+ asio::placeholders::bytes_transferred
+ )
+ );
+
+ return 0;
+ }
+ else if (!BIO_should_retry(ssl_bio_))
+ {
+ // Seems like fatal error
+ // reading from SSL BIO has failed...
+ handler_(asio::error(asio::error::no_recovery), 0);
+ return 0;
+ }
+ }
+
+ if (is_operation_done)
+ {
+ // Finish the operation, with success
+ handler_(asio::error(), rc);
+ return 0;
+ }
+
+ // OPeration is not done and writing to net has been made...
+ // start reading...
+ do_async_read();
+
+ return 0;
+ }
+
+ void async_write_handler(bool is_operation_done, int rc,
+ const asio::error& error, size_t bytes_sent)
+ {
+ if (!error)
+ {
+ // Remove data from send buffer
+ send_buf_.data_removed(bytes_sent);
+
+ if (is_operation_done)
+ handler_(asio::error(), rc);
+ else
+ // Since the operation was not completed, try it again...
+ start();
+ }
+ else
+ handler_(error, rc);
+ }
+
+ void do_async_read()
+ {
+ // Wait for new data
+ socket_.async_read_some
+ (
+ asio::buffer(recv_buf_.get_unused_start(),
+ recv_buf_.get_unused_len()),
+ boost::bind
+ (
+ &openssl_operation::async_read_handler,
+ this,
+ asio::placeholders::error,
+ asio::placeholders::bytes_transferred
+ )
+ );
+ }
+
+ void async_read_handler(const asio::error& error, size_t bytes_recvd)
+ {
+ if (!error)
+ {
+ recv_buf_.data_added(bytes_recvd);
+
+ // Pass the received data to SSL
+ int written = ::BIO_write
+ (
+ ssl_bio_,
+ recv_buf_.get_data_start(),
+ recv_buf_.get_data_len()
+ );
+
+ if (written > 0)
+ {
+ recv_buf_.data_removed(written);
+ }
+ else if (written < 0)
+ {
+ if (!BIO_should_retry(ssl_bio_))
+ {
+ // Some serios error with BIO....
+ handler_(asio::error(asio::error::no_recovery), 0);
+ return;
+ }
+ }
+
+ // and try the SSL primitive again
+ start();
+ }
+ else
+ {
+ // Error in network level...
+ // SSL can't continue either...
+ handler_(error, 0);
+ }
+ }
+
+ // Syncronous functions...
+ int do_sync_write(bool is_operation_done, int rc)
+ {
+ int len = ::BIO_ctrl_pending( ssl_bio_ );
+ if ( len )
+ {
+ // There is something to write into net, do it...
+ len = (int)send_buf_.get_unused_len() > len?
+ len:
+ send_buf_.get_unused_len();
+
+ // Read outgoing data from bio
+ len = ::BIO_read( ssl_bio_, send_buf_.get_unused_start(), len);
+
+ if (len > 0)
+ {
+ size_t sent_len = asio::write(
+ socket_,
+ asio::buffer(send_buf_.get_unused_start(), len)
+ );
+
+ send_buf_.data_added(len);
+ send_buf_.data_removed(sent_len);
+ }
+ else if (!BIO_should_retry(ssl_bio_))
+ {
+ // Seems like fatal error
+ // reading from SSL BIO has failed...
+ throw asio::error(asio::error::no_recovery);
+ }
+ }
+
+ if (is_operation_done)
+ // Finish the operation, with success
+ return rc;
+
+ // Operation is not finished, read data from net...
+ return do_sync_read();
+ }
+
+ int do_sync_read()
+ {
+ size_t len = socket_.read_some
+ (
+ asio::buffer(recv_buf_.get_unused_start(),
+ recv_buf_.get_unused_len())
+ );
+
+ // Write data to ssl
+ recv_buf_.data_added(len);
+
+ // Pass the received data to SSL
+ int written = ::BIO_write
+ (
+ ssl_bio_,
+ recv_buf_.get_data_start(),
+ recv_buf_.get_data_len()
+ );
+
+ if (written > 0)
+ {
+ recv_buf_.data_removed(written);
+ }
+ else if (written < 0)
+ {
+ if (!BIO_should_retry(ssl_bio_))
+ {
+ // Some serios error with BIO....
+ throw asio::error(asio::error::no_recovery);
+ }
+ }
+
+ // Try the operation again
+ return start();
+ }
+}; // class openssl_operation
+
+} // namespace detail
+} // namespace ssl
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_SSL_DETAIL_OPENSSL_OPERATION_HPP
diff --git a/library/include/libtorrent/asio/ssl/detail/openssl_stream_service.hpp b/library/include/libtorrent/asio/ssl/detail/openssl_stream_service.hpp
new file mode 100644
index 000000000..d190ce937
--- /dev/null
+++ b/library/include/libtorrent/asio/ssl/detail/openssl_stream_service.hpp
@@ -0,0 +1,505 @@
+//
+// stream_service.hpp
+// ~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2005 Voipster / Indrek dot Juhani at voipster dot com
+// Copyright (c) 2005 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_SSL_DETAIL_OPENSSL_STREAM_SERVICE_HPP
+#define ASIO_SSL_DETAIL_OPENSSL_STREAM_SERVICE_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/detail/push_options.hpp"
+#include <cstddef>
+#include <boost/config.hpp>
+#include <boost/noncopyable.hpp>
+#include <boost/function.hpp>
+#include <boost/bind.hpp>
+#include "asio/detail/pop_options.hpp"
+
+#include "asio/io_service.hpp"
+#include "asio/ssl/basic_context.hpp"
+#include "asio/ssl/stream_base.hpp"
+#include "asio/ssl/detail/openssl_operation.hpp"
+#include "asio/ssl/detail/openssl_types.hpp"
+
+namespace asio {
+namespace ssl {
+namespace detail {
+
+class openssl_stream_service
+ : public asio::io_service::service
+{
+private:
+ //Base handler for asyncrhonous operations
+ template <typename Stream>
+ class base_handler
+ {
+ public:
+ typedef boost::function<void (const asio::error&, size_t)> func_t;
+
+ base_handler(asio::io_service& io_service)
+ : op_(NULL)
+ , io_service_(io_service)
+ , work_(io_service)
+ {}
+
+ void do_func(const asio::error& error, size_t size)
+ {
+ func_(error, size);
+ }
+
+ void set_operation(openssl_operation<Stream>* op) { op_ = op; }
+ void set_func(func_t func) { func_ = func; }
+
+ ~base_handler()
+ {
+ delete op_;
+ }
+
+ private:
+ func_t func_;
+ openssl_operation<Stream>* op_;
+ asio::io_service& io_service_;
+ asio::io_service::work work_;
+ }; // class base_handler
+
+ // Handler for asynchronous IO (write/read) operations
+ template<typename Stream, typename Handler>
+ class io_handler
+ : public base_handler<Stream>
+ {
+ public:
+ io_handler(Handler handler, asio::io_service& io_service)
+ : base_handler<Stream>(io_service)
+ , handler_(handler)
+ {
+ set_func(boost::bind(
+ &io_handler<Stream, Handler>::handler_impl,
+ this, boost::arg<1>(), boost::arg<2>() ));
+ }
+
+ private:
+ Handler handler_;
+ void handler_impl(const asio::error& error, size_t size)
+ {
+ handler_(error, size);
+ delete this;
+ }
+ }; // class io_handler
+
+ // Handler for asyncrhonous handshake (connect, accept) functions
+ template <typename Stream, typename Handler>
+ class handshake_handler
+ : public base_handler<Stream>
+ {
+ public:
+ handshake_handler(Handler handler, asio::io_service& io_service)
+ : base_handler<Stream>(io_service)
+ , handler_(handler)
+ {
+ set_func(boost::bind(
+ &handshake_handler<Stream, Handler>::handler_impl,
+ this, boost::arg<1>(), boost::arg<2>() ));
+ }
+
+ private:
+ Handler handler_;
+ void handler_impl(const asio::error& error, size_t)
+ {
+ handler_(error);
+ delete this;
+ }
+
+ }; // class handshake_handler
+
+ // Handler for asyncrhonous shutdown
+ template <typename Stream, typename Handler>
+ class shutdown_handler
+ : public base_handler<Stream>
+ {
+ public:
+ shutdown_handler(Handler handler, asio::io_service& io_service)
+ : base_handler<Stream>(io_service),
+ handler_(handler)
+ {
+ set_func(boost::bind(
+ &shutdown_handler<Stream, Handler>::handler_impl,
+ this, boost::arg<1>(), boost::arg<2>() ));
+ }
+
+ private:
+ Handler handler_;
+ void handler_impl(const asio::error& error, size_t)
+ {
+ handler_(error);
+ delete this;
+ }
+ }; // class shutdown_handler
+
+public:
+ // The implementation type.
+ typedef struct impl_struct
+ {
+ ::SSL* ssl;
+ ::BIO* ext_bio;
+ net_buffer recv_buf;
+ } * impl_type;
+
+ // Construct a new stream socket service for the specified io_service.
+ explicit openssl_stream_service(asio::io_service& io_service)
+ : asio::io_service::service(io_service)
+ {
+ }
+
+ // Destroy all user-defined handler objects owned by the service.
+ void shutdown_service()
+ {
+ }
+
+ // Return a null stream implementation.
+ impl_type null() const
+ {
+ return 0;
+ }
+
+ // Create a new stream implementation.
+ template <typename Stream, typename Context_Service>
+ void create(impl_type& impl, Stream& next_layer,
+ basic_context<Context_Service>& context)
+ {
+ impl = new impl_struct;
+ impl->ssl = ::SSL_new(context.impl());
+ ::SSL_set_mode(impl->ssl, SSL_MODE_ENABLE_PARTIAL_WRITE);
+ ::SSL_set_mode(impl->ssl, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
+ ::BIO* int_bio = 0;
+ impl->ext_bio = 0;
+ ::BIO_new_bio_pair(&int_bio, 8192, &impl->ext_bio, 8192);
+ ::SSL_set_bio(impl->ssl, int_bio, int_bio);
+ }
+
+ // Destroy a stream implementation.
+ template <typename Stream>
+ void destroy(impl_type& impl, Stream& next_layer)
+ {
+ if (impl != 0)
+ {
+ ::BIO_free(impl->ext_bio);
+ ::SSL_free(impl->ssl);
+ delete impl;
+ impl = 0;
+ }
+ }
+
+ // Perform SSL handshaking.
+ template <typename Stream, typename Error_Handler>
+ void handshake(impl_type& impl, Stream& next_layer,
+ stream_base::handshake_type type, Error_Handler error_handler)
+ {
+ try
+ {
+ openssl_operation<Stream> op(
+ type == stream_base::client ?
+ &ssl_wrap<mutex_type>::SSL_connect:
+ &ssl_wrap<mutex_type>::SSL_accept,
+ next_layer,
+ impl->recv_buf,
+ impl->ssl,
+ impl->ext_bio);
+ op.start();
+ }
+ catch (asio::error& e)
+ {
+ error_handler(e);
+ return;
+ }
+
+ asio::error e;
+ error_handler(e);
+ }
+
+ // Start an asynchronous SSL handshake.
+ template <typename Stream, typename Handler>
+ void async_handshake(impl_type& impl, Stream& next_layer,
+ stream_base::handshake_type type, Handler handler)
+ {
+ typedef handshake_handler<Stream, Handler> connect_handler;
+
+ connect_handler* local_handler =
+ new connect_handler(handler, io_service());
+
+ openssl_operation<Stream>* op = new openssl_operation<Stream>
+ (
+ type == stream_base::client ?
+ &ssl_wrap<mutex_type>::SSL_connect:
+ &ssl_wrap<mutex_type>::SSL_accept,
+ next_layer,
+ impl->recv_buf,
+ impl->ssl,
+ impl->ext_bio,
+ boost::bind
+ (
+ &base_handler<Stream>::do_func,
+ local_handler,
+ boost::arg<1>(),
+ boost::arg<2>()
+ )
+ );
+ local_handler->set_operation(op);
+
+ io_service().post(boost::bind(&openssl_operation<Stream>::start, op));
+ }
+
+ // Shut down SSL on the stream.
+ template <typename Stream, typename Error_Handler>
+ void shutdown(impl_type& impl, Stream& next_layer,
+ Error_Handler error_handler)
+ {
+ try
+ {
+ openssl_operation<Stream> op(
+ &ssl_wrap<mutex_type>::SSL_shutdown,
+ next_layer,
+ impl->recv_buf,
+ impl->ssl,
+ impl->ext_bio);
+ op.start();
+ }
+ catch (asio::error& e)
+ {
+ error_handler(e);
+ return;
+ }
+
+ asio::error e;
+ error_handler(e);
+ }
+
+ // Asynchronously shut down SSL on the stream.
+ template <typename Stream, typename Handler>
+ void async_shutdown(impl_type& impl, Stream& next_layer, Handler handler)
+ {
+ typedef shutdown_handler<Stream, Handler> disconnect_handler;
+
+ disconnect_handler* local_handler =
+ new disconnect_handler(handler, io_service());
+
+ openssl_operation<Stream>* op = new openssl_operation<Stream>
+ (
+ &ssl_wrap<mutex_type>::SSL_shutdown,
+ next_layer,
+ impl->recv_buf,
+ impl->ssl,
+ impl->ext_bio,
+ boost::bind
+ (
+ &base_handler<Stream>::do_func,
+ local_handler,
+ boost::arg<1>(),
+ boost::arg<2>()
+ )
+ );
+ local_handler->set_operation(op);
+
+ io_service().post(boost::bind(&openssl_operation<Stream>::start, op));
+ }
+
+ // Write some data to the stream.
+ template <typename Stream, typename Const_Buffers, typename Error_Handler>
+ std::size_t write_some(impl_type& impl, Stream& next_layer,
+ const Const_Buffers& buffers, Error_Handler error_handler)
+ {
+ size_t bytes_transferred = 0;
+ try
+ {
+ boost::function<int (SSL*)> send_func =
+ boost::bind(&::SSL_write, boost::arg<1>(),
+ asio::buffer_cast<const void*>(*buffers.begin()),
+ static_cast<int>(asio::buffer_size(*buffers.begin())));
+ openssl_operation<Stream> op(
+ send_func,
+ next_layer,
+ impl->recv_buf,
+ impl->ssl,
+ impl->ext_bio
+ );
+ bytes_transferred = static_cast<size_t>(op.start());
+ }
+ catch (asio::error& e)
+ {
+ error_handler(e);
+ return 0;
+ }
+
+ asio::error e;
+ error_handler(e);
+ return bytes_transferred;
+ }
+
+ // Start an asynchronous write.
+ template <typename Stream, typename Const_Buffers, typename Handler>
+ void async_write_some(impl_type& impl, Stream& next_layer,
+ const Const_Buffers& buffers, Handler handler)
+ {
+ typedef io_handler<Stream, Handler> send_handler;
+
+ send_handler* local_handler = new send_handler(handler, io_service());
+
+ boost::function<int (SSL*)> send_func =
+ boost::bind(&::SSL_write, boost::arg<1>(),
+ asio::buffer_cast<const void*>(*buffers.begin()),
+ static_cast<int>(asio::buffer_size(*buffers.begin())));
+
+ openssl_operation<Stream>* op = new openssl_operation<Stream>
+ (
+ send_func,
+ next_layer,
+ impl->recv_buf,
+ impl->ssl,
+ impl->ext_bio,
+ boost::bind
+ (
+ &base_handler<Stream>::do_func,
+ local_handler,
+ boost::arg<1>(),
+ boost::arg<2>()
+ )
+ );
+ local_handler->set_operation(op);
+
+ io_service().post(boost::bind(&openssl_operation<Stream>::start, op));
+ }
+
+ // Read some data from the stream.
+ template <typename Stream, typename Mutable_Buffers, typename Error_Handler>
+ std::size_t read_some(impl_type& impl, Stream& next_layer,
+ const Mutable_Buffers& buffers, Error_Handler error_handler)
+ {
+ size_t bytes_transferred = 0;
+ try
+ {
+ boost::function<int (SSL*)> recv_func =
+ boost::bind(&::SSL_read, boost::arg<1>(),
+ asio::buffer_cast<void*>(*buffers.begin()),
+ asio::buffer_size(*buffers.begin()));
+ openssl_operation<Stream> op(recv_func,
+ next_layer,
+ impl->recv_buf,
+ impl->ssl,
+ impl->ext_bio
+ );
+
+ bytes_transferred = static_cast<size_t>(op.start());
+ }
+ catch (asio::error& e)
+ {
+ error_handler(e);
+ return 0;
+ }
+
+ asio::error e;
+ error_handler(e);
+ return bytes_transferred;
+ }
+
+ // Start an asynchronous read.
+ template <typename Stream, typename Mutable_Buffers, typename Handler>
+ void async_read_some(impl_type& impl, Stream& next_layer,
+ const Mutable_Buffers& buffers, Handler handler)
+ {
+ typedef io_handler<Stream, Handler> recv_handler;
+
+ recv_handler* local_handler = new recv_handler(handler, io_service());
+
+ boost::function<int (SSL*)> recv_func =
+ boost::bind(&::SSL_read, boost::arg<1>(),
+ asio::buffer_cast<void*>(*buffers.begin()),
+ asio::buffer_size(*buffers.begin()));
+
+ openssl_operation<Stream>* op = new openssl_operation<Stream>
+ (
+ recv_func,
+ next_layer,
+ impl->recv_buf,
+ impl->ssl,
+ impl->ext_bio,
+ boost::bind
+ (
+ &base_handler<Stream>::do_func,
+ local_handler,
+ boost::arg<1>(),
+ boost::arg<2>()
+ )
+ );
+ local_handler->set_operation(op);
+
+ io_service().post(boost::bind(&openssl_operation<Stream>::start, op));
+ }
+
+ // Peek at the incoming data on the stream.
+ template <typename Stream, typename Mutable_Buffers, typename Error_Handler>
+ std::size_t peek(impl_type& impl, Stream& next_layer,
+ const Mutable_Buffers& buffers, Error_Handler error_handler)
+ {
+ asio::error e;
+ error_handler(e);
+ return 0;
+ }
+
+ // Determine the amount of data that may be read without blocking.
+ template <typename Stream, typename Error_Handler>
+ std::size_t in_avail(impl_type& impl, Stream& next_layer,
+ Error_Handler error_handler)
+ {
+ asio::error e;
+ error_handler(e);
+ return 0;
+ }
+
+private:
+ typedef asio::detail::mutex mutex_type;
+
+ template<typename Mutex>
+ struct ssl_wrap
+ {
+ static Mutex ssl_mutex_;
+
+ static int SSL_accept(SSL *ssl)
+ {
+ typename Mutex::scoped_lock lock(ssl_mutex_);
+ return ::SSL_accept(ssl);
+ }
+
+ static int SSL_connect(SSL *ssl)
+ {
+ typename Mutex::scoped_lock lock(ssl_mutex_);
+ return ::SSL_connect(ssl);
+ }
+
+ static int SSL_shutdown(SSL *ssl)
+ {
+ typename Mutex::scoped_lock lock(ssl_mutex_);
+ return ::SSL_shutdown(ssl);
+ }
+ };
+};
+
+template<typename Mutex>
+Mutex openssl_stream_service::ssl_wrap<Mutex>::ssl_mutex_;
+
+} // namespace detail
+} // namespace ssl
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_SSL_DETAIL_OPENSSL_STREAM_SERVICE_HPP
diff --git a/library/include/libtorrent/asio/ssl/detail/openssl_types.hpp b/library/include/libtorrent/asio/ssl/detail/openssl_types.hpp
new file mode 100755
index 000000000..d193b3a0c
--- /dev/null
+++ b/library/include/libtorrent/asio/ssl/detail/openssl_types.hpp
@@ -0,0 +1,29 @@
+//
+// openssl_types.hpp
+// ~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2005 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_SSL_DETAIL_OPENSSL_TYPES_HPP
+#define ASIO_SSL_DETAIL_OPENSSL_TYPES_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/detail/push_options.hpp"
+#include <openssl/conf.h>
+#include <openssl/ssl.h>
+#include <openssl/engine.h>
+#include <openssl/err.h>
+#include "asio/detail/pop_options.hpp"
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_SSL_DETAIL_OPENSSL_TYPES_HPP
diff --git a/library/include/libtorrent/asio/ssl/stream.hpp b/library/include/libtorrent/asio/ssl/stream.hpp
new file mode 100644
index 000000000..451b02e32
--- /dev/null
+++ b/library/include/libtorrent/asio/ssl/stream.hpp
@@ -0,0 +1,509 @@
+//
+// stream.hpp
+// ~~~~~~~~~~
+//
+// Copyright (c) 2005 Voipster / Indrek dot Juhani at voipster dot com
+// Copyright (c) 2005 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_SSL_STREAM_HPP
+#define ASIO_SSL_STREAM_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/detail/push_options.hpp"
+#include <cstddef>
+#include <boost/config.hpp>
+#include <boost/noncopyable.hpp>
+#include <boost/type_traits.hpp>
+#include "asio/detail/pop_options.hpp"
+
+#include "asio/error.hpp"
+#include "asio/error_handler.hpp"
+#include "asio/ssl/basic_context.hpp"
+#include "asio/ssl/stream_base.hpp"
+#include "asio/ssl/stream_service.hpp"
+
+namespace asio {
+namespace ssl {
+
+/// Provides stream-oriented functionality using SSL.
+/**
+ * The stream class template provides asynchronous and blocking stream-oriented
+ * functionality using SSL.
+ *
+ * @par Thread Safety:
+ * @e Distinct @e objects: Safe.@n
+ * @e Shared @e objects: Unsafe.
+ *
+ * @par Example:
+ * To use the SSL stream template with a stream_socket, you would write:
+ * @code
+ * asio::io_service io_service;
+ * asio::ssl::context context(io_service, asio::ssl::context::sslv23);
+ * asio::ssl::stream<asio::stream_socket> sock(io_service, context);
+ * @endcode
+ *
+ * @par Concepts:
+ * Async_Object, Async_Read_Stream, Async_Write_Stream, Error_Source, Stream,
+ * Sync_Read_Stream, Sync_Write_Stream.
+ */
+template <typename Stream, typename Service = stream_service>
+class stream
+ : public stream_base,
+ private boost::noncopyable
+{
+public:
+ /// The type of the next layer.
+ typedef typename boost::remove_reference<Stream>::type next_layer_type;
+
+ /// The type of the lowest layer.
+ typedef typename next_layer_type::lowest_layer_type lowest_layer_type;
+
+ /// The type used for reporting errors.
+ typedef typename next_layer_type::error_type error_type;
+
+ /// The type of the service that will be used to provide stream operations.
+ typedef Service service_type;
+
+ /// The native implementation type of the stream.
+ typedef typename service_type::impl_type impl_type;
+
+ /// Construct a stream.
+ /**
+ * This constructor creates a stream and initialises the underlying stream
+ * object.
+ *
+ * @param arg The argument to be passed to initialise the underlying stream.
+ *
+ * @param context The SSL context to be used for the stream.
+ */
+ template <typename Arg, typename Context_Service>
+ explicit stream(Arg& arg, basic_context<Context_Service>& context)
+ : next_layer_(arg),
+ service_(asio::use_service<Service>(next_layer_.io_service())),
+ impl_(service_.null())
+ {
+ service_.create(impl_, next_layer_, context);
+ }
+
+ /// Destructor.
+ ~stream()
+ {
+ service_.destroy(impl_, next_layer_);
+ }
+
+ /// Get the io_service associated with the object.
+ /**
+ * This function may be used to obtain the io_service object that the stream
+ * uses to dispatch handlers for asynchronous operations.
+ *
+ * @return A reference to the io_service object that stream will use to
+ * dispatch handlers. Ownership is not transferred to the caller.
+ */
+ asio::io_service& io_service()
+ {
+ return next_layer_.io_service();
+ }
+
+ /// Get a reference to the next layer.
+ /**
+ * This function returns a reference to the next layer in a stack of stream
+ * layers.
+ *
+ * @return A reference to the next layer in the stack of stream layers.
+ * Ownership is not transferred to the caller.
+ */
+ next_layer_type& next_layer()
+ {
+ return next_layer_;
+ }
+
+ /// Get a reference to the lowest layer.
+ /**
+ * This function returns a reference to the lowest layer in a stack of
+ * stream layers.
+ *
+ * @return A reference to the lowest layer in the stack of stream layers.
+ * Ownership is not transferred to the caller.
+ */
+ lowest_layer_type& lowest_layer()
+ {
+ return next_layer_.lowest_layer();
+ }
+
+ /// Get the underlying implementation in the native type.
+ /**
+ * This function may be used to obtain the underlying implementation of the
+ * context. This is intended to allow access to stream functionality that is
+ * not otherwise provided.
+ */
+ impl_type impl()
+ {
+ return impl_;
+ }
+
+ /// Perform SSL handshaking.
+ /**
+ * This function is used to perform SSL handshaking on the stream. The
+ * function call will block until handshaking is complete or an error occurs.
+ *
+ * @param type The type of handshaking to be performed, i.e. as a client or as
+ * a server.
+ *
+ * @throws asio::error Thrown on failure.
+ */
+ void handshake(handshake_type type)
+ {
+ service_.handshake(impl_, next_layer_, type, throw_error());
+ }
+
+ /// Perform SSL handshaking.
+ /**
+ * This function is used to perform SSL handshaking on the stream. The
+ * function call will block until handshaking is complete or an error occurs.
+ *
+ * @param type The type of handshaking to be performed, i.e. as a client or as
+ * a server.
+ *
+ * @param error_handler A handler to be called when the operation completes,
+ * to indicate whether or not an error has occurred. Copies will be made of
+ * the handler as required. The function signature of the handler must be:
+ * @code void error_handler(
+ * const asio::error& error // Result of operation
+ * ); @endcode
+ */
+ template <typename Error_Handler>
+ void handshake(handshake_type type, Error_Handler error_handler)
+ {
+ service_.handshake(impl_, next_layer_, type, error_handler);
+ }
+
+ /// Start an asynchronous SSL handshake.
+ /**
+ * This function is used to asynchronously perform an SSL handshake on the
+ * stream. This function call always returns immediately.
+ *
+ * @param type The type of handshaking to be performed, i.e. as a client or as
+ * a server.
+ *
+ * @param handler The handler to be called when the handshake operation
+ * completes. Copies will be made of the handler as required. The equivalent
+ * function signature of the handler must be:
+ * @code void handler(
+ * const asio::error& error, // Result of operation
+ * ); @endcode
+ */
+ template <typename Handler>
+ void async_handshake(handshake_type type, Handler handler)
+ {
+ service_.async_handshake(impl_, next_layer_, type, handler);
+ }
+
+ /// Shut down SSL on the stream.
+ /**
+ * This function is used to shut down SSL on the stream. The function call
+ * will block until SSL has been shut down or an error occurs.
+ *
+ * @throws asio::error Thrown on failure.
+ */
+ void shutdown()
+ {
+ service_.shutdown(impl_, next_layer_, throw_error());
+ }
+
+ /// Shut down SSL on the stream.
+ /**
+ * This function is used to shut down SSL on the stream. The function call
+ * will block until SSL has been shut down or an error occurs.
+ *
+ * @param error_handler A handler to be called when the operation completes,
+ * to indicate whether or not an error has occurred. Copies will be made of
+ * the handler as required. The function signature of the handler must be:
+ * @code void error_handler(
+ * const asio::error& error // Result of operation
+ * ); @endcode
+ */
+ template <typename Error_Handler>
+ void shutdown(Error_Handler error_handler)
+ {
+ service_.shutdown(impl_, next_layer_, error_handler);
+ }
+
+ /// Asynchronously shut down SSL on the stream.
+ /**
+ * This function is used to asynchronously shut down SSL on the stream. This
+ * function call always returns immediately.
+ *
+ * @param handler The handler to be called when the handshake operation
+ * completes. Copies will be made of the handler as required. The equivalent
+ * function signature of the handler must be:
+ * @code void handler(
+ * const asio::error& error, // Result of operation
+ * ); @endcode
+ */
+ template <typename Handler>
+ void async_shutdown(Handler handler)
+ {
+ service_.async_shutdown(impl_, next_layer_, handler);
+ }
+
+ /// Write some data to the stream.
+ /**
+ * This function is used to write data on the stream. The function call will
+ * block until one or more bytes of data has been written successfully, or
+ * until an error occurs.
+ *
+ * @param buffers The data to be written.
+ *
+ * @returns The number of bytes written.
+ *
+ * @throws asio::error Thrown on failure.
+ *
+ * @note The write_some operation may not transmit all of the data to the
+ * peer. Consider using the @ref write function if you need to ensure that all
+ * data is written before the blocking operation completes.
+ */
+ template <typename Const_Buffers>
+ std::size_t write_some(const Const_Buffers& buffers)
+ {
+ return service_.write_some(impl_, next_layer_, buffers, throw_error());
+ }
+
+ /// Write some data to the stream.
+ /**
+ * This function is used to write data on the stream. The function call will
+ * block until one or more bytes of data has been written successfully, or
+ * until an error occurs.
+ *
+ * @param buffers The data to be written to the stream.
+ *
+ * @param error_handler A handler to be called when the operation completes,
+ * to indicate whether or not an error has occurred. Copies will be made of
+ * the handler as required. The function signature of the handler must be:
+ * @code void error_handler(
+ * const asio::error& error // Result of operation.
+ * ); @endcode
+ *
+ * @returns The number of bytes written. Returns 0 if an error occurred and
+ * the error handler did not throw an exception.
+ *
+ * @note The write_some operation may not transmit all of the data to the
+ * peer. Consider using the @ref write function if you need to ensure that all
+ * data is written before the blocking operation completes.
+ */
+ template <typename Const_Buffers, typename Error_Handler>
+ std::size_t write_some(const Const_Buffers& buffers,
+ Error_Handler error_handler)
+ {
+ return service_.write_some(impl_, next_layer_, buffers, error_handler);
+ }
+
+ /// Start an asynchronous write.
+ /**
+ * This function is used to asynchronously write one or more bytes of data to
+ * the stream. The function call always returns immediately.
+ *
+ * @param buffers The data to be written to the stream. Although the buffers
+ * object may be copied as necessary, ownership of the underlying buffers is
+ * retained by the caller, which must guarantee that they remain valid until
+ * the handler is called.
+ *
+ * @param handler The handler to be called when the write operation completes.
+ * Copies will be made of the handler as required. The equivalent function
+ * signature of the handler must be:
+ * @code void handler(
+ * const asio::error& error, // Result of operation.
+ * std::size_t bytes_transferred // Number of bytes written.
+ * ); @endcode
+ *
+ * @note The async_write_some operation may not transmit all of the data to
+ * the peer. Consider using the @ref async_write function if you need to
+ * ensure that all data is written before the blocking operation completes.
+ */
+ template <typename Const_Buffers, typename Handler>
+ void async_write_some(const Const_Buffers& buffers, Handler handler)
+ {
+ service_.async_write_some(impl_, next_layer_, buffers, handler);
+ }
+
+ /// Read some data from the stream.
+ /**
+ * This function is used to read data from the stream. The function call will
+ * block until one or more bytes of data has been read successfully, or until
+ * an error occurs.
+ *
+ * @param buffers The buffers into which the data will be read.
+ *
+ * @returns The number of bytes read.
+ *
+ * @throws asio::error Thrown on failure.
+ *
+ * @note The read_some operation may not read all of the requested number of
+ * bytes. Consider using the @ref read function if you need to ensure that the
+ * requested amount of data is read before the blocking operation completes.
+ */
+ template <typename Mutable_Buffers>
+ std::size_t read_some(const Mutable_Buffers& buffers)
+ {
+ return service_.read_some(impl_, next_layer_, buffers, throw_error());
+ }
+
+ /// Read some data from the stream.
+ /**
+ * This function is used to read data from the stream. The function call will
+ * block until one or more bytes of data has been read successfully, or until
+ * an error occurs.
+ *
+ * @param buffers The buffers into which the data will be read.
+ *
+ * @param error_handler A handler to be called when the operation completes,
+ * to indicate whether or not an error has occurred. Copies will be made of
+ * the handler as required. The function signature of the handler must be:
+ * @code void error_handler(
+ * const asio::error& error // Result of operation.
+ * ); @endcode
+ *
+ * @returns The number of bytes read. Returns 0 if an error occurred and the
+ * error handler did not throw an exception.
+ *
+ * @note The read_some operation may not read all of the requested number of
+ * bytes. Consider using the @ref read function if you need to ensure that the
+ * requested amount of data is read before the blocking operation completes.
+ */
+ template <typename Mutable_Buffers, typename Error_Handler>
+ std::size_t read_some(const Mutable_Buffers& buffers,
+ Error_Handler error_handler)
+ {
+ return service_.read_some(impl_, next_layer_, buffers, error_handler);
+ }
+
+ /// Start an asynchronous read.
+ /**
+ * This function is used to asynchronously read one or more bytes of data from
+ * the stream. The function call always returns immediately.
+ *
+ * @param buffers The buffers into which the data will be read. Although the
+ * buffers object may be copied as necessary, ownership of the underlying
+ * buffers is retained by the caller, which must guarantee that they remain
+ * valid until the handler is called.
+ *
+ * @param handler The handler to be called when the read operation completes.
+ * Copies will be made of the handler as required. The equivalent function
+ * signature of the handler must be:
+ * @code void handler(
+ * const asio::error& error, // Result of operation.
+ * std::size_t bytes_transferred // Number of bytes read.
+ * ); @endcode
+ *
+ * @note The async_read_some operation may not read all of the requested
+ * number of bytes. Consider using the @ref async_read function if you need to
+ * ensure that the requested amount of data is read before the asynchronous
+ * operation completes.
+ */
+ template <typename Mutable_Buffers, typename Handler>
+ void async_read_some(const Mutable_Buffers& buffers, Handler handler)
+ {
+ service_.async_read_some(impl_, next_layer_, buffers, handler);
+ }
+
+ /// Peek at the incoming data on the stream.
+ /**
+ * This function is used to peek at the incoming data on the stream, without
+ * removing it from the input queue. The function call will block until data
+ * has been read successfully or an error occurs.
+ *
+ * @param buffers The buffers into which the data will be read.
+ *
+ * @returns The number of bytes read.
+ *
+ * @throws asio::error Thrown on failure.
+ */
+ template <typename Mutable_Buffers>
+ std::size_t peek(const Mutable_Buffers& buffers)
+ {
+ return service_.peek(impl_, next_layer_, buffers, throw_error());
+ }
+
+ /// Peek at the incoming data on the stream.
+ /**
+ * This function is used to peek at the incoming data on the stream, withoutxi
+ * removing it from the input queue. The function call will block until data
+ * has been read successfully or an error occurs.
+ *
+ * @param buffers The buffers into which the data will be read.
+ *
+ * @param error_handler A handler to be called when the operation completes,
+ * to indicate whether or not an error has occurred. Copies will be made of
+ * the handler as required. The function signature of the handler must be:
+ * @code void error_handler(
+ * const asio::error& error // Result of operation.
+ * ); @endcode
+ *
+ * @returns The number of bytes read. Returns 0 if an error occurred and the
+ * error handler did not throw an exception.
+ */
+ template <typename Mutable_Buffers, typename Error_Handler>
+ std::size_t peek(const Mutable_Buffers& buffers, Error_Handler error_handler)
+ {
+ return service_.peek(impl_, next_layer_, buffers, error_handler);
+ }
+
+ /// Determine the amount of data that may be read without blocking.
+ /**
+ * This function is used to determine the amount of data, in bytes, that may
+ * be read from the stream without blocking.
+ *
+ * @returns The number of bytes of data that can be read without blocking.
+ *
+ * @throws asio::error Thrown on failure.
+ */
+ std::size_t in_avail()
+ {
+ return service_.in_avail(impl_, next_layer_, throw_error());
+ }
+
+ /// Determine the amount of data that may be read without blocking.
+ /**
+ * This function is used to determine the amount of data, in bytes, that may
+ * be read from the stream without blocking.
+ *
+ * @param error_handler A handler to be called when the operation completes,
+ * to indicate whether or not an error has occurred. Copies will be made of
+ * the handler as required. The function signature of the handler must be:
+ * @code void error_handler(
+ * const asio::error& error // Result of operation
+ * ); @endcode
+ *
+ * @returns The number of bytes of data that can be read without blocking.
+ */
+ template <typename Error_Handler>
+ std::size_t in_avail(Error_Handler error_handler)
+ {
+ return service_.in_avail(impl_, next_layer_, error_handler);
+ }
+
+private:
+ /// The next layer.
+ Stream next_layer_;
+
+ /// The backend service implementation.
+ service_type& service_;
+
+ /// The underlying native implementation.
+ impl_type impl_;
+};
+
+} // namespace ssl
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_SSL_STREAM_HPP
diff --git a/library/include/libtorrent/asio/ssl/stream_base.hpp b/library/include/libtorrent/asio/ssl/stream_base.hpp
new file mode 100755
index 000000000..89c4b65a9
--- /dev/null
+++ b/library/include/libtorrent/asio/ssl/stream_base.hpp
@@ -0,0 +1,60 @@
+//
+// stream_base.hpp
+// ~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2005 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_SSL_STREAM_BASE_HPP
+#define ASIO_SSL_STREAM_BASE_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/detail/push_options.hpp"
+#include <boost/detail/workaround.hpp>
+#include "asio/detail/pop_options.hpp"
+
+namespace asio {
+namespace ssl {
+
+/// The stream_base class is used as a base for the asio::ssl::stream
+/// class template so that we have a common place to define various enums.
+class stream_base
+{
+public:
+ /// Different handshake types.
+ enum handshake_type
+ {
+ /// Perform handshaking as a client.
+ client,
+
+ /// Perform handshaking as a server.
+ server
+ };
+
+protected:
+ /// Protected destructor to prevent deletion through this type.
+ ~stream_base()
+ {
+ }
+
+#if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564))
+private:
+ // Workaround to enable the empty base optimisation with Borland C++.
+ char dummy_;
+#endif
+};
+
+} // namespace ssl
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_SSL_STREAM_BASE_HPP
diff --git a/library/include/libtorrent/asio/ssl/stream_service.hpp b/library/include/libtorrent/asio/ssl/stream_service.hpp
new file mode 100644
index 000000000..5ad5165cc
--- /dev/null
+++ b/library/include/libtorrent/asio/ssl/stream_service.hpp
@@ -0,0 +1,173 @@
+//
+// stream_service.hpp
+// ~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2005 Voipster / Indrek dot Juhani at voipster dot com
+// Copyright (c) 2005 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_SSL_STREAM_SERVICE_HPP
+#define ASIO_SSL_STREAM_SERVICE_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/detail/push_options.hpp"
+#include <cstddef>
+#include <boost/config.hpp>
+#include <boost/noncopyable.hpp>
+#include "asio/detail/pop_options.hpp"
+
+#include "asio/io_service.hpp"
+#include "asio/ssl/basic_context.hpp"
+#include "asio/ssl/stream_base.hpp"
+#include "asio/ssl/detail/openssl_stream_service.hpp"
+
+namespace asio {
+namespace ssl {
+
+/// Default service implementation for an SSL stream.
+class stream_service
+ : public asio::io_service::service
+{
+private:
+ // The type of the platform-specific implementation.
+ typedef detail::openssl_stream_service service_impl_type;
+
+public:
+ /// The type of a stream implementation.
+#if defined(GENERATING_DOCUMENTATION)
+ typedef implementation_defined impl_type;
+#else
+ typedef service_impl_type::impl_type impl_type;
+#endif
+
+ /// Construct a new stream service for the specified io_service.
+ explicit stream_service(asio::io_service& io_service)
+ : asio::io_service::service(io_service),
+ service_impl_(asio::use_service<service_impl_type>(io_service))
+ {
+ }
+
+ /// Destroy all user-defined handler objects owned by the service.
+ void shutdown_service()
+ {
+ }
+
+ /// Return a null stream implementation.
+ impl_type null() const
+ {
+ return service_impl_.null();
+ }
+
+ /// Create a new stream implementation.
+ template <typename Stream, typename Context_Service>
+ void create(impl_type& impl, Stream& next_layer,
+ basic_context<Context_Service>& context)
+ {
+ service_impl_.create(impl, next_layer, context);
+ }
+
+ /// Destroy a stream implementation.
+ template <typename Stream>
+ void destroy(impl_type& impl, Stream& next_layer)
+ {
+ service_impl_.destroy(impl, next_layer);
+ }
+
+ /// Perform SSL handshaking.
+ template <typename Stream, typename Error_Handler>
+ void handshake(impl_type& impl, Stream& next_layer,
+ stream_base::handshake_type type, Error_Handler error_handler)
+ {
+ service_impl_.handshake(impl, next_layer, type, error_handler);
+ }
+
+ /// Start an asynchronous SSL handshake.
+ template <typename Stream, typename Handler>
+ void async_handshake(impl_type& impl, Stream& next_layer,
+ stream_base::handshake_type type, Handler handler)
+ {
+ service_impl_.async_handshake(impl, next_layer, type, handler);
+ }
+
+ /// Shut down SSL on the stream.
+ template <typename Stream, typename Error_Handler>
+ void shutdown(impl_type& impl, Stream& next_layer,
+ Error_Handler error_handler)
+ {
+ service_impl_.shutdown(impl, next_layer, error_handler);
+ }
+
+ /// Asynchronously shut down SSL on the stream.
+ template <typename Stream, typename Handler>
+ void async_shutdown(impl_type& impl, Stream& next_layer, Handler handler)
+ {
+ service_impl_.async_shutdown(impl, next_layer, handler);
+ }
+
+ /// Write some data to the stream.
+ template <typename Stream, typename Const_Buffers, typename Error_Handler>
+ std::size_t write_some(impl_type& impl, Stream& next_layer,
+ const Const_Buffers& buffers, Error_Handler error_handler)
+ {
+ return service_impl_.write_some(impl, next_layer, buffers, error_handler);
+ }
+
+ /// Start an asynchronous write.
+ template <typename Stream, typename Const_Buffers, typename Handler>
+ void async_write_some(impl_type& impl, Stream& next_layer,
+ const Const_Buffers& buffers, Handler handler)
+ {
+ service_impl_.async_write_some(impl, next_layer, buffers, handler);
+ }
+
+ /// Read some data from the stream.
+ template <typename Stream, typename Mutable_Buffers, typename Error_Handler>
+ std::size_t read_some(impl_type& impl, Stream& next_layer,
+ const Mutable_Buffers& buffers, Error_Handler error_handler)
+ {
+ return service_impl_.read_some(impl, next_layer, buffers, error_handler);
+ }
+
+ /// Start an asynchronous read.
+ template <typename Stream, typename Mutable_Buffers, typename Handler>
+ void async_read_some(impl_type& impl, Stream& next_layer,
+ const Mutable_Buffers& buffers, Handler handler)
+ {
+ service_impl_.async_read_some(impl, next_layer, buffers, handler);
+ }
+
+ /// Peek at the incoming data on the stream.
+ template <typename Stream, typename Mutable_Buffers, typename Error_Handler>
+ std::size_t peek(impl_type& impl, Stream& next_layer,
+ const Mutable_Buffers& buffers, Error_Handler error_handler)
+ {
+ return service_impl_.peek(impl, next_layer, buffers, error_handler);
+ }
+
+ /// Determine the amount of data that may be read without blocking.
+ template <typename Stream, typename Error_Handler>
+ std::size_t in_avail(impl_type& impl, Stream& next_layer,
+ Error_Handler error_handler)
+ {
+ return service_impl_.in_avail(impl, next_layer, error_handler);
+ }
+
+private:
+ // The service that provides the platform-specific implementation.
+ service_impl_type& service_impl_;
+};
+
+} // namespace ssl
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_SSL_STREAM_SERVICE_HPP
diff --git a/library/include/libtorrent/asio/strand.hpp b/library/include/libtorrent/asio/strand.hpp
new file mode 100644
index 000000000..e378bfd32
--- /dev/null
+++ b/library/include/libtorrent/asio/strand.hpp
@@ -0,0 +1,166 @@
+//
+// strand.hpp
+// ~~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_STRAND_HPP
+#define ASIO_STRAND_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/io_service.hpp"
+#include "asio/detail/strand_service.hpp"
+#include "asio/detail/wrapped_handler.hpp"
+
+namespace asio {
+
+/// Provides serialised handler execution.
+/**
+ * The io_service::strand class provides the ability to post and dispatch
+ * handlers with the guarantee that none of those handlers will execute
+ * concurrently.
+ *
+ * @par Thread Safety:
+ * @e Distinct @e objects: Safe.@n
+ * @e Shared @e objects: Safe.
+ *
+ * @par Concepts:
+ * Dispatcher.
+ */
+class io_service::strand
+{
+public:
+ /// Constructor.
+ /**
+ * Constructs the strand.
+ *
+ * @param io_service The io_service object that the strand will use to
+ * dispatch handlers that are ready to be run.
+ */
+ explicit strand(asio::io_service& io_service)
+ : service_(asio::use_service<
+ asio::detail::strand_service>(io_service))
+ {
+ service_.construct(impl_);
+ }
+
+ /// Destructor.
+ ~strand()
+ {
+ service_.destroy(impl_);
+ }
+
+ /// Get the io_service associated with the strand.
+ /**
+ * This function may be used to obtain the io_service object that the strand
+ * uses to dispatch handlers for asynchronous operations.
+ *
+ * @return A reference to the io_service object that the strand will use to
+ * dispatch handlers. Ownership is not transferred to the caller.
+ */
+ asio::io_service& io_service()
+ {
+ return service_.io_service();
+ }
+
+ /// Request the strand to invoke the given handler.
+ /**
+ * This function is used to ask the strand to execute the given handler.
+ *
+ * The strand object guarantees that handlers posted or dispatched through
+ * the strand will not be executed concurrently. The handler may be executed
+ * inside this function if the guarantee can be met. If this function is
+ * called from within a handler that was posted or dispatched through the same
+ * strand, then the new handler will be executed immediately.
+ *
+ * The strand's guarantee is in addition to the guarantee provided by the
+ * underlying io_service. The io_service guarantees that the handler will only
+ * be called in a thread in which the io_service's run member function is
+ * currently being invoked.
+ *
+ * @param handler The handler to be called. The strand will make a copy of the
+ * handler object as required. The function signature of the handler must be:
+ * @code void handler(); @endcode
+ */
+ template <typename Handler>
+ void dispatch(Handler handler)
+ {
+ service_.dispatch(impl_, handler);
+ }
+
+ /// Request the strand to invoke the given handler and return
+ /// immediately.
+ /**
+ * This function is used to ask the strand to execute the given handler, but
+ * without allowing the strand to call the handler from inside this function.
+ *
+ * The strand object guarantees that handlers posted or dispatched through
+ * the strand will not be executed concurrently. The strand's guarantee is in
+ * addition to the guarantee provided by the underlying io_service. The
+ * io_service guarantees that the handler will only be called in a thread in
+ * which the io_service's run member function is currently being invoked.
+ *
+ * @param handler The handler to be called. The strand will make a copy of the
+ * handler object as required. The function signature of the handler must be:
+ * @code void handler(); @endcode
+ */
+ template <typename Handler>
+ void post(Handler handler)
+ {
+ service_.post(impl_, handler);
+ }
+
+ /// Create a new handler that automatically dispatches the wrapped handler
+ /// on the strand.
+ /**
+ * This function is used to create a new handler function object that, when
+ * invoked, will automatically pass the wrapped handler to the strand's
+ * dispatch function.
+ *
+ * @param handler The handler to be wrapped. The strand will make a copy of
+ * the handler object as required. The function signature of the handler must
+ * be: @code void handler(A1 a1, ... An an); @endcode
+ *
+ * @return A function object that, when invoked, passes the wrapped handler to
+ * the strand's dispatch function. Given a function object with the signature:
+ * @code R f(A1 a1, ... An an); @endcode
+ * If this function object is passed to the wrap function like so:
+ * @code strand.wrap(f); @endcode
+ * then the return value is a function object with the signature
+ * @code void g(A1 a1, ... An an); @endcode
+ * that, when invoked, executes code equivalent to:
+ * @code strand.dispatch(boost::bind(f, a1, ... an)); @endcode
+ */
+ template <typename Handler>
+#if defined(GENERATING_DOCUMENTATION)
+ unspecified
+#else
+ detail::wrapped_handler<strand, Handler>
+#endif
+ wrap(Handler handler)
+ {
+ return detail::wrapped_handler<io_service::strand, Handler>(*this, handler);
+ }
+
+private:
+ asio::detail::strand_service& service_;
+ asio::detail::strand_service::implementation_type impl_;
+};
+
+/// Typedef for backwards compatibility.
+typedef asio::io_service::strand strand;
+
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_STRAND_HPP
diff --git a/library/include/libtorrent/asio/stream_socket_service.hpp b/library/include/libtorrent/asio/stream_socket_service.hpp
new file mode 100644
index 000000000..bb604b764
--- /dev/null
+++ b/library/include/libtorrent/asio/stream_socket_service.hpp
@@ -0,0 +1,256 @@
+//
+// stream_socket_service.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_STREAM_SOCKET_SERVICE_HPP
+#define ASIO_STREAM_SOCKET_SERVICE_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/detail/push_options.hpp"
+#include <cstddef>
+#include <boost/config.hpp>
+#include "asio/detail/pop_options.hpp"
+
+#include "asio/io_service.hpp"
+#include "asio/detail/epoll_reactor.hpp"
+#include "asio/detail/kqueue_reactor.hpp"
+#include "asio/detail/select_reactor.hpp"
+#include "asio/detail/win_iocp_socket_service.hpp"
+#include "asio/detail/reactive_socket_service.hpp"
+
+namespace asio {
+
+/// Default service implementation for a stream socket.
+template <typename Protocol>
+class stream_socket_service
+ : public asio::io_service::service
+{
+public:
+ /// The protocol type.
+ typedef Protocol protocol_type;
+
+ /// The endpoint type.
+ typedef typename Protocol::endpoint endpoint_type;
+
+private:
+ // The type of the platform-specific implementation.
+#if defined(ASIO_HAS_IOCP)
+ typedef detail::win_iocp_socket_service<Protocol> service_impl_type;
+#elif defined(ASIO_HAS_EPOLL)
+ typedef detail::reactive_socket_service<
+ Protocol, detail::epoll_reactor<false> > service_impl_type;
+#elif defined(ASIO_HAS_KQUEUE)
+ typedef detail::reactive_socket_service<
+ Protocol, detail::kqueue_reactor<false> > service_impl_type;
+#else
+ typedef detail::reactive_socket_service<
+ Protocol, detail::select_reactor<false> > service_impl_type;
+#endif
+
+public:
+ /// The type of a stream socket implementation.
+#if defined(GENERATING_DOCUMENTATION)
+ typedef implementation_defined implementation_type;
+#else
+ typedef typename service_impl_type::implementation_type implementation_type;
+#endif
+
+ /// The native socket type.
+#if defined(GENERATING_DOCUMENTATION)
+ typedef implementation_defined native_type;
+#else
+ typedef typename service_impl_type::native_type native_type;
+#endif
+
+ /// Construct a new stream socket service for the specified io_service.
+ explicit stream_socket_service(asio::io_service& io_service)
+ : asio::io_service::service(io_service),
+ service_impl_(asio::use_service<service_impl_type>(io_service))
+ {
+ }
+
+ /// Destroy all user-defined handler objects owned by the service.
+ void shutdown_service()
+ {
+ }
+
+ /// Construct a new stream socket implementation.
+ void construct(implementation_type& impl)
+ {
+ service_impl_.construct(impl);
+ }
+
+ /// Destroy a stream socket implementation.
+ void destroy(implementation_type& impl)
+ {
+ service_impl_.destroy(impl);
+ }
+
+ /// Open a stream socket.
+ template <typename Error_Handler>
+ void open(implementation_type& impl, const protocol_type& protocol,
+ Error_Handler error_handler)
+ {
+ if (protocol.type() == SOCK_STREAM)
+ service_impl_.open(impl, protocol, error_handler);
+ else
+ error_handler(asio::error(asio::error::invalid_argument));
+ }
+
+ /// Assign an existing native socket to a stream socket.
+ template <typename Error_Handler>
+ void assign(implementation_type& impl, const protocol_type& protocol,
+ const native_type& native_socket, Error_Handler error_handler)
+ {
+ service_impl_.assign(impl, protocol, native_socket, error_handler);
+ }
+
+ /// Close a stream socket implementation.
+ template <typename Error_Handler>
+ void close(implementation_type& impl, Error_Handler error_handler)
+ {
+ service_impl_.close(impl, error_handler);
+ }
+
+ /// Get the native socket implementation.
+ native_type native(implementation_type& impl)
+ {
+ return service_impl_.native(impl);
+ }
+
+ /// Cancel all asynchronous operations associated with the socket.
+ template <typename Error_Handler>
+ void cancel(implementation_type& impl, Error_Handler error_handler)
+ {
+ service_impl_.cancel(impl, error_handler);
+ }
+
+ /// Bind the stream socket to the specified local endpoint.
+ template <typename Error_Handler>
+ void bind(implementation_type& impl, const endpoint_type& endpoint,
+ Error_Handler error_handler)
+ {
+ service_impl_.bind(impl, endpoint, error_handler);
+ }
+
+ /// Connect the stream socket to the specified endpoint.
+ template <typename Error_Handler>
+ void connect(implementation_type& impl, const endpoint_type& peer_endpoint,
+ Error_Handler error_handler)
+ {
+ service_impl_.connect(impl, peer_endpoint, error_handler);
+ }
+
+ /// Start an asynchronous connect.
+ template <typename Handler>
+ void async_connect(implementation_type& impl,
+ const endpoint_type& peer_endpoint, Handler handler)
+ {
+ service_impl_.async_connect(impl, peer_endpoint, handler);
+ }
+
+ /// Set a socket option.
+ template <typename Option, typename Error_Handler>
+ void set_option(implementation_type& impl, const Option& option,
+ Error_Handler error_handler)
+ {
+ service_impl_.set_option(impl, option, error_handler);
+ }
+
+ /// Get a socket option.
+ template <typename Option, typename Error_Handler>
+ void get_option(const implementation_type& impl, Option& option,
+ Error_Handler error_handler) const
+ {
+ service_impl_.get_option(impl, option, error_handler);
+ }
+
+ /// Perform an IO control command on the socket.
+ template <typename IO_Control_Command, typename Error_Handler>
+ void io_control(implementation_type& impl, IO_Control_Command& command,
+ Error_Handler error_handler)
+ {
+ service_impl_.io_control(impl, command, error_handler);
+ }
+
+ /// Get the local endpoint.
+ template <typename Error_Handler>
+ endpoint_type local_endpoint(const implementation_type& impl,
+ Error_Handler error_handler) const
+ {
+ endpoint_type endpoint;
+ service_impl_.get_local_endpoint(impl, endpoint, error_handler);
+ return endpoint;
+ }
+
+ /// Get the remote endpoint.
+ template <typename Error_Handler>
+ endpoint_type remote_endpoint(const implementation_type& impl,
+ Error_Handler error_handler) const
+ {
+ endpoint_type endpoint;
+ service_impl_.get_remote_endpoint(impl, endpoint, error_handler);
+ return endpoint;
+ }
+
+ /// Disable sends or receives on the socket.
+ template <typename Error_Handler>
+ void shutdown(implementation_type& impl, socket_base::shutdown_type what,
+ Error_Handler error_handler)
+ {
+ service_impl_.shutdown(impl, what, error_handler);
+ }
+
+ /// Send the given data to the peer.
+ template <typename Const_Buffers, typename Error_Handler>
+ std::size_t send(implementation_type& impl, const Const_Buffers& buffers,
+ socket_base::message_flags flags, Error_Handler error_handler)
+ {
+ return service_impl_.send(impl, buffers, flags, error_handler);
+ }
+
+ /// Start an asynchronous send.
+ template <typename Const_Buffers, typename Handler>
+ void async_send(implementation_type& impl, const Const_Buffers& buffers,
+ socket_base::message_flags flags, Handler handler)
+ {
+ service_impl_.async_send(impl, buffers, flags, handler);
+ }
+
+ /// Receive some data from the peer.
+ template <typename Mutable_Buffers, typename Error_Handler>
+ std::size_t receive(implementation_type& impl, const Mutable_Buffers& buffers,
+ socket_base::message_flags flags, Error_Handler error_handler)
+ {
+ return service_impl_.receive(impl, buffers, flags, error_handler);
+ }
+
+ /// Start an asynchronous receive.
+ template <typename Mutable_Buffers, typename Handler>
+ void async_receive(implementation_type& impl, const Mutable_Buffers& buffers,
+ socket_base::message_flags flags, Handler handler)
+ {
+ service_impl_.async_receive(impl, buffers, flags, handler);
+ }
+
+private:
+ // The service that provides the platform-specific implementation.
+ service_impl_type& service_impl_;
+};
+
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_STREAM_SOCKET_SERVICE_HPP
diff --git a/library/include/libtorrent/asio/streambuf.hpp b/library/include/libtorrent/asio/streambuf.hpp
new file mode 100644
index 000000000..d20c957da
--- /dev/null
+++ b/library/include/libtorrent/asio/streambuf.hpp
@@ -0,0 +1,31 @@
+//
+// streambuf.hpp
+// ~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_STREAMBUF_HPP
+#define ASIO_STREAMBUF_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/basic_streambuf.hpp"
+
+namespace asio {
+
+/// Typedef for the typical usage of basic_streambuf.
+typedef basic_streambuf<> streambuf;
+
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_STREAMBUF_HPP
diff --git a/library/include/libtorrent/asio/system_exception.hpp b/library/include/libtorrent/asio/system_exception.hpp
new file mode 100644
index 000000000..599e22712
--- /dev/null
+++ b/library/include/libtorrent/asio/system_exception.hpp
@@ -0,0 +1,198 @@
+//
+// error.hpp
+// ~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_SYSTEM_EXCEPTION_HPP
+#define ASIO_SYSTEM_EXCEPTION_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/detail/push_options.hpp"
+#include <boost/config.hpp>
+#include <boost/scoped_ptr.hpp>
+#include <cerrno>
+#include <cstring>
+#include <exception>
+#include <string>
+#include <boost/detail/workaround.hpp>
+#if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564))
+# include <iostream>
+#endif // BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564))
+#include "asio/detail/pop_options.hpp"
+
+#include "asio/detail/win_local_free_on_block_exit.hpp"
+
+namespace asio {
+
+/// The system_exception class is used to represent system conditions that
+/// prevent the library from operating correctly.
+class system_exception
+ : public std::exception
+{
+public:
+ /// Construct with a specific context and error code.
+ system_exception(const std::string& context, int code)
+ : context_(context),
+ code_(code)
+ {
+ }
+
+ /// Copy constructor.
+ system_exception(const system_exception& e)
+ : std::exception(e),
+ context_(e.context_),
+ code_(e.code_)
+ {
+ }
+
+ /// Destructor.
+ virtual ~system_exception() throw ()
+ {
+ }
+
+ /// Assignment operator.
+ system_exception& operator=(const system_exception& e)
+ {
+ context_ = e.context_;
+ code_ = e.code_;
+ what_.reset();
+ return *this;
+ }
+
+ /// Get a string representation of the exception.
+ virtual const char* what() const throw ()
+ {
+#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+ try
+ {
+ if (!what_)
+ {
+ char* msg = 0;
+ DWORD length = ::FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER
+ | FORMAT_MESSAGE_FROM_SYSTEM
+ | FORMAT_MESSAGE_IGNORE_INSERTS, 0, code_,
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (char*)&msg, 0, 0);
+ detail::win_local_free_on_block_exit local_free_obj(msg);
+ if (length && msg[length - 1] == '\n')
+ msg[--length] = '\0';
+ if (length && msg[length - 1] == '\r')
+ msg[--length] = '\0';
+ if (length)
+ {
+ std::string tmp(context_);
+ tmp += ": ";
+ tmp += msg;
+ what_.reset(new std::string(tmp));
+ }
+ else
+ {
+ return "asio system_exception";
+ }
+ }
+ return what_->c_str();
+ }
+ catch (std::exception&)
+ {
+ return "asio system_exception";
+ }
+#elif defined(__sun) || defined(__QNX__)
+ return strerror(code_);
+#elif defined(__MACH__) && defined(__APPLE__) \
+ || defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__)
+ try
+ {
+ char buf[256] = "";
+ strerror_r(code_, buf, sizeof(buf));
+ std::string tmp(context_);
+ tmp += ": ";
+ tmp += buf;
+ what_.reset(new std::string(tmp));
+ return what_->c_str();
+ }
+ catch (std::exception&)
+ {
+ return "asio system_exception";
+ }
+#else
+ try
+ {
+ char buf[256] = "";
+ std::string tmp(context_);
+ tmp += ": ";
+ tmp += strerror_r(code_, buf, sizeof(buf));
+ what_.reset(new std::string(tmp));
+ return what_->c_str();
+ }
+ catch (std::exception&)
+ {
+ return "asio system_exception";
+ }
+#endif
+ }
+
+ /// Get the implementation-defined context associated with the exception.
+ const std::string& context() const
+ {
+ return context_;
+ }
+
+ /// Get the implementation-defined code associated with the exception.
+ int code() const
+ {
+ return code_;
+ }
+
+private:
+ // The context associated with the error.
+ std::string context_;
+
+ // The code associated with the error.
+ int code_;
+
+ // The string representation of the error.
+ mutable boost::scoped_ptr<std::string> what_;
+};
+
+/// Output the string associated with a system exception.
+/**
+ * Used to output a human-readable string that is associated with a system
+ * exception.
+ *
+ * @param os The output stream to which the string will be written.
+ *
+ * @param e The exception to be written.
+ *
+ * @return The output stream.
+ *
+ * @relates asio::system_exception
+ */
+#if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564))
+std::ostream& operator<<(std::ostream& os, const system_exception& e)
+{
+ os << e.what();
+ return os;
+}
+#else // BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564))
+template <typename Ostream>
+Ostream& operator<<(Ostream& os, const system_exception& e)
+{
+ os << e.what();
+ return os;
+}
+#endif // BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564))
+
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_SYSTEM_EXCEPTION_HPP
diff --git a/library/include/libtorrent/asio/thread.hpp b/library/include/libtorrent/asio/thread.hpp
new file mode 100644
index 000000000..72b2510ca
--- /dev/null
+++ b/library/include/libtorrent/asio/thread.hpp
@@ -0,0 +1,91 @@
+//
+// thread.hpp
+// ~~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_THREAD_HPP
+#define ASIO_THREAD_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/detail/noncopyable.hpp"
+#include "asio/detail/thread.hpp"
+
+namespace asio {
+
+/// A simple abstraction for starting threads.
+/**
+ * The asio::thread class implements the smallest possible subset of the
+ * functionality of boost::thread. It is intended to be used only for starting
+ * a thread and waiting for it to exit. If more extensive threading
+ * capabilities are required, you are strongly advised to use something else.
+ *
+ * @par Thread Safety:
+ * @e Distinct @e objects: Safe.@n
+ * @e Shared @e objects: Unsafe.
+ *
+ * @par Example:
+ * A typical use of asio::thread would be to launch a thread to run an
+ * io_service's event processing loop:
+ *
+ * @par
+ * @code asio::io_service io_service;
+ * // ...
+ * asio::thread t(boost::bind(&asio::io_service::run, &io_service));
+ * // ...
+ * t.join(); @endcode
+ */
+class thread
+ : private noncopyable
+{
+public:
+ /// Start a new thread that executes the supplied function.
+ /**
+ * This constructor creates a new thread that will execute the given function
+ * or function object.
+ *
+ * @param f The function or function object to be run in the thread. The
+ * function signature must be: @code void f(); @endcode
+ */
+ template <typename Function>
+ explicit thread(Function f)
+ : impl_(f)
+ {
+ }
+
+ /// Destructor.
+ ~thread()
+ {
+ }
+
+ /// Wait for the thread to exit.
+ /**
+ * This function will block until the thread has exited.
+ *
+ * If this function is not called before the thread object is destroyed, the
+ * thread itself will continue to run until completion. You will, however,
+ * no longer have the ability to wait for it to exit.
+ */
+ void join()
+ {
+ impl_.join();
+ }
+
+private:
+ detail::thread impl_;
+};
+
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_THREAD_HPP
diff --git a/library/include/libtorrent/asio/time_traits.hpp b/library/include/libtorrent/asio/time_traits.hpp
new file mode 100644
index 000000000..7b459944c
--- /dev/null
+++ b/library/include/libtorrent/asio/time_traits.hpp
@@ -0,0 +1,78 @@
+//
+// time_traits.hpp
+// ~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_TIME_TRAITS_HPP
+#define ASIO_TIME_TRAITS_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/detail/socket_types.hpp" // Must come before posix_time.
+
+#include "asio/detail/push_options.hpp"
+#include <boost/date_time/posix_time/posix_time_types.hpp>
+#include "asio/detail/pop_options.hpp"
+
+namespace asio {
+
+/// Time traits suitable for use with the deadline timer.
+template <typename Time>
+struct time_traits;
+
+/// Time traits specialised for posix_time.
+template <>
+struct time_traits<boost::posix_time::ptime>
+{
+ /// The time type.
+ typedef boost::posix_time::ptime time_type;
+
+ /// The duration type.
+ typedef boost::posix_time::time_duration duration_type;
+
+ /// Get the current time.
+ static time_type now()
+ {
+ return boost::posix_time::microsec_clock::universal_time();
+ }
+
+ /// Add a duration to a time.
+ static time_type add(const time_type& t, const duration_type& d)
+ {
+ return t + d;
+ }
+
+ /// Subtract one time from another.
+ static duration_type subtract(const time_type& t1, const time_type& t2)
+ {
+ return t1 - t2;
+ }
+
+ /// Test whether one time is less than another.
+ static bool less_than(const time_type& t1, const time_type& t2)
+ {
+ return t1 < t2;
+ }
+
+ /// Convert to POSIX duration type.
+ static boost::posix_time::time_duration to_posix_duration(
+ const duration_type& d)
+ {
+ return d;
+ }
+};
+
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_TIME_TRAITS_HPP
diff --git a/library/include/libtorrent/asio/write.hpp b/library/include/libtorrent/asio/write.hpp
new file mode 100644
index 000000000..7bebecf49
--- /dev/null
+++ b/library/include/libtorrent/asio/write.hpp
@@ -0,0 +1,543 @@
+//
+// write.hpp
+// ~~~~~~~~~
+//
+// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_WRITE_HPP
+#define ASIO_WRITE_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+#include "asio/detail/push_options.hpp"
+#include <cstddef>
+#include <boost/config.hpp>
+#include "asio/detail/pop_options.hpp"
+
+#include "asio/basic_streambuf.hpp"
+
+namespace asio {
+
+/**
+ * @defgroup write asio::write
+ */
+/*@{*/
+
+/// Write all of the supplied data to a stream before returning.
+/**
+ * This function is used to write a certain number of bytes of data to a stream.
+ * The call will block until one of the following conditions is true:
+ *
+ * @li All of the data in the supplied buffers has been written. That is, the
+ * bytes transferred is equal to the sum of the buffer sizes.
+ *
+ * @li An error occurred.
+ *
+ * This operation is implemented in terms of one or more calls to the stream's
+ * write_some function.
+ *
+ * @param s The stream to which the data is to be written. The type must support
+ * the Sync_Write_Stream concept.
+ *
+ * @param buffers One or more buffers containing the data to be written. The sum
+ * of the buffer sizes indicates the maximum number of bytes to write to the
+ * stream.
+ *
+ * @returns The number of bytes transferred.
+ *
+ * @throws Sync_Write_Stream::error_type Thrown on failure.
+ *
+ * @par Example:
+ * To write a single data buffer use the @ref buffer function as follows:
+ * @code asio::write(s, asio::buffer(data, size)); @endcode
+ * See the @ref buffer documentation for information on writing multiple
+ * buffers in one go, and how to use it with arrays, boost::array or
+ * std::vector.
+ *
+ * @note This overload is equivalent to calling:
+ * @code asio::write(
+ * s, buffers,
+ * asio::transfer_all(),
+ * asio::throw_error()); @endcode
+ */
+template <typename Sync_Write_Stream, typename Const_Buffers>
+std::size_t write(Sync_Write_Stream& s, const Const_Buffers& buffers);
+
+/// Write a certain amount of data to a stream before returning.
+/**
+ * This function is used to write a certain number of bytes of data to a stream.
+ * The call will block until one of the following conditions is true:
+ *
+ * @li All of the data in the supplied buffers has been written. That is, the
+ * bytes transferred is equal to the sum of the buffer sizes.
+ *
+ * @li The completion_condition function object returns true.
+ *
+ * This operation is implemented in terms of one or more calls to the stream's
+ * write_some function.
+ *
+ * @param s The stream to which the data is to be written. The type must support
+ * the Sync_Write_Stream concept.
+ *
+ * @param buffers One or more buffers containing the data to be written. The sum
+ * of the buffer sizes indicates the maximum number of bytes to write to the
+ * stream.
+ *
+ * @param completion_condition The function object to be called to determine
+ * whether the write operation is complete. The signature of the function object
+ * must be:
+ * @code bool completion_condition(
+ * const Sync_Write_Stream::error_type& error, // Result of latest write_some
+ * // operation.
+ *
+ * std::size_t bytes_transferred // Number of bytes transferred
+ * // so far.
+ * ); @endcode
+ * A return value of true indicates that the write operation is complete. False
+ * indicates that further calls to the stream's write_some function are
+ * required.
+ *
+ * @returns The number of bytes transferred.
+ *
+ * @throws Sync_Write_Stream::error_type Thrown on failure.
+ *
+ * @par Example:
+ * To write a single data buffer use the @ref buffer function as follows:
+ * @code asio::write(s, asio::buffer(data, size),
+ * asio::transfer_at_least(32)); @endcode
+ * See the @ref buffer documentation for information on writing multiple
+ * buffers in one go, and how to use it with arrays, boost::array or
+ * std::vector.
+ *
+ * @note This overload is equivalent to calling:
+ * @code asio::write(
+ * s, buffers,
+ * completion_condition,
+ * asio::throw_error()); @endcode
+ */
+template <typename Sync_Write_Stream, typename Const_Buffers,
+ typename Completion_Condition>
+std::size_t write(Sync_Write_Stream& s, const Const_Buffers& buffers,
+ Completion_Condition completion_condition);
+
+/// Write a certain amount of data to a stream before returning.
+/**
+ * This function is used to write a certain number of bytes of data to a stream.
+ * The call will block until one of the following conditions is true:
+ *
+ * @li All of the data in the supplied buffers has been written. That is, the
+ * bytes transferred is equal to the sum of the buffer sizes.
+ *
+ * @li The completion_condition function object returns true.
+ *
+ * This operation is implemented in terms of one or more calls to the stream's
+ * write_some function.
+ *
+ * @param s The stream to which the data is to be written. The type must support
+ * the Sync_Write_Stream concept.
+ *
+ * @param buffers One or more buffers containing the data to be written. The sum
+ * of the buffer sizes indicates the maximum number of bytes to write to the
+ * stream.
+ *
+ * @param completion_condition The function object to be called to determine
+ * whether the write operation is complete. The signature of the function object
+ * must be:
+ * @code bool completion_condition(
+ * const Sync_Write_Stream::error_type& error, // Result of latest write_some
+ * // operation.
+ *
+ * std::size_t bytes_transferred // Number of bytes transferred
+ * // so far.
+ * ); @endcode
+ * A return value of true indicates that the write operation is complete. False
+ * indicates that further calls to the stream's write_some function are
+ * required.
+ *
+ * @param error_handler A handler to be called when the operation completes,
+ * to indicate whether or not an error has occurred. Copies will be made of
+ * the handler as required. The function signature of the handler must be:
+ * @code void error_handler(
+ * const Sync_Write_Stream::error_type& error // Result of operation.
+ * ); @endcode
+ *
+ * @returns The number of bytes written. If an error occurs, and the error
+ * handler does not throw an exception, returns the total number of bytes
+ * successfully transferred prior to the error.
+ */
+template <typename Sync_Write_Stream, typename Const_Buffers,
+ typename Completion_Condition, typename Error_Handler>
+std::size_t write(Sync_Write_Stream& s, const Const_Buffers& buffers,
+ Completion_Condition completion_condition, Error_Handler error_handler);
+
+/// Write a certain amount of data to a stream before returning.
+/**
+ * This function is used to write a certain number of bytes of data to a stream.
+ * The call will block until one of the following conditions is true:
+ *
+ * @li All of the data in the supplied basic_streambuf has been written.
+ *
+ * @li An error occurred.
+ *
+ * This operation is implemented in terms of one or more calls to the stream's
+ * write_some function.
+ *
+ * @param s The stream to which the data is to be written. The type must support
+ * the Sync_Write_Stream concept.
+ *
+ * @param b The basic_streambuf object from which data will be written.
+ *
+ * @returns The number of bytes transferred.
+ *
+ * @throws Sync_Write_Stream::error_type Thrown on failure.
+ *
+ * @note This overload is equivalent to calling:
+ * @code asio::write(
+ * s, b,
+ * asio::transfer_all(),
+ * asio::throw_error()); @endcode
+ */
+template <typename Sync_Write_Stream, typename Allocator>
+std::size_t write(Sync_Write_Stream& s, basic_streambuf<Allocator>& b);
+
+/// Write a certain amount of data to a stream before returning.
+/**
+ * This function is used to write a certain number of bytes of data to a stream.
+ * The call will block until one of the following conditions is true:
+ *
+ * @li All of the data in the supplied basic_streambuf has been written.
+ *
+ * @li The completion_condition function object returns true.
+ *
+ * This operation is implemented in terms of one or more calls to the stream's
+ * write_some function.
+ *
+ * @param s The stream to which the data is to be written. The type must support
+ * the Sync_Write_Stream concept.
+ *
+ * @param b The basic_streambuf object from which data will be written.
+ *
+ * @param completion_condition The function object to be called to determine
+ * whether the write operation is complete. The signature of the function object
+ * must be:
+ * @code bool completion_condition(
+ * const Sync_Write_Stream::error_type& error, // Result of latest write_some
+ * // operation.
+ *
+ * std::size_t bytes_transferred // Number of bytes transferred
+ * // so far.
+ * ); @endcode
+ * A return value of true indicates that the write operation is complete. False
+ * indicates that further calls to the stream's write_some function are
+ * required.
+ *
+ * @returns The number of bytes transferred.
+ *
+ * @throws Sync_Write_Stream::error_type Thrown on failure.
+ *
+ * @note This overload is equivalent to calling:
+ * @code asio::write(
+ * s, b,
+ * completion_condition,
+ * asio::throw_error()); @endcode
+ */
+template <typename Sync_Write_Stream, typename Allocator,
+ typename Completion_Condition>
+std::size_t write(Sync_Write_Stream& s, basic_streambuf<Allocator>& b,
+ Completion_Condition completion_condition);
+
+/// Write a certain amount of data to a stream before returning.
+/**
+ * This function is used to write a certain number of bytes of data to a stream.
+ * The call will block until one of the following conditions is true:
+ *
+ * @li All of the data in the supplied basic_streambuf has been written.
+ *
+ * @li The completion_condition function object returns true.
+ *
+ * This operation is implemented in terms of one or more calls to the stream's
+ * write_some function.
+ *
+ * @param s The stream to which the data is to be written. The type must support
+ * the Sync_Write_Stream concept.
+ *
+ * @param b The basic_streambuf object from which data will be written.
+ *
+ * @param completion_condition The function object to be called to determine
+ * whether the write operation is complete. The signature of the function object
+ * must be:
+ * @code bool completion_condition(
+ * const Sync_Write_Stream::error_type& error, // Result of latest write_some
+ * // operation.
+ *
+ * std::size_t bytes_transferred // Number of bytes transferred
+ * // so far.
+ * ); @endcode
+ * A return value of true indicates that the write operation is complete. False
+ * indicates that further calls to the stream's write_some function are
+ * required.
+ *
+ * @param error_handler A handler to be called when the operation completes,
+ * to indicate whether or not an error has occurred. Copies will be made of
+ * the handler as required. The function signature of the handler must be:
+ * @code void error_handler(
+ * const Sync_Write_Stream::error_type& error // Result of operation.
+ * ); @endcode
+ *
+ * @returns The number of bytes written. If an error occurs, and the error
+ * handler does not throw an exception, returns the total number of bytes
+ * successfully transferred prior to the error.
+ */
+template <typename Sync_Write_Stream, typename Allocator,
+ typename Completion_Condition, typename Error_Handler>
+std::size_t write(Sync_Write_Stream& s, basic_streambuf<Allocator>& b,
+ Completion_Condition completion_condition, Error_Handler error_handler);
+
+/*@}*/
+/**
+ * @defgroup async_write asio::async_write
+ */
+/*@{*/
+
+/// Start an asynchronous operation to write of all of the supplied data to a
+/// stream.
+/**
+ * This function is used to asynchronously write a certain number of bytes of
+ * data to a stream. The function call always returns immediately. The
+ * asynchronous operation will continue until one of the following conditions
+ * is true:
+ *
+ * @li All of the data in the supplied buffers has been written. That is, the
+ * bytes transferred is equal to the sum of the buffer sizes.
+ *
+ * @li An error occurred.
+ *
+ * This operation is implemented in terms of one or more calls to the stream's
+ * async_write_some function.
+ *
+ * @param s The stream to which the data is to be written. The type must support
+ * the Async_Write_Stream concept.
+ *
+ * @param buffers One or more buffers containing the data to be written.
+ * Although the buffers object may be copied as necessary, ownership of the
+ * underlying memory blocks is retained by the caller, which must guarantee
+ * that they remain valid until the handler is called.
+ *
+ * @param handler The handler to be called when the write operation completes.
+ * Copies will be made of the handler as required. The function signature of
+ * the handler must be:
+ * @code void handler(
+ * const Async_Write_Stream::error_type& error, // Result of operation.
+ *
+ * std::size_t bytes_transferred // Number of bytes written
+ * // from the buffers. If an
+ * // error occurred, this will
+ * // be less than the sum of the
+ * // buffer sizes.
+ * ); @endcode
+ * Regardless of whether the asynchronous operation completes immediately or
+ * not, the handler will not be invoked from within this function. Invocation of
+ * the handler will be performed in a manner equivalent to using
+ * asio::io_service::post().
+ *
+ * @par Example:
+ * To write a single data buffer use the @ref buffer function as follows:
+ * @code
+ * asio::async_write(s, asio::buffer(data, size), handler);
+ * @endcode
+ * See the @ref buffer documentation for information on writing multiple
+ * buffers in one go, and how to use it with arrays, boost::array or
+ * std::vector.
+ */
+template <typename Async_Write_Stream, typename Const_Buffers, typename Handler>
+void async_write(Async_Write_Stream& s, const Const_Buffers& buffers,
+ Handler handler);
+
+/// Start an asynchronous operation to write a certain amount of data to a
+/// stream.
+/**
+ * This function is used to asynchronously write a certain number of bytes of
+ * data to a stream. The function call always returns immediately. The
+ * asynchronous operation will continue until one of the following conditions
+ * is true:
+ *
+ * @li All of the data in the supplied buffers has been written. That is, the
+ * bytes transferred is equal to the sum of the buffer sizes.
+ *
+ * @li The completion_condition function object returns true.
+ *
+ * This operation is implemented in terms of one or more calls to the stream's
+ * async_write_some function.
+ *
+ * @param s The stream to which the data is to be written. The type must support
+ * the Async_Write_Stream concept.
+ *
+ * @param buffers One or more buffers containing the data to be written.
+ * Although the buffers object may be copied as necessary, ownership of the
+ * underlying memory blocks is retained by the caller, which must guarantee
+ * that they remain valid until the handler is called.
+ *
+ * @param completion_condition The function object to be called to determine
+ * whether the write operation is complete. The signature of the function object
+ * must be:
+ * @code bool completion_condition(
+ * const Async_Write_Stream::error_type& error, // Result of latest write_some
+ * // operation.
+ *
+ * std::size_t bytes_transferred // Number of bytes transferred
+ * // so far.
+ * ); @endcode
+ * A return value of true indicates that the write operation is complete. False
+ * indicates that further calls to the stream's async_write_some function are
+ * required.
+ *
+ * @param handler The handler to be called when the write operation completes.
+ * Copies will be made of the handler as required. The function signature of the
+ * handler must be:
+ * @code void handler(
+ * const Async_Write_Stream::error_type& error, // Result of operation.
+ *
+ * std::size_t bytes_transferred // Number of bytes written
+ * // from the buffers. If an
+ * // error occurred, this will
+ * // be less than the sum of the
+ * // buffer sizes.
+ * ); @endcode
+ * Regardless of whether the asynchronous operation completes immediately or
+ * not, the handler will not be invoked from within this function. Invocation of
+ * the handler will be performed in a manner equivalent to using
+ * asio::io_service::post().
+ *
+ * @par Example:
+ * To write a single data buffer use the @ref buffer function as follows:
+ * @code asio::async_write(s,
+ * asio::buffer(data, size),
+ * asio::transfer_at_least(32),
+ * handler); @endcode
+ * See the @ref buffer documentation for information on writing multiple
+ * buffers in one go, and how to use it with arrays, boost::array or
+ * std::vector.
+ */
+template <typename Async_Write_Stream, typename Const_Buffers,
+ typename Completion_Condition, typename Handler>
+void async_write(Async_Write_Stream& s, const Const_Buffers& buffers,
+ Completion_Condition completion_condition, Handler handler);
+
+/// Start an asynchronous operation to write a certain amount of data to a
+/// stream.
+/**
+ * This function is used to asynchronously write a certain number of bytes of
+ * data to a stream. The function call always returns immediately. The
+ * asynchronous operation will continue until one of the following conditions
+ * is true:
+ *
+ * @li All of the data in the supplied basic_streambuf has been written.
+ *
+ * @li An error occurred.
+ *
+ * This operation is implemented in terms of one or more calls to the stream's
+ * async_write_some function.
+ *
+ * @param s The stream to which the data is to be written. The type must support
+ * the Async_Write_Stream concept.
+ *
+ * @param b A basic_streambuf object from which data will be written. Ownership
+ * of the streambuf is retained by the caller, which must guarantee that it
+ * remains valid until the handler is called.
+ *
+ * @param handler The handler to be called when the write operation completes.
+ * Copies will be made of the handler as required. The function signature of the
+ * handler must be:
+ * @code void handler(
+ * const Async_Write_Stream::error_type& error, // Result of operation.
+ *
+ * std::size_t bytes_transferred // Number of bytes written
+ * // from the buffers. If an
+ * // error occurred, this will
+ * // be less than the sum of the
+ * // buffer sizes.
+ * ); @endcode
+ * Regardless of whether the asynchronous operation completes immediately or
+ * not, the handler will not be invoked from within this function. Invocation of
+ * the handler will be performed in a manner equivalent to using
+ * asio::io_service::post().
+ */
+template <typename Async_Write_Stream, typename Allocator, typename Handler>
+void async_write(Async_Write_Stream& s, basic_streambuf<Allocator>& b,
+ Handler handler);
+
+/// Start an asynchronous operation to write a certain amount of data to a
+/// stream.
+/**
+ * This function is used to asynchronously write a certain number of bytes of
+ * data to a stream. The function call always returns immediately. The
+ * asynchronous operation will continue until one of the following conditions
+ * is true:
+ *
+ * @li All of the data in the supplied basic_streambuf has been written.
+ *
+ * @li The completion_condition function object returns true.
+ *
+ * This operation is implemented in terms of one or more calls to the stream's
+ * async_write_some function.
+ *
+ * @param s The stream to which the data is to be written. The type must support
+ * the Async_Write_Stream concept.
+ *
+ * @param b A basic_streambuf object from which data will be written. Ownership
+ * of the streambuf is retained by the caller, which must guarantee that it
+ * remains valid until the handler is called.
+ *
+ * @param completion_condition The function object to be called to determine
+ * whether the write operation is complete. The signature of the function object
+ * must be:
+ * @code bool completion_condition(
+ * const Async_Write_Stream::error_type& error, // Result of latest write_some
+ * // operation.
+ *
+ * std::size_t bytes_transferred // Number of bytes transferred
+ * // so far.
+ * ); @endcode
+ * A return value of true indicates that the write operation is complete. False
+ * indicates that further calls to the stream's async_write_some function are
+ * required.
+ *
+ * @param handler The handler to be called when the write operation completes.
+ * Copies will be made of the handler as required. The function signature of the
+ * handler must be:
+ * @code void handler(
+ * const Async_Write_Stream::error_type& error, // Result of operation.
+ *
+ * std::size_t bytes_transferred // Number of bytes written
+ * // from the buffers. If an
+ * // error occurred, this will
+ * // be less than the sum of the
+ * // buffer sizes.
+ * ); @endcode
+ * Regardless of whether the asynchronous operation completes immediately or
+ * not, the handler will not be invoked from within this function. Invocation of
+ * the handler will be performed in a manner equivalent to using
+ * asio::io_service::post().
+ */
+template <typename Async_Write_Stream, typename Allocator,
+ typename Completion_Condition, typename Handler>
+void async_write(Async_Write_Stream& s, basic_streambuf<Allocator>& b,
+ Completion_Condition completion_condition, Handler handler);
+
+/*@}*/
+
+} // namespace asio
+
+#include "asio/impl/write.ipp"
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_WRITE_HPP
diff --git a/library/include/libtorrent/aux_/allocate_resources_impl.hpp b/library/include/libtorrent/aux_/allocate_resources_impl.hpp
new file mode 100644
index 000000000..4be3d8a1e
--- /dev/null
+++ b/library/include/libtorrent/aux_/allocate_resources_impl.hpp
@@ -0,0 +1,239 @@
+/*
+
+Copyright (c) 2003, Magnus Jonsson
+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_ALLOCATE_RESOURCES_IMPL_HPP_INCLUDED
+#define TORRENT_ALLOCATE_RESOURCES_IMPL_HPP_INCLUDED
+
+#include <map>
+#include <utility>
+
+#include <boost/shared_ptr.hpp>
+
+#include "libtorrent/resource_request.hpp"
+#include "libtorrent/peer_id.hpp"
+#include "libtorrent/socket.hpp"
+#include "libtorrent/size_type.hpp"
+
+#ifdef min
+#undef min
+#endif
+
+#ifdef max
+#undef max
+#endif
+
+namespace libtorrent
+{
+
+ int saturated_add(int a, int b);
+
+ namespace aux
+ {
+ // give num_resources to r,
+ // return how how many were actually accepted.
+ inline int give(resource_request& r, int num_resources)
+ {
+ assert(num_resources >= 0);
+ assert(r.given <= r.max);
+
+ int accepted = (std::min)(num_resources, r.max - r.given);
+ assert(accepted >= 0);
+
+ r.given += accepted;
+ assert(r.given <= r.max);
+
+ return accepted;
+ }
+
+#ifndef NDEBUG
+
+ template<class It, class T>
+ class allocate_resources_contract_check
+ {
+ int m_resources;
+ It m_start;
+ It m_end;
+ resource_request T::* m_res;
+
+ public:
+ allocate_resources_contract_check(
+ int resources
+ , It start
+ , It end
+ , resource_request T::* res)
+ : m_resources(resources)
+ , m_start(start)
+ , m_end(end)
+ , m_res(res)
+ {
+ assert(m_resources >= 0);
+ for (It i = m_start, end(m_end); i != end; ++i)
+ {
+ assert(((*i).*m_res).max >= 0);
+ assert(((*i).*m_res).given >= 0);
+ }
+ }
+
+ ~allocate_resources_contract_check()
+ {
+ int sum_given = 0;
+ int sum_max = 0;
+ int sum_min = 0;
+ for (It i = m_start, end(m_end); i != end; ++i)
+ {
+ assert(((*i).*m_res).max >= 0);
+ assert(((*i).*m_res).min >= 0);
+ assert(((*i).*m_res).max >= ((*i).*m_res).min);
+ assert(((*i).*m_res).given >= 0);
+ assert(((*i).*m_res).given <= ((*i).*m_res).max);
+
+ sum_given = saturated_add(sum_given, ((*i).*m_res).given);
+ sum_max = saturated_add(sum_max, ((*i).*m_res).max);
+ sum_min = saturated_add(sum_min, ((*i).*m_res).min);
+ }
+ assert(sum_given == (std::min)(std::max(m_resources, sum_min), sum_max));
+ }
+ };
+
+#endif
+
+ template<class It, class T>
+ void allocate_resources_impl(
+ int resources
+ , It start
+ , It end
+ , resource_request T::* res)
+ {
+ assert(resources >= 0);
+ #ifndef NDEBUG
+ allocate_resources_contract_check<It, T> contract_check(
+ resources
+ , start
+ , end
+ , res);
+ #endif
+
+ if (resources == resource_request::inf)
+ {
+ // No competition for resources.
+ // Just give everyone what they want.
+ for (It i = start; i != end; ++i)
+ {
+ ((*i).*res).given = ((*i).*res).max;
+ }
+ return;
+ }
+
+ // Resources are scarce
+
+ int sum_max = 0;
+ int sum_min = 0;
+ for (It i = start; i != end; ++i)
+ {
+ sum_max = saturated_add(sum_max, ((*i).*res).max);
+ assert(((*i).*res).min < resource_request::inf);
+ assert(((*i).*res).min >= 0);
+ assert(((*i).*res).min <= ((*i).*res).max);
+ sum_min += ((*i).*res).min;
+ ((*i).*res).given = ((*i).*res).min;
+ }
+
+ if (resources == 0 || sum_max == 0)
+ return;
+
+ resources = (std::max)(resources, sum_min);
+ int resources_to_distribute = (std::min)(resources, sum_max) - sum_min;
+ assert(resources_to_distribute >= 0);
+#ifndef NDEBUG
+ int prev_resources_to_distribute = resources_to_distribute;
+#endif
+ while (resources_to_distribute > 0)
+ {
+ size_type total_used = 0;
+ size_type max_used = 0;
+ for (It i = start; i != end; ++i)
+ {
+ resource_request& r = (*i).*res;
+ if(r.given == r.max) continue;
+
+ assert(r.given < r.max);
+
+ max_used = (std::max)(max_used, (size_type)r.used + 1);
+ total_used += (size_type)r.used + 1;
+ }
+
+ size_type kNumer = resources_to_distribute;
+ size_type kDenom = total_used;
+ assert(kNumer >= 0);
+ assert(kDenom >= 0);
+ assert(kNumer <= (std::numeric_limits<int>::max)());
+ assert(total_used < (std::numeric_limits<int>::max)());
+
+ if (kNumer * max_used <= kDenom)
+ {
+ kNumer = 1;
+ kDenom = max_used;
+ assert(kDenom >= 0);
+ assert(kDenom <= (std::numeric_limits<int>::max)());
+ }
+
+ for (It i = start; i != end && resources_to_distribute > 0; ++i)
+ {
+ resource_request& r = (*i).*res;
+ if (r.given == r.max) continue;
+
+ assert(r.given < r.max);
+
+ size_type used = (size_type)r.used + 1;
+ if (used < 1) used = 1;
+ size_type to_give = used * kNumer / kDenom;
+ if (to_give > resources_to_distribute)
+ to_give = resources_to_distribute;
+ assert(to_give >= 0);
+ assert(to_give <= resources_to_distribute);
+ resources_to_distribute -= give(r, (int)to_give);
+ assert(resources_to_distribute >= 0);
+ }
+
+ assert(resources_to_distribute >= 0);
+ assert(resources_to_distribute < prev_resources_to_distribute);
+#ifndef NDEBUG
+ prev_resources_to_distribute = resources_to_distribute;
+#endif
+ }
+ }
+
+ } // namespace libtorrent::aux
+}
+
+
+#endif
diff --git a/library/include/libtorrent/aux_/session_impl.hpp b/library/include/libtorrent/aux_/session_impl.hpp
new file mode 100644
index 000000000..2712e7078
--- /dev/null
+++ b/library/include/libtorrent/aux_/session_impl.hpp
@@ -0,0 +1,382 @@
+/*
+
+Copyright (c) 2006, 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_SESSION_IMPL_HPP_INCLUDED
+#define TORRENT_SESSION_IMPL_HPP_INCLUDED
+
+#include <ctime>
+#include <algorithm>
+#include <vector>
+#include <set>
+#include <list>
+#include <deque>
+
+#ifdef _MSC_VER
+#pragma warning(push, 1)
+#endif
+
+#include <boost/limits.hpp>
+#include <boost/tuple/tuple.hpp>
+#include <boost/filesystem/path.hpp>
+#include <boost/thread.hpp>
+#include <boost/thread/recursive_mutex.hpp>
+
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+#include "libtorrent/torrent_handle.hpp"
+#include "libtorrent/entry.hpp"
+#include "libtorrent/torrent_info.hpp"
+#include "libtorrent/socket.hpp"
+#include "libtorrent/peer_connection.hpp"
+#include "libtorrent/peer_id.hpp"
+#include "libtorrent/policy.hpp"
+#include "libtorrent/tracker_manager.hpp"
+#include "libtorrent/peer_info.hpp"
+#include "libtorrent/alert.hpp"
+#include "libtorrent/fingerprint.hpp"
+#include "libtorrent/debug.hpp"
+#include "libtorrent/peer_request.hpp"
+#include "libtorrent/piece_block_progress.hpp"
+#include "libtorrent/ip_filter.hpp"
+#include "libtorrent/config.hpp"
+#include "libtorrent/session_settings.hpp"
+#include "libtorrent/kademlia/dht_tracker.hpp"
+#include "libtorrent/session_status.hpp"
+#include "libtorrent/session.hpp"
+#include "libtorrent/stat.hpp"
+
+namespace libtorrent
+{
+
+ namespace aux
+ {
+ struct session_impl;
+
+ // this data is shared between the main thread and the
+ // thread that initialize pieces
+ struct piece_checker_data
+ {
+ piece_checker_data()
+ : processing(false), progress(0.f), abort(false) {}
+
+ boost::shared_ptr<torrent> torrent_ptr;
+ boost::filesystem::path save_path;
+
+ sha1_hash info_hash;
+
+ void parse_resume_data(
+ const entry& rd
+ , const torrent_info& info
+ , std::string& error);
+
+ std::vector<int> piece_map;
+ std::vector<piece_picker::downloading_piece> unfinished_pieces;
+ std::vector<tcp::endpoint> peers;
+ entry resume_data;
+
+ // this is true if this torrent is being processed (checked)
+ // if it is not being processed, then it can be removed from
+ // the queue without problems, otherwise the abort flag has
+ // to be set.
+ bool processing;
+
+ // is filled in by storage::initialize_pieces()
+ // and represents the progress. It should be a
+ // value in the range [0, 1]
+ float progress;
+
+ // abort defaults to false and is typically
+ // filled in by torrent_handle when the user
+ // aborts the torrent
+ bool abort;
+ };
+
+ struct checker_impl: boost::noncopyable
+ {
+ checker_impl(session_impl& s): m_ses(s), m_abort(false) {}
+ void operator()();
+ piece_checker_data* find_torrent(const sha1_hash& info_hash);
+ void remove_torrent(sha1_hash const& info_hash);
+
+#ifndef NDEBUG
+ void check_invariant() const;
+#endif
+
+ // when the files has been checked
+ // the torrent is added to the session
+ session_impl& m_ses;
+
+ mutable boost::mutex m_mutex;
+ boost::condition m_cond;
+
+ // a list of all torrents that are currently in queue
+ // or checking their files
+ std::deque<boost::shared_ptr<piece_checker_data> > m_torrents;
+ std::deque<boost::shared_ptr<piece_checker_data> > m_processing;
+
+ bool m_abort;
+ };
+
+ // this is the link between the main thread and the
+ // thread started to run the main downloader loop
+ struct session_impl: boost::noncopyable
+ {
+#ifndef NDEBUG
+ friend class ::libtorrent::peer_connection;
+#endif
+ friend class checker_impl;
+ friend class invariant_access;
+ typedef std::map<boost::shared_ptr<stream_socket>
+ , boost::intrusive_ptr<peer_connection> >
+ connection_map;
+ typedef std::map<sha1_hash, boost::shared_ptr<torrent> > torrent_map;
+ typedef std::deque<boost::intrusive_ptr<peer_connection> >
+ connection_queue;
+
+ session_impl(
+ std::pair<int, int> listen_port_range
+ , fingerprint const& cl_fprint
+ , char const* listen_interface = "0.0.0.0");
+ ~session_impl();
+
+ void operator()();
+
+ void open_listen_port();
+
+ void async_accept();
+ void on_incoming_connection(boost::shared_ptr<stream_socket> const& s
+ , boost::weak_ptr<socket_acceptor> const& as, asio::error const& e);
+
+ // must be locked to access the data
+ // in this struct
+ typedef boost::recursive_mutex mutex_t;
+ mutable mutex_t m_mutex;
+
+ boost::weak_ptr<torrent> find_torrent(const sha1_hash& info_hash);
+ peer_id const& get_peer_id() const { return m_peer_id; }
+
+ // this will see if there are any pending connection attempts
+ // and in that case initiate new connections until the limit
+ // is reached.
+ void process_connection_queue();
+
+ void close_connection(boost::intrusive_ptr<peer_connection> const& p);
+ void connection_completed(boost::intrusive_ptr<peer_connection> const& p);
+ void connection_failed(boost::shared_ptr<stream_socket> const& s
+ , tcp::endpoint const& a, char const* message);
+
+ void set_settings(session_settings const& s);
+ session_settings const& settings() const { return m_settings; }
+
+#ifndef TORRENT_DISABLE_DHT
+ void add_dht_node(std::pair<std::string, int> const& node);
+ void add_dht_node(udp::endpoint n);
+ void add_dht_router(std::pair<std::string, int> const& node);
+ void set_dht_settings(dht_settings const& s);
+ dht_settings const& kad_settings() const { return m_dht_settings; }
+ void start_dht(entry const& startup_state);
+ void stop_dht();
+ entry dht_state() const;
+#endif
+ bool is_aborted() const { return m_abort; }
+
+ void set_ip_filter(ip_filter const& f);
+
+ bool listen_on(
+ std::pair<int, int> const& port_range
+ , const char* net_interface = 0);
+ bool is_listening() const;
+
+ torrent_handle add_torrent(
+ torrent_info const& ti
+ , boost::filesystem::path const& save_path
+ , entry const& resume_data
+ , bool compact_mode
+ , int block_size);
+
+ torrent_handle add_torrent(
+ char const* tracker_url
+ , sha1_hash const& info_hash
+ , boost::filesystem::path const& save_path
+ , entry const& resume_data
+ , bool compact_mode
+ , int block_size);
+
+ void remove_torrent(torrent_handle const& h);
+
+ void disable_extensions();
+ void enable_extension(extension_index i);
+ bool extensions_enabled() const;
+ bool extension_enabled(int i) const
+ { return m_extension_enabled[i]; }
+
+ std::vector<torrent_handle> get_torrents();
+
+ void set_severity_level(alert::severity_t s);
+ std::auto_ptr<alert> pop_alert();
+ void set_download_rate_limit(int bytes_per_second);
+ void set_upload_rate_limit(int bytes_per_second);
+ void set_max_half_open_connections(int limit);
+ void set_max_connections(int limit);
+ void set_max_uploads(int limit);
+
+
+ session_status status() const;
+ void set_peer_id(peer_id const& id);
+ void set_key(int key);
+ unsigned short listen_port() const;
+
+ void abort();
+
+ // handles delayed alerts
+ alert_manager m_alerts;
+
+// private:
+
+ // this is where all active sockets are stored.
+ // the selector can sleep while there's no activity on
+ // them
+ demuxer m_selector;
+
+ tracker_manager m_tracker_manager;
+ torrent_map m_torrents;
+
+ // this maps sockets to their peer_connection
+ // object. It is the complete list of all connected
+ // peers.
+ connection_map m_connections;
+
+ // this is a list of half-open tcp connections
+ // (only outgoing connections)
+ connection_map m_half_open;
+
+ // this is a queue of pending outgoing connections. If the
+ // list of half-open connections is full (given the global
+ // limit), new outgoing connections are put on this queue,
+ // waiting for one slot in the half-open queue to open up.
+ connection_queue m_connection_queue;
+
+ // filters incoming connections
+ ip_filter m_ip_filter;
+
+ // the peer id that is generated at the start of the session
+ peer_id m_peer_id;
+
+ // the key is an id that is used to identify the
+ // client with the tracker only. It is randomized
+ // at startup
+ int m_key;
+
+ // the range of ports we try to listen on
+ std::pair<int, int> m_listen_port_range;
+
+ // the ip-address of the interface
+ // we are supposed to listen on.
+ // if the ip is set to zero, it means
+ // that we should let the os decide which
+ // interface to listen on
+ tcp::endpoint m_listen_interface;
+
+ boost::shared_ptr<socket_acceptor> m_listen_socket;
+
+ // the entries in this array maps the
+ // extension index (as specified in peer_connection)
+ bool m_extension_enabled[num_supported_extensions];
+
+ // the settings for the client
+ session_settings m_settings;
+
+ // set to true when the session object
+ // is being destructed and the thread
+ // should exit
+ volatile bool m_abort;
+
+ // maximum upload rate given in
+ // bytes per second. -1 means
+ // unlimited
+ int m_upload_rate;
+ int m_download_rate;
+ int m_max_uploads;
+ int m_max_connections;
+ // the number of simultaneous half-open tcp
+ // connections libtorrent will have.
+ int m_half_open_limit;
+
+ // statistics gathered from all torrents.
+ stat m_stat;
+
+ // is false by default and set to true when
+ // the first incoming connection is established
+ // this is used to know if the client is behind
+ // NAT or not.
+ bool m_incoming_connection;
+
+ // does the actual disconnections
+ // that are queued up in m_disconnect_peer
+ void second_tick(asio::error const& e);
+ boost::posix_time::ptime m_last_tick;
+
+#ifndef TORRENT_DISABLE_DHT
+ boost::scoped_ptr<dht::dht_tracker> m_dht;
+ dht_settings m_dht_settings;
+#endif
+ // the timer used to fire the second_tick
+ deadline_timer m_timer;
+#ifndef NDEBUG
+ void check_invariant(const char *place = 0);
+#endif
+#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
+ boost::shared_ptr<logger> create_log(std::string const& name, bool append = true);
+ public:
+ boost::shared_ptr<logger> m_logger;
+ private:
+#endif
+
+ // data shared between the main thread
+ // and the checker thread
+ checker_impl m_checker_impl;
+
+ // the main working thread
+ boost::scoped_ptr<boost::thread> m_thread;
+
+ // the thread that calls initialize_pieces()
+ // on all torrents before they start downloading
+ boost::scoped_ptr<boost::thread> m_checker_thread;
+ };
+ }
+}
+
+
+#endif
+
diff --git a/library/include/libtorrent/bencode.hpp b/library/include/libtorrent/bencode.hpp
new file mode 100755
index 000000000..a142b5864
--- /dev/null
+++ b/library/include/libtorrent/bencode.hpp
@@ -0,0 +1,299 @@
+/*
+
+Copyright (c) 2003, 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_BENCODE_HPP_INCLUDED
+#define TORRENT_BENCODE_HPP_INCLUDED
+
+
+
+/*
+ * This file declares the following functions:
+ *
+ *----------------------------------
+ * template<class OutIt>
+ * void libtorrent::bencode(OutIt out, const libtorrent::entry& e);
+ *
+ * Encodes a message entry with bencoding into the output
+ * iterator given. The bencoding is described in the BitTorrent
+ * protocol description document OutIt must be an OutputIterator
+ * of type char. This may throw libtorrent::invalid_encoding if
+ * the entry contains invalid nodes (undefined_t for example).
+ *
+ *----------------------------------
+ * template<class InIt>
+ * libtorrent::entry libtorrent::bdecode(InIt start, InIt end);
+ *
+ * Decodes the buffer given by the start and end iterators
+ * and returns the decoded entry. InIt must be an InputIterator
+ * of type char. May throw libtorrent::invalid_encoding if
+ * the string is not correctly bencoded.
+ *
+ */
+
+
+
+
+#include <cstdlib>
+
+#ifdef _MSC_VER
+#pragma warning(push, 1)
+#endif
+
+#include <boost/lexical_cast.hpp>
+#include <boost/static_assert.hpp>
+
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+#include "libtorrent/entry.hpp"
+#include "libtorrent/config.hpp"
+
+#if defined(_MSC_VER)
+namespace std
+{
+ using ::isdigit;
+ using ::atoi;
+};
+
+#define for if (false) {} else for
+#endif
+
+
+namespace libtorrent
+{
+
+ struct TORRENT_EXPORT invalid_encoding: std::exception
+ {
+ virtual const char* what() const throw() { return "invalid bencoding"; }
+ };
+
+ namespace detail
+ {
+ template <class OutIt>
+ void write_string(OutIt& out, const std::string& val)
+ {
+ std::string::const_iterator end = val.begin() + val.length();
+ std::copy(val.begin(), end, out);
+ }
+
+ TORRENT_EXPORT char const* integer_to_str(char* buf, int size, entry::integer_type val);
+
+ template <class OutIt>
+ void write_integer(OutIt& out, entry::integer_type val)
+ {
+ // the stack allocated buffer for keeping the
+ // decimal representation of the number can
+ // not hold number bigger than this:
+ BOOST_STATIC_ASSERT(sizeof(entry::integer_type) <= 8);
+ char buf[21];
+ for (char const* str = integer_to_str(buf, 21, val);
+ *str != 0; ++str)
+ {
+ *out = *str;
+ ++out;
+ }
+ }
+
+ template <class OutIt>
+ void write_char(OutIt& out, char c)
+ {
+ *out = c;
+ ++out;
+ }
+
+ template <class InIt>
+ std::string read_until(InIt& in, InIt end, char end_token)
+ {
+ if (in == end) throw invalid_encoding();
+ std::string ret;
+ while (*in != end_token)
+ {
+ ret += *in;
+ ++in;
+ if (in == end) throw invalid_encoding();
+ }
+ return ret;
+ }
+
+ template<class InIt>
+ void read_string(InIt& in, InIt end, int len, std::string& str)
+ {
+ assert(len >= 0);
+ for (int i = 0; i < len; ++i)
+ {
+ if (in == end) throw invalid_encoding();
+ str += *in;
+ ++in;
+ }
+ }
+
+ template<class OutIt>
+ void bencode_recursive(OutIt& out, const entry& e)
+ {
+ switch(e.type())
+ {
+ case entry::int_t:
+ write_char(out, 'i');
+ write_integer(out, e.integer());
+ write_char(out, 'e');
+ break;
+ case entry::string_t:
+ write_integer(out, e.string().length());
+ write_char(out, ':');
+ write_string(out, e.string());
+ break;
+ case entry::list_t:
+ write_char(out, 'l');
+ for (entry::list_type::const_iterator i = e.list().begin(); i != e.list().end(); ++i)
+ bencode_recursive(out, *i);
+ write_char(out, 'e');
+ break;
+ case entry::dictionary_t:
+ write_char(out, 'd');
+ for (entry::dictionary_type::const_iterator i = e.dict().begin();
+ i != e.dict().end(); ++i)
+ {
+ // write key
+ write_integer(out, i->first.length());
+ write_char(out, ':');
+ write_string(out, i->first);
+ // write value
+ bencode_recursive(out, i->second);
+ }
+ write_char(out, 'e');
+ break;
+ default:
+ // do nothing
+ break;
+ }
+ }
+
+ template<class InIt>
+ void bdecode_recursive(InIt& in, InIt end, entry& ret)
+ {
+ if (in == end) throw invalid_encoding();
+ switch (*in)
+ {
+
+ // ----------------------------------------------
+ // integer
+ case 'i':
+ {
+ ++in; // 'i'
+ std::string val = read_until(in, end, 'e');
+ assert(*in == 'e');
+ ++in; // 'e'
+ ret = entry(entry::int_t);
+ ret.integer() = boost::lexical_cast<entry::integer_type>(val);
+ } break;
+
+ // ----------------------------------------------
+ // list
+ case 'l':
+ {
+ ret = entry(entry::list_t);
+ ++in; // 'l'
+ while (*in != 'e')
+ {
+ ret.list().push_back(entry());
+ entry& e = ret.list().back();
+ bdecode_recursive(in, end, e);
+ if (in == end) throw invalid_encoding();
+ }
+ assert(*in == 'e');
+ ++in; // 'e'
+ } break;
+
+ // ----------------------------------------------
+ // dictionary
+ case 'd':
+ {
+ ret = entry(entry::dictionary_t);
+ ++in; // 'd'
+ while (*in != 'e')
+ {
+ entry key;
+ bdecode_recursive(in, end, key);
+ entry& e = ret[key.string()];
+ bdecode_recursive(in, end, e);
+ if (in == end) throw invalid_encoding();
+ }
+ assert(*in == 'e');
+ ++in; // 'e'
+ } break;
+
+ // ----------------------------------------------
+ // string
+ default:
+ if (isdigit((unsigned char)*in))
+ {
+ std::string len_s = read_until(in, end, ':');
+ assert(*in == ':');
+ ++in; // ':'
+ int len = std::atoi(len_s.c_str());
+ ret = entry(entry::string_t);
+ read_string(in, end, len, ret.string());
+ }
+ else
+ {
+ throw invalid_encoding();
+ }
+ }
+ }
+ }
+
+ template<class OutIt>
+ void bencode(OutIt out, const entry& e)
+ {
+ detail::bencode_recursive(out, e);
+ }
+
+ template<class InIt>
+ entry bdecode(InIt start, InIt end)
+ {
+ try
+ {
+ entry e;
+ detail::bdecode_recursive(start, end, e);
+ return e;
+ }
+ catch(type_error&)
+ {
+ throw invalid_encoding();
+ }
+ }
+
+}
+
+#endif // TORRENT_BENCODE_HPP_INCLUDED
diff --git a/library/include/libtorrent/bt_peer_connection.hpp b/library/include/libtorrent/bt_peer_connection.hpp
new file mode 100755
index 000000000..d02614c52
--- /dev/null
+++ b/library/include/libtorrent/bt_peer_connection.hpp
@@ -0,0 +1,295 @@
+/*
+
+Copyright (c) 2003, 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_BT_PEER_CONNECTION_HPP_INCLUDED
+#define TORRENT_BT_PEER_CONNECTION_HPP_INCLUDED
+
+#include <ctime>
+#include <algorithm>
+#include <vector>
+#include <deque>
+#include <string>
+
+#include "libtorrent/debug.hpp"
+
+#ifdef _MSC_VER
+#pragma warning(push, 1)
+#endif
+
+#include <boost/smart_ptr.hpp>
+#include <boost/noncopyable.hpp>
+#include <boost/array.hpp>
+#include <boost/date_time/posix_time/posix_time.hpp>
+#include <boost/optional.hpp>
+#include <boost/cstdint.hpp>
+
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+#include "libtorrent/buffer.hpp"
+#include "libtorrent/peer_connection.hpp"
+#include "libtorrent/socket.hpp"
+#include "libtorrent/peer_id.hpp"
+#include "libtorrent/storage.hpp"
+#include "libtorrent/stat.hpp"
+#include "libtorrent/alert.hpp"
+#include "libtorrent/torrent_handle.hpp"
+#include "libtorrent/torrent.hpp"
+#include "libtorrent/allocate_resources.hpp"
+#include "libtorrent/peer_request.hpp"
+#include "libtorrent/piece_block_progress.hpp"
+#include "libtorrent/config.hpp"
+
+namespace libtorrent
+{
+ class torrent;
+
+ namespace detail
+ {
+ struct session_impl;
+ }
+
+ class TORRENT_EXPORT bt_peer_connection
+ : public peer_connection
+ {
+ friend class invariant_access;
+ public:
+
+ // this is the constructor where the we are the active part.
+ // The peer_conenction should handshake and verify that the
+ // other end has the correct id
+ bt_peer_connection(
+ aux::session_impl& ses
+ , boost::weak_ptr<torrent> t
+ , boost::shared_ptr<stream_socket> s
+ , tcp::endpoint const& remote);
+
+ // with this constructor we have been contacted and we still don't
+ // know which torrent the connection belongs to
+ bt_peer_connection(
+ aux::session_impl& ses
+ , boost::shared_ptr<stream_socket> s);
+
+ ~bt_peer_connection();
+
+ // called from the main loop when this connection has any
+ // work to do.
+
+ void on_sent(asio::error const& error
+ , std::size_t bytes_transferred);
+ void on_receive(asio::error const& error
+ , std::size_t bytes_transferred);
+
+ virtual void get_peer_info(peer_info& p) const;
+
+ bool support_extensions() const { return m_supports_extensions; }
+
+ bool supports_extension(extension_index ex) const
+ { return m_extension_messages[ex] > 0; }
+
+ bool has_metadata() const;
+
+ // the message handlers are called
+ // each time a recv() returns some new
+ // data, the last time it will be called
+ // is when the entire packet has been
+ // received, then it will no longer
+ // be called. i.e. most handlers need
+ // to check how much of the packet they
+ // have received before any processing
+ void on_keepalive();
+ void on_choke(int received);
+ void on_unchoke(int received);
+ void on_interested(int received);
+ void on_not_interested(int received);
+ void on_have(int received);
+ void on_bitfield(int received);
+ void on_request(int received);
+ void on_piece(int received);
+ void on_cancel(int received);
+ void on_dht_port(int received);
+
+ void on_extended(int received);
+
+ void on_extended_handshake();
+ void on_chat();
+ void on_metadata();
+ void on_peer_exchange();
+
+ typedef void (bt_peer_connection::*message_handler)(int received);
+
+ // the following functions appends messages
+ // to the send buffer
+ void write_choke();
+ void write_unchoke();
+ void write_interested();
+ void write_not_interested();
+ void write_request(peer_request const& r);
+ 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_handshake();
+ void write_extensions();
+ void write_chat_message(const std::string& msg);
+ void write_metadata(std::pair<int, int> req);
+ void write_metadata_request(std::pair<int, int> req);
+ void write_keepalive();
+ void write_dht_port(int listen_port);
+ void on_connected() {}
+ void on_tick();
+
+#ifndef NDEBUG
+ void check_invariant() const;
+ boost::posix_time::ptime m_last_choke;
+#endif
+
+ private:
+
+ bool dispatch_message(int received);
+ // returns the block currently being
+ // downloaded. And the progress of that
+ // block. If the peer isn't downloading
+ // a piece for the moment, the boost::optional
+ // will be invalid.
+ boost::optional<piece_block_progress> downloading_piece_progress() const;
+
+ // if we don't have all metadata
+ // this function will request a part of it
+ // from this peer
+ void request_metadata();
+
+ enum state
+ {
+ read_protocol_length = 0,
+ read_protocol_string,
+ read_info_hash,
+ read_peer_id,
+
+ read_packet_size,
+ read_packet
+ };
+
+ std::string m_client_version;
+
+ state m_state;
+
+ // the timeout in seconds
+ int m_timeout;
+
+ enum message_type
+ {
+ // standard messages
+ msg_choke = 0,
+ msg_unchoke,
+ msg_interested,
+ msg_not_interested,
+ msg_have,
+ msg_bitfield,
+ msg_request,
+ msg_piece,
+ msg_cancel,
+ msg_dht_port,
+ // extension protocol message
+ msg_extended = 20,
+
+ num_supported_messages
+ };
+
+ static const message_handler m_message_handler[num_supported_messages];
+
+ // this is a queue of ranges that describes
+ // where in the send buffer actual payload
+ // data is located. This is currently
+ // only used to be able to gather statistics
+ // seperately on payload and protocol data.
+ struct range
+ {
+ range(int s, int l)
+ : start(s)
+ , length(l)
+ {
+ assert(s >= 0);
+ assert(l > 0);
+ }
+ int start;
+ int length;
+ };
+ static bool range_below_zero(const range& r)
+ { return r.start < 0; }
+ std::deque<range> m_payloads;
+
+ // this is set to true if the handshake from
+ // the peer indicated that it supports the
+ // extension protocol
+ bool m_supports_extensions;
+ bool m_supports_dht_port;
+
+ static const char* extension_names[num_supported_extensions];
+ // contains the indices of the extension messages for each extension
+ // supported by the other end. A value of <= 0 means that the extension
+ // is not supported.
+ int m_extension_messages[num_supported_extensions];
+
+ // this is set to the current time each time we get a
+ // "I don't have metadata" message.
+ boost::posix_time::ptime m_no_metadata;
+
+ // this is set to the time when we last sent
+ // a request for metadata to this peer
+ boost::posix_time::ptime m_metadata_request;
+
+ // this is set to true when we send a metadata
+ // request to this peer, and reset to false when
+ // we receive a reply to our request.
+ bool m_waiting_metadata_request;
+
+ // if we're waiting for a metadata request
+ // this was the request we sent
+ std::pair<int, int> m_last_metadata_request;
+
+ // the number of bytes of metadata we have received
+ // so far from this per, only counting the current
+ // request. Any previously finished requests
+ // that have been forwarded to the torrent object
+ // do not count.
+ int m_metadata_progress;
+
+#ifndef NDEBUG
+ bool m_in_constructor;
+#endif
+ };
+}
+
+#endif // TORRENT_BT_PEER_CONNECTION_HPP_INCLUDED
+
diff --git a/library/include/libtorrent/buffer.hpp b/library/include/libtorrent/buffer.hpp
new file mode 100644
index 000000000..5dc2e558a
--- /dev/null
+++ b/library/include/libtorrent/buffer.hpp
@@ -0,0 +1,447 @@
+/*
+Copyright (c) 2003 - 2005, Arvid Norberg, Daniel Wallin
+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 Rasterbar Software 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 LIBTORRENT_BUFFER_HPP
+#define LIBTORRENT_BUFFER_HPP
+
+//#define TORRENT_BUFFER_DEBUG
+
+#include "libtorrent/invariant_check.hpp"
+#include <memory>
+
+namespace libtorrent {
+
+class buffer
+{
+public:
+ struct interval
+ {
+ interval(char* begin, char* end)
+ : begin(begin)
+ , end(end)
+ {}
+
+ char operator[](int index) const
+ {
+ assert(begin + index < end);
+ return begin[index];
+ }
+
+ int left() const { assert(end > begin); return end - begin; }
+
+ char* begin;
+ char* end;
+ };
+
+ struct const_interval
+ {
+ const_interval(char const* begin, char const* end)
+ : begin(begin)
+ , end(end)
+ {}
+
+ char operator[](int index) const
+ {
+ assert(begin + index < end);
+ return begin[index];
+ }
+
+ int left() const { assert(end > begin); return end - begin; }
+
+ char const* begin;
+ char const* end;
+ };
+
+ typedef std::pair<const_interval, const_interval> interval_type;
+
+ buffer(std::size_t n = 0);
+ ~buffer();
+
+ interval allocate(std::size_t n);
+ void insert(char const* first, char const* last);
+ void erase(std::size_t n);
+ std::size_t size() const;
+ std::size_t capacity() const;
+ void reserve(std::size_t n);
+ interval_type data() const;
+ bool empty() const;
+
+ std::size_t space_left() const;
+
+ char const* raw_data() const
+ {
+ return m_first;
+ }
+
+#ifndef NDEBUG
+ void check_invariant() const;
+#endif
+
+private:
+ char* m_first;
+ char* m_last;
+ char* m_write_cursor;
+ char* m_read_cursor;
+ char* m_read_end;
+ bool m_empty;
+#ifdef TORRENT_BUFFER_DEBUG
+ mutable std::vector<char> m_debug;
+ mutable int m_pending_copy;
+#endif
+};
+
+inline buffer::buffer(std::size_t n)
+ : m_first((char*)::operator new(n))
+ , m_last(m_first + n)
+ , m_write_cursor(m_first)
+ , m_read_cursor(m_first)
+ , m_read_end(m_last)
+ , m_empty(true)
+{
+#ifdef TORRENT_BUFFER_DEBUG
+ m_pending_copy = 0;
+#endif
+}
+
+inline buffer::~buffer()
+{
+ ::operator delete (m_first);
+}
+
+inline buffer::interval buffer::allocate(std::size_t n)
+{
+ assert(m_read_cursor <= m_read_end || m_empty);
+
+ INVARIANT_CHECK;
+
+#ifdef TORRENT_BUFFER_DEBUG
+ if (m_pending_copy)
+ {
+ std::copy(m_write_cursor - m_pending_copy, m_write_cursor
+ , m_debug.end() - m_pending_copy);
+ m_pending_copy = 0;
+ }
+ m_debug.resize(m_debug.size() + n);
+ m_pending_copy = n;
+#endif
+ if (m_read_cursor < m_write_cursor || m_empty)
+ {
+ // ..R***W..
+ if (m_last - m_write_cursor >= (std::ptrdiff_t)n)
+ {
+ interval ret(m_write_cursor, m_write_cursor + n);
+ m_write_cursor += n;
+ m_read_end = m_write_cursor;
+ assert(m_read_cursor <= m_read_end);
+ if (n) m_empty = false;
+ return ret;
+ }
+
+ if (m_read_cursor - m_first >= (std::ptrdiff_t)n)
+ {
+ m_read_end = m_write_cursor;
+ interval ret(m_first, m_first + n);
+ m_write_cursor = m_first + n;
+ assert(m_read_cursor <= m_read_end);
+ if (n) m_empty = false;
+ return ret;
+ }
+
+ reserve(capacity() + n - (m_last - m_write_cursor));
+ assert(m_last - m_write_cursor >= (std::ptrdiff_t)n);
+ interval ret(m_write_cursor, m_write_cursor + n);
+ m_write_cursor += n;
+ m_read_end = m_write_cursor;
+ if (n) m_empty = false;
+ assert(m_read_cursor <= m_read_end);
+ return ret;
+
+ }
+ //**W...R**
+ if (m_read_cursor - m_write_cursor >= (std::ptrdiff_t)n)
+ {
+ interval ret(m_write_cursor, m_write_cursor + n);
+ m_write_cursor += n;
+ if (n) m_empty = false;
+ return ret;
+ }
+ reserve(capacity() + n - (m_read_cursor - m_write_cursor));
+ assert(m_read_cursor - m_write_cursor >= (std::ptrdiff_t)n);
+ interval ret(m_write_cursor, m_write_cursor + n);
+ m_write_cursor += n;
+ if (n) m_empty = false;
+ return ret;
+}
+
+inline void buffer::insert(char const* first, char const* last)
+{
+ INVARIANT_CHECK;
+
+ std::size_t n = last - first;
+
+#ifdef TORRENT_BUFFER_DEBUG
+ if (m_pending_copy)
+ {
+ std::copy(m_write_cursor - m_pending_copy, m_write_cursor
+ , m_debug.end() - m_pending_copy);
+ m_pending_copy = 0;
+ }
+ m_debug.insert(m_debug.end(), first, last);
+#endif
+
+ if (space_left() < n)
+ {
+ reserve(capacity() + n);
+ }
+
+ m_empty = false;
+
+ char const* end = (m_last - m_write_cursor) < (std::ptrdiff_t)n ?
+ m_last : m_write_cursor + n;
+
+ std::size_t copied = end - m_write_cursor;
+ std::memcpy(m_write_cursor, first, copied);
+
+ m_write_cursor += copied;
+ if (m_write_cursor > m_read_end) m_read_end = m_write_cursor;
+ first += copied;
+ n -= copied;
+
+ if (n == 0) return;
+
+ assert(m_write_cursor == m_last);
+ m_write_cursor = m_first;
+
+ memcpy(m_write_cursor, first, n);
+ m_write_cursor += n;
+}
+
+inline void buffer::erase(std::size_t n)
+{
+ INVARIANT_CHECK;
+
+ if (n == 0) return;
+ assert(!m_empty);
+
+#ifndef NDEBUG
+ int prev_size = size();
+#endif
+ assert(m_read_cursor <= m_read_end);
+ m_read_cursor += n;
+ if (m_read_cursor > m_read_end)
+ {
+ m_read_cursor = m_first + (m_read_cursor - m_read_end);
+ assert(m_read_cursor <= m_write_cursor);
+ }
+
+ m_empty = m_read_cursor == m_write_cursor;
+
+ assert(prev_size - n == size());
+
+#ifdef TORRENT_BUFFER_DEBUG
+ m_debug.erase(m_debug.begin(), m_debug.begin() + n);
+#endif
+}
+
+inline std::size_t buffer::size() const
+{
+ // ...R***W.
+ if (m_read_cursor < m_write_cursor)
+ {
+ return m_write_cursor - m_read_cursor;
+ }
+ // ***W..R*
+ else
+ {
+ if (m_empty) return 0;
+ return (m_write_cursor - m_first) + (m_read_end - m_read_cursor);
+ }
+}
+
+inline std::size_t buffer::capacity() const
+{
+ return m_last - m_first;
+}
+
+inline void buffer::reserve(std::size_t size)
+{
+ std::size_t n = (std::size_t)(capacity() * 1.f);
+ if (n < size) n = size;
+
+ char* buf = (char*)::operator new(n);
+ char* old = m_first;
+
+ if (m_read_cursor < m_write_cursor)
+ {
+ // ...R***W.<>.
+ std::memcpy(
+ buf + (m_read_cursor - m_first)
+ , m_read_cursor
+ , m_write_cursor - m_read_cursor
+ );
+
+ m_write_cursor = buf + (m_write_cursor - m_first);
+ m_read_cursor = buf + (m_read_cursor - m_first);
+ m_read_end = m_write_cursor;
+ m_first = buf;
+ m_last = buf + n;
+ }
+ else
+ {
+ // **W..<>.R**
+ std::size_t skip = n - (m_last - m_first);
+
+ std::memcpy(buf, m_first, m_write_cursor - m_first);
+ std::memcpy(
+ buf + (m_read_cursor - m_first) + skip
+ , m_read_cursor
+ , m_last - m_read_cursor
+ );
+
+ m_write_cursor = buf + (m_write_cursor - m_first);
+
+ if (!m_empty)
+ {
+ m_read_cursor = buf + (m_read_cursor - m_first) + skip;
+ m_read_end = buf + (m_read_end - m_first) + skip;
+ }
+ else
+ {
+ m_read_cursor = m_write_cursor;
+ m_read_end = m_write_cursor;
+ }
+
+ m_first = buf;
+ m_last = buf + n;
+ }
+
+ ::operator delete (old);
+}
+
+#ifndef NDEBUG
+inline void buffer::check_invariant() const
+{
+ assert(m_read_end >= m_read_cursor);
+ assert(m_last >= m_read_cursor);
+ assert(m_last >= m_write_cursor);
+ assert(m_last >= m_first);
+ assert(m_first <= m_read_cursor);
+ assert(m_first <= m_write_cursor);
+#ifdef TORRENT_BUFFER_DEBUG
+ int a = m_debug.size();
+ int b = size();
+ (void)a;
+ (void)b;
+ assert(m_debug.size() == size());
+#endif
+}
+#endif
+
+inline buffer::interval_type buffer::data() const
+{
+ INVARIANT_CHECK;
+
+#ifdef TORRENT_BUFFER_DEBUG
+ if (m_pending_copy)
+ {
+ std::copy(m_write_cursor - m_pending_copy, m_write_cursor
+ , m_debug.end() - m_pending_copy);
+ m_pending_copy = 0;
+ }
+#endif
+
+ // ...R***W.
+ if (m_read_cursor < m_write_cursor)
+ {
+#ifdef TORRENT_BUFFER_DEBUG
+ assert(m_debug.size() == size());
+ assert(std::equal(m_debug.begin(), m_debug.end(), m_read_cursor));
+#endif
+ return interval_type(
+ const_interval(m_read_cursor, m_write_cursor)
+ , const_interval(m_last, m_last)
+ );
+ }
+ // **W...R**
+ else
+ {
+ if (m_read_cursor == m_read_end)
+ {
+#ifdef TORRENT_BUFFER_DEBUG
+ assert(m_debug.size() == size());
+ assert(std::equal(m_debug.begin(), m_debug.end(), m_first));
+#endif
+
+ return interval_type(
+ const_interval(m_first, m_write_cursor)
+ , const_interval(m_last, m_last));
+ }
+#ifdef TORRENT_BUFFER_DEBUG
+ assert(m_debug.size() == size());
+ assert(std::equal(m_debug.begin(), m_debug.begin() + (m_read_end
+ - m_read_cursor), m_read_cursor));
+ assert(std::equal(m_debug.begin() + (m_read_end - m_read_cursor), m_debug.end()
+ , m_first));
+#endif
+
+ assert(m_read_cursor <= m_read_end || m_empty);
+ return interval_type(
+ const_interval(m_read_cursor, m_read_end)
+ , const_interval(m_first, m_write_cursor)
+ );
+ }
+}
+
+inline bool buffer::empty() const
+{
+ return m_empty;
+}
+
+inline std::size_t buffer::space_left() const
+{
+ if (m_empty) return m_last - m_first;
+
+ // ...R***W.
+ if (m_read_cursor < m_write_cursor)
+ {
+ return (m_last - m_write_cursor) + (m_read_cursor - m_first);
+ }
+ // ***W..R*
+ else
+ {
+ return m_read_cursor - m_write_cursor;
+ }
+}
+
+}
+
+#endif // LIBTORRENT_BUFFER_HPP
+
diff --git a/library/include/libtorrent/config.hpp b/library/include/libtorrent/config.hpp
new file mode 100755
index 000000000..c8d86955e
--- /dev/null
+++ b/library/include/libtorrent/config.hpp
@@ -0,0 +1,66 @@
+/*
+
+Copyright (c) 2005, 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_CONFIG_HPP_INCLUDED
+#define TORRENT_CONFIG_HPP_INCLUDED
+
+#include <boost/config.hpp>
+
+#if defined(__GNUC__) && __GNUC__ >= 4
+
+# if defined(TORRENT_BUILDING_SHARED) || defined(TORRENT_LINKING_SHARED)
+# define TORRENT_EXPORT __attribute__ ((visibility("default")))
+# else
+# define TORRENT_EXPORT
+# endif
+
+#elif defined(__GNUC__)
+
+# define TORRENT_EXPORT
+
+#elif defined(BOOST_MSVC)
+
+# if defined(TORRENT_BUILDING_SHARED)
+# define TORRENT_EXPORT __declspec(dllexport)
+# elif defined(TORRENT_LINKING_SHARED)
+# define TORRENT_EXPORT __declspec(dllimport)
+# else
+# define TORRENT_EXPORT
+# endif
+
+#else
+# define TORRENT_EXPORT
+#endif
+
+
+#endif // TORRENT_CONFIG_HPP_INCLUDED
+
diff --git a/library/include/libtorrent/debug.hpp b/library/include/libtorrent/debug.hpp
new file mode 100755
index 000000000..8aeeeb544
--- /dev/null
+++ b/library/include/libtorrent/debug.hpp
@@ -0,0 +1,92 @@
+/*
+
+Copyright (c) 2003, 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_DEBUG_HPP_INCLUDED
+#define TORRENT_DEBUG_HPP_INCLUDED
+
+#include <string>
+#include <fstream>
+#include <iostream>
+
+#ifdef _MSC_VER
+#pragma warning(push, 1)
+#endif
+
+#include <boost/lexical_cast.hpp>
+#include <boost/filesystem/fstream.hpp>
+#include <boost/filesystem/convenience.hpp>
+
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+
+namespace libtorrent
+{
+
+ // PROFILING CODE
+#ifdef TORRENT_PROFILE
+
+ void add_checkpoint(std::string const& str);
+ void print_checkpoints();
+#define TORRENT_CHECKPOINT(str) libtorrent::add_checkpoint(str)
+#else
+#define TORRENT_CHECKPOINT(str) void(0)
+#endif
+
+ // DEBUG API
+
+ struct logger
+ {
+ logger(boost::filesystem::path const& filename, bool append = true)
+ {
+ using namespace boost::filesystem;
+ path dir(complete("libtorrent_logs"));
+ if (!exists(dir)) create_directories(dir);
+ m_file.open(dir / filename, std::ios_base::out | (append ? std::ios_base::app : std::ios_base::out));
+ *this << "\n\n\n*** starting log ***\n";
+ }
+
+ template <class T>
+ logger& operator<<(T const& v)
+ {
+ m_file << v;
+ m_file.flush();
+ return *this;
+ }
+
+ boost::filesystem::ofstream m_file;
+ };
+
+}
+
+#endif // TORRENT_DEBUG_HPP_INCLUDED
diff --git a/library/include/libtorrent/entry.hpp b/library/include/libtorrent/entry.hpp
new file mode 100755
index 000000000..85c5462a2
--- /dev/null
+++ b/library/include/libtorrent/entry.hpp
@@ -0,0 +1,272 @@
+/*
+
+Copyright (c) 2003, 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_ENTRY_HPP_INCLUDED
+#define TORRENT_ENTRY_HPP_INCLUDED
+
+/*
+ *
+ * This file declares the entry class. It is a
+ * variant-type that can be an integer, list,
+ * dictionary (map) or a string. This type is
+ * used to hold bdecoded data (which is the
+ * encoding BitTorrent messages uses).
+ *
+ * it has 4 accessors to access the actual
+ * type of the object. They are:
+ * integer()
+ * string()
+ * list()
+ * dict()
+ * The actual type has to match the type you
+ * are asking for, otherwise you will get an
+ * assertion failure.
+ * When you default construct an entry, it is
+ * uninitialized. You can initialize it through the
+ * assignment operator, copy-constructor or
+ * the constructor that takes a data_type enum.
+ *
+ *
+ */
+
+
+#include <iostream>
+#include <map>
+#include <list>
+#include <string>
+#include <stdexcept>
+#include <cassert>
+
+#include "libtorrent/size_type.hpp"
+#include "libtorrent/config.hpp"
+
+namespace libtorrent
+{
+
+ struct TORRENT_EXPORT type_error: std::runtime_error
+ {
+ type_error(const char* error): std::runtime_error(error) {}
+ };
+
+ namespace detail
+ {
+ template<int v1, int v2>
+ struct max2 { enum { value = v1>v2?v1:v2 }; };
+
+ template<int v1, int v2, int v3>
+ struct max3
+ {
+ enum
+ {
+ temp = max2<v1,v2>::value,
+ value = temp>v3?temp:v3
+ };
+ };
+
+ template<int v1, int v2, int v3, int v4>
+ struct max4
+ {
+ enum
+ {
+ temp = max3<v1,v2, v3>::value,
+ value = temp>v4?temp:v4
+ };
+ };
+ }
+
+ class entry;
+
+ class TORRENT_EXPORT entry
+ {
+ public:
+
+ // the key is always a string. If a generic entry would be allowed
+ // as a key, sorting would become a problem (e.g. to compare a string
+ // to a list). The definition doesn't mention such a limit though.
+ typedef std::map<std::string, entry> dictionary_type;
+ typedef std::string string_type;
+ typedef std::list<entry> list_type;
+ typedef size_type integer_type;
+
+ enum data_type
+ {
+ int_t,
+ string_t,
+ list_t,
+ dictionary_t,
+ undefined_t
+ };
+
+ data_type type() const;
+
+ entry(const dictionary_type&);
+ entry(const string_type&);
+ entry(const list_type&);
+ entry(const integer_type&);
+
+ entry();
+ entry(data_type t);
+ entry(const entry& e);
+ ~entry();
+
+ bool operator==(entry const& e) const;
+
+ void operator=(entry const&);
+ void operator=(dictionary_type const&);
+ void operator=(string_type const&);
+ void operator=(list_type const&);
+ void operator=(integer_type const&);
+
+ integer_type& integer();
+ const integer_type& integer() const;
+ string_type& string();
+ const string_type& string() const;
+ list_type& list();
+ const list_type& list() const;
+ dictionary_type& dict();
+ const dictionary_type& dict() const;
+
+ // these functions requires that the entry
+ // is a dictionary, otherwise they will throw
+ entry& operator[](char const* key);
+ entry& operator[](std::string const& key);
+ const entry& operator[](char const* key) const;
+ const entry& operator[](std::string const& key) const;
+ entry* find_key(char const* key);
+ entry const* find_key(char const* key) const;
+
+ void print(std::ostream& os, int indent = 0) const;
+
+ private:
+
+ void construct(data_type t);
+ void copy(const entry& e);
+ void destruct();
+
+ data_type m_type;
+
+#if defined(_MSC_VER) && _MSC_VER < 1310
+ // workaround for msvc-bug.
+ // assumes sizeof(map<string, char>) == sizeof(map<string, entry>)
+ // and sizeof(list<char>) == sizeof(list<entry>)
+ union
+ {
+ char data[
+ detail::max4<sizeof(std::list<char>)
+ , sizeof(std::map<std::string, char>)
+ , sizeof(string_type)
+ , sizeof(integer_type)>::value];
+ integer_type dummy_aligner;
+ };
+#else
+ union
+ {
+ char data[detail::max4<sizeof(list_type)
+ , sizeof(dictionary_type)
+ , sizeof(string_type)
+ , sizeof(integer_type)>::value];
+ integer_type dummy_aligner;
+ };
+#endif
+
+ };
+
+ inline std::ostream& operator<<(std::ostream& os, const entry& e)
+ {
+ e.print(os, 0);
+ return os;
+ }
+
+ inline entry::data_type entry::type() const { return m_type; }
+
+ inline entry::entry(): m_type(undefined_t) {}
+ inline entry::entry(data_type t): m_type(t) { construct(t); }
+ inline entry::entry(const entry& e) { copy(e); }
+ inline entry::~entry() { destruct(); }
+
+ inline void entry::operator=(const entry& e)
+ {
+ destruct();
+ copy(e);
+ }
+
+ inline entry::integer_type& entry::integer()
+ {
+ if (m_type != int_t) throw type_error("invalid type requested from entry");
+ return *reinterpret_cast<integer_type*>(data);
+ }
+
+ inline entry::integer_type const& entry::integer() const
+ {
+ if (m_type != int_t) throw type_error("invalid type requested from entry");
+ return *reinterpret_cast<const integer_type*>(data);
+ }
+
+ inline entry::string_type& entry::string()
+ {
+ if (m_type != string_t) throw type_error("invalid type requested from entry");
+ return *reinterpret_cast<string_type*>(data);
+ }
+
+ inline entry::string_type const& entry::string() const
+ {
+ if (m_type != string_t) throw type_error("invalid type requested from entry");
+ return *reinterpret_cast<const string_type*>(data);
+ }
+
+ inline entry::list_type& entry::list()
+ {
+ if (m_type != list_t) throw type_error("invalid type requested from entry");
+ return *reinterpret_cast<list_type*>(data);
+ }
+
+ inline entry::list_type const& entry::list() const
+ {
+ if (m_type != list_t) throw type_error("invalid type requested from entry");
+ return *reinterpret_cast<const list_type*>(data);
+ }
+
+ inline entry::dictionary_type& entry::dict()
+ {
+ if (m_type != dictionary_t) throw type_error("invalid type requested from entry");
+ return *reinterpret_cast<dictionary_type*>(data);
+ }
+
+ inline entry::dictionary_type const& entry::dict() const
+ {
+ if (m_type != dictionary_t) throw type_error("invalid type requested from entry");
+ return *reinterpret_cast<const dictionary_type*>(data);
+ }
+
+}
+
+#endif // TORRENT_ENTRY_HPP_INCLUDED
diff --git a/library/include/libtorrent/escape_string.hpp b/library/include/libtorrent/escape_string.hpp
new file mode 100755
index 000000000..e0e743e1e
--- /dev/null
+++ b/library/include/libtorrent/escape_string.hpp
@@ -0,0 +1,46 @@
+/*
+
+Copyright (c) 2003, 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_ESCAPE_STRING_HPP_INCLUDED
+#define TORRENT_ESCAPE_STRING_HPP_INCLUDED
+
+#include <string>
+#include "libtorrent/config.hpp"
+
+namespace libtorrent
+{
+ std::string TORRENT_EXPORT unescape_string(std::string const& s);
+ std::string TORRENT_EXPORT escape_string(const char* str, int len);
+ std::string TORRENT_EXPORT escape_path(const char* str, int len);
+}
+
+#endif // TORRENT_ESCAPE_STRING_HPP_INCLUDED
diff --git a/library/include/libtorrent/file.hpp b/library/include/libtorrent/file.hpp
new file mode 100755
index 000000000..1b71c66f5
--- /dev/null
+++ b/library/include/libtorrent/file.hpp
@@ -0,0 +1,130 @@
+/*
+
+Copyright (c) 2003, 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_FILE_HPP_INCLUDED
+#define TORRENT_FILE_HPP_INCLUDED
+
+#include <memory>
+#include <stdexcept>
+
+#ifdef _MSC_VER
+#pragma warning(push, 1)
+#endif
+
+#include <boost/noncopyable.hpp>
+#include <boost/filesystem/path.hpp>
+
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+#include "libtorrent/size_type.hpp"
+#include "libtorrent/config.hpp"
+
+namespace libtorrent
+{
+
+ struct TORRENT_EXPORT file_error: std::runtime_error
+ {
+ file_error(std::string const& msg): std::runtime_error(msg) {}
+ };
+
+ class TORRENT_EXPORT file: public boost::noncopyable
+ {
+ public:
+
+ class seek_mode
+ {
+ friend class file;
+ private:
+ seek_mode(int v): m_val(v) {}
+ int m_val;
+ };
+
+ static const seek_mode begin;
+ static const seek_mode end;
+
+ class open_mode
+ {
+ friend class file;
+ public:
+
+ open_mode(): m_mask(0) {}
+
+ open_mode operator|(open_mode m) const
+ { return open_mode(m.m_mask | m_mask); }
+
+ open_mode operator&(open_mode m) const
+ { return open_mode(m.m_mask & m_mask); }
+
+ open_mode operator|=(open_mode m)
+ {
+ m_mask |= m.m_mask;
+ return *this;
+ }
+
+ bool operator==(open_mode m) const { return m_mask == m.m_mask; }
+ bool operator!=(open_mode m) const { return m_mask != m.m_mask; }
+
+ private:
+
+ open_mode(int val): m_mask(val) {}
+ int m_mask;
+ };
+
+ static const open_mode in;
+ static const open_mode out;
+
+ file();
+ file(boost::filesystem::path const& p, open_mode m);
+ ~file();
+
+ void open(boost::filesystem::path const& p, open_mode m);
+ void close();
+
+ size_type write(const char*, size_type num_bytes);
+ size_type read(char*, size_type num_bytes);
+
+ size_type seek(size_type pos, seek_mode m = begin);
+ size_type tell();
+
+ private:
+
+ struct impl;
+ const std::auto_ptr<impl> m_impl;
+
+ };
+
+}
+
+#endif // TORRENT_FILE_HPP_INCLUDED
+
diff --git a/library/include/libtorrent/fingerprint.hpp b/library/include/libtorrent/fingerprint.hpp
new file mode 100755
index 000000000..d7e5a5fc6
--- /dev/null
+++ b/library/include/libtorrent/fingerprint.hpp
@@ -0,0 +1,93 @@
+/*
+
+Copyright (c) 2003, 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_FINGERPRINT_HPP_INCLUDED
+#define TORRENT_FINGERPRINT_HPP_INCLUDED
+
+#include <string>
+#include <sstream>
+
+#include "libtorrent/peer_id.hpp"
+
+namespace libtorrent
+{
+
+ struct fingerprint
+ {
+ fingerprint(const char* id_string, int major, int minor, int revision, int tag)
+ : major_version(major)
+ , minor_version(minor)
+ , revision_version(revision)
+ , tag_version(tag)
+ {
+ assert(id_string);
+ assert(major >= 0);
+ assert(minor >= 0);
+ assert(revision >= 0);
+ assert(tag >= 0);
+ assert(std::strlen(id_string) == 2);
+ name[0] = id_string[0];
+ name[1] = id_string[1];
+ }
+
+ std::string to_string() const
+ {
+ std::stringstream s;
+ s << "-" << name[0] << name[1]
+ << version_to_char(major_version)
+ << version_to_char(minor_version)
+ << version_to_char(revision_version)
+ << version_to_char(tag_version) << "-";
+ return s.str();
+ }
+
+ char name[2];
+ int major_version;
+ int minor_version;
+ int revision_version;
+ int tag_version;
+
+ private:
+
+ char version_to_char(int v) const
+ {
+ if (v >= 0 && v < 10) return '0' + v;
+ else if (v >= 10) return 'A' + (v - 10);
+ assert(false);
+ return '0';
+ }
+
+ };
+
+}
+
+#endif // TORRENT_FINGERPRINT_HPP_INCLUDED
diff --git a/library/include/libtorrent/hasher.hpp b/library/include/libtorrent/hasher.hpp
new file mode 100755
index 000000000..803b758eb
--- /dev/null
+++ b/library/include/libtorrent/hasher.hpp
@@ -0,0 +1,118 @@
+/*
+
+Copyright (c) 2003, 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_HASHER_HPP_INCLUDED
+#define TORRENT_HASHER_HPP_INCLUDED
+
+#include <cassert>
+#include <boost/cstdint.hpp>
+
+#include "libtorrent/peer_id.hpp"
+#include "libtorrent/config.hpp"
+
+// from sha1.cpp
+struct TORRENT_EXPORT SHA1_CTX
+{
+ boost::uint32_t state[5];
+ boost::uint32_t count[2];
+ boost::uint8_t buffer[64];
+};
+
+TORRENT_EXPORT void SHA1Init(SHA1_CTX* context);
+TORRENT_EXPORT void SHA1Update(SHA1_CTX* context, boost::uint8_t const* data, boost::uint32_t len);
+TORRENT_EXPORT void SHA1Final(SHA1_CTX* context, boost::uint8_t* digest);
+
+extern "C"
+{
+ // from zlib/adler32.c
+ unsigned long adler32(unsigned long adler, const char* data, unsigned int len);
+}
+
+namespace libtorrent
+{
+
+ class adler32_crc
+ {
+ public:
+ adler32_crc(): m_adler(adler32(0, 0, 0)) {}
+
+ void update(const char* data, int len)
+ {
+ assert(data != 0);
+ assert(len > 0);
+ m_adler = adler32(m_adler, data, len);
+ }
+ unsigned long final() const { return m_adler; }
+ void reset() { m_adler = adler32(0, 0, 0); }
+
+ private:
+
+ unsigned long m_adler;
+
+ };
+
+ class hasher
+ {
+ public:
+
+ hasher() { SHA1Init(&m_context); }
+ hasher(const char* data, int len)
+ {
+ SHA1Init(&m_context);
+ assert(data != 0);
+ assert(len > 0);
+ SHA1Update(&m_context, reinterpret_cast<unsigned char const*>(data), len);
+ }
+ void update(const char* data, int len)
+ {
+ assert(data != 0);
+ assert(len > 0);
+ SHA1Update(&m_context, reinterpret_cast<unsigned char const*>(data), len);
+ }
+
+ sha1_hash final()
+ {
+ sha1_hash digest;
+ SHA1Final(&m_context, digest.begin());
+ return digest;
+ }
+
+ void reset() { SHA1Init(&m_context); }
+
+ private:
+
+ SHA1_CTX m_context;
+
+ };
+}
+
+#endif // TORRENT_HASHER_HPP_INCLUDED
diff --git a/library/include/libtorrent/http_tracker_connection.hpp b/library/include/libtorrent/http_tracker_connection.hpp
new file mode 100755
index 000000000..b66d4349d
--- /dev/null
+++ b/library/include/libtorrent/http_tracker_connection.hpp
@@ -0,0 +1,176 @@
+/*
+
+Copyright (c) 2003, 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_TRACKER_CONNECTION_HPP_INCLUDED
+#define TORRENT_HTTP_TRACKER_CONNECTION_HPP_INCLUDED
+
+#include <vector>
+#include <string>
+#include <utility>
+#include <ctime>
+
+#ifdef _MSC_VER
+#pragma warning(push, 1)
+#endif
+
+#include <boost/shared_ptr.hpp>
+#include <boost/date_time/posix_time/posix_time.hpp>
+#include <boost/cstdint.hpp>
+#include <boost/tuple/tuple.hpp>
+
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+#include "libtorrent/socket.hpp"
+#include "libtorrent/entry.hpp"
+#include "libtorrent/session_settings.hpp"
+#include "libtorrent/peer_id.hpp"
+#include "libtorrent/peer.hpp"
+#include "libtorrent/tracker_manager.hpp"
+#include "libtorrent/config.hpp"
+#include "libtorrent/buffer.hpp"
+
+namespace libtorrent
+{
+
+ class http_parser
+ {
+ public:
+ http_parser();
+ template <class T>
+ T header(char const* key) const;
+ std::string const& protocol() const { return m_protocol; }
+ int status_code() const { return m_status_code; }
+ std::string message() const { return m_server_message; }
+ buffer::const_interval get_body();
+ bool header_finished() const { return m_state == read_body; }
+ bool finished() const { return m_finished; }
+ boost::tuple<int, int> incoming(buffer::const_interval recv_buffer);
+ int body_start() const { return m_body_start_pos; }
+ private:
+ int m_recv_pos;
+ int m_status_code;
+ std::string m_protocol;
+ std::string m_server_message;
+
+ int m_content_length;
+ enum { plain, gzip } m_content_encoding;
+
+ enum { read_status, read_header, read_body } m_state;
+
+ std::map<std::string, std::string> m_header;
+ buffer::const_interval m_recv_buffer;
+ int m_body_start_pos;
+
+ bool m_finished;
+ };
+
+ template <class T>
+ T http_parser::header(char const* key) const
+ {
+ std::map<std::string, std::string>::const_iterator i
+ = m_header.find(key);
+ if (i == m_header.end()) return T();
+ return boost::lexical_cast<T>(i->second);
+ }
+
+ class TORRENT_EXPORT http_tracker_connection
+ : public tracker_connection
+ {
+ friend class tracker_manager;
+ public:
+
+ http_tracker_connection(
+ demuxer& d
+ , tracker_manager& man
+ , tracker_request const& req
+ , std::string const& hostname
+ , unsigned short port
+ , std::string request
+ , boost::weak_ptr<request_callback> c
+ , session_settings const& stn
+ , std::string const& password = "");
+
+ private:
+
+ boost::intrusive_ptr<http_tracker_connection> self()
+ { return boost::intrusive_ptr<http_tracker_connection>(this); }
+
+ void on_response();
+
+ void init_send_buffer(
+ std::string const& hostname
+ , std::string const& request);
+
+ void name_lookup(asio::error const& error, tcp::resolver::iterator i);
+ void connected(asio::error const& error);
+ void sent(asio::error const& error);
+ void receive(asio::error const& error
+ , std::size_t bytes_transferred);
+
+ virtual void on_timeout();
+
+ void parse(const entry& e);
+ peer_entry extract_peer_info(const entry& e);
+
+ tracker_manager& m_man;
+ enum { read_status, read_header, read_body } m_state;
+
+ enum { plain, gzip } m_content_encoding;
+ int m_content_length;
+ std::string m_location;
+
+ tcp::resolver m_name_lookup;
+ int m_port;
+ boost::shared_ptr<stream_socket> m_socket;
+ int m_recv_pos;
+ std::vector<char> m_buffer;
+ std::string m_send_buffer;
+
+ std::string m_server_message;
+ std::string m_server_protocol;
+
+ session_settings const& m_settings;
+ std::string m_password;
+ int m_code;
+
+ // server string in http-reply
+ std::string m_server;
+
+ bool m_timed_out;
+ };
+
+}
+
+#endif // TORRENT_HTTP_TRACKER_CONNECTION_HPP_INCLUDED
+
diff --git a/library/include/libtorrent/identify_client.hpp b/library/include/libtorrent/identify_client.hpp
new file mode 100755
index 000000000..e8cb3b930
--- /dev/null
+++ b/library/include/libtorrent/identify_client.hpp
@@ -0,0 +1,58 @@
+/*
+
+Copyright (c) 2003, 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_IDENTIFY_CLIENT_HPP_INCLUDED
+#define TORRENT_IDENTIFY_CLIENT_HPP_INCLUDED
+
+#ifdef _MSC_VER
+#pragma warning(push, 1)
+#endif
+
+#include <boost/optional.hpp>
+
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+#include "libtorrent/peer_id.hpp"
+#include "libtorrent/fingerprint.hpp"
+#include "libtorrent/config.hpp"
+
+namespace libtorrent
+{
+
+ TORRENT_EXPORT std::string identify_client(const peer_id& p);
+ TORRENT_EXPORT boost::optional<fingerprint> client_fingerprint(peer_id const& p);
+
+}
+
+#endif // TORRENT_IDENTIFY_CLIENT_HPP_INCLUDED
diff --git a/library/include/libtorrent/invariant_check.hpp b/library/include/libtorrent/invariant_check.hpp
new file mode 100755
index 000000000..c6eacf338
--- /dev/null
+++ b/library/include/libtorrent/invariant_check.hpp
@@ -0,0 +1,78 @@
+// Copyright Daniel Wallin 2004. Use, modification and distribution is
+// subject to the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef TORRENT_INVARIANT_ACCESS_HPP_INCLUDED
+#define TORRENT_INVARIANT_ACCESS_HPP_INCLUDED
+
+#include <cassert>
+
+namespace libtorrent
+{
+
+ class invariant_access
+ {
+ public:
+ template<class T>
+ static void check_invariant(T const& self)
+ {
+ self.check_invariant();
+ }
+ };
+
+ template<class T>
+ void check_invariant(T const& x)
+ {
+ invariant_access::check_invariant(x);
+ }
+
+ struct invariant_checker {};
+
+ template<class T>
+ struct invariant_checker_impl : invariant_checker
+ {
+ invariant_checker_impl(T const& self_)
+ : self(self_)
+ {
+ try
+ {
+ check_invariant(self);
+ }
+ catch (...)
+ {
+ assert(false);
+ }
+ }
+
+ ~invariant_checker_impl()
+ {
+ try
+ {
+ check_invariant(self);
+ }
+ catch (...)
+ {
+ assert(false);
+ }
+ }
+
+ T const& self;
+ };
+
+ template<class T>
+ invariant_checker_impl<T> make_invariant_checker(T const& x)
+ {
+ return invariant_checker_impl<T>(x);
+ }
+}
+
+#ifndef NDEBUG
+#define INVARIANT_CHECK \
+ invariant_checker const& _invariant_check = make_invariant_checker(*this); \
+ (void)_invariant_check; \
+ do {} while (false)
+#else
+#define INVARIANT_CHECK do {} while (false)
+#endif
+
+#endif // TORRENT_INVARIANT_ACCESS_HPP_INCLUDED
diff --git a/library/include/libtorrent/io.hpp b/library/include/libtorrent/io.hpp
new file mode 100755
index 000000000..57a22cf97
--- /dev/null
+++ b/library/include/libtorrent/io.hpp
@@ -0,0 +1,140 @@
+/*
+
+Copyright (c) 2003, 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_IO_HPP_INCLUDED
+#define TORRENT_IO_HPP_INCLUDED
+
+#include <boost/cstdint.hpp>
+
+namespace libtorrent
+{
+ namespace detail
+ {
+ template <class T> struct type {};
+
+ // reads an integer from a byte stream
+ // in big endian byte order and converts
+ // it to native endianess
+ template <class T, class InIt>
+ inline T read_impl(InIt& start, type<T>)
+ {
+ T ret = 0;
+ for (int i = 0; i < (int)sizeof(T); ++i)
+ {
+ ret <<= 8;
+ ret |= static_cast<unsigned char>(*start);
+ ++start;
+ }
+ return ret;
+ }
+
+ template <class T, class OutIt>
+ inline void write_impl(T val, OutIt& start)
+ {
+ for (int i = (int)sizeof(T)-1; i >= 0; --i)
+ {
+ *start = static_cast<unsigned char>((val >> (i * 8)) & 0xff);
+ ++start;
+ }
+ }
+
+ // -- adaptors
+
+ template <class InIt>
+ boost::int64_t read_int64(InIt& start)
+ { return read_impl(start, type<boost::int64_t>()); }
+
+ template <class InIt>
+ boost::uint64_t read_uint64(InIt& start)
+ { return read_impl(start, type<boost::uint64_t>()); }
+
+ template <class InIt>
+ boost::uint32_t read_uint32(InIt& start)
+ { return read_impl(start, type<boost::uint32_t>()); }
+
+ template <class InIt>
+ boost::int32_t read_int32(InIt& start)
+ { return read_impl(start, type<boost::int32_t>()); }
+
+ template <class InIt>
+ boost::int16_t read_int16(InIt& start)
+ { return read_impl(start, type<boost::int16_t>()); }
+
+ template <class InIt>
+ boost::uint16_t read_uint16(InIt& start)
+ { return read_impl(start, type<boost::uint16_t>()); }
+
+ template <class InIt>
+ boost::int8_t read_int8(InIt& start)
+ { return read_impl(start, type<boost::int8_t>()); }
+
+ template <class InIt>
+ boost::uint8_t read_uint8(InIt& start)
+ { return read_impl(start, type<boost::uint8_t>()); }
+
+
+ template <class OutIt>
+ void write_uint64(boost::uint64_t val, OutIt& start)
+ { write_impl(val, start); }
+
+ template <class OutIt>
+ void write_int64(boost::int64_t val, OutIt& start)
+ { write_impl(val, start); }
+
+ template <class OutIt>
+ void write_uint32(boost::uint32_t val, OutIt& start)
+ { write_impl(val, start); }
+
+ template <class OutIt>
+ void write_int32(boost::int32_t val, OutIt& start)
+ { write_impl(val, start); }
+
+ template <class OutIt>
+ void write_uint16(boost::uint16_t val, OutIt& start)
+ { write_impl(val, start); }
+
+ template <class OutIt>
+ void write_int16(boost::int16_t val, OutIt& start)
+ { write_impl(val, start); }
+
+ template <class OutIt>
+ void write_uint8(boost::uint8_t val, OutIt& start)
+ { write_impl(val, start); }
+
+ template <class OutIt>
+ void write_int8(boost::int8_t val, OutIt& start)
+ { write_impl(val, start); }
+
+ }
+}
+
+#endif // TORRENT_IO_HPP_INCLUDED
diff --git a/library/include/libtorrent/ip_filter.hpp b/library/include/libtorrent/ip_filter.hpp
new file mode 100644
index 000000000..c75dafd66
--- /dev/null
+++ b/library/include/libtorrent/ip_filter.hpp
@@ -0,0 +1,276 @@
+/*
+
+Copyright (c) 2005, 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_IP_FILTER_HPP
+#define TORRENT_IP_FILTER_HPP
+
+#ifdef _MSC_VER
+#pragma warning(push, 1)
+#endif
+
+#include <boost/limits.hpp>
+#include <boost/utility.hpp>
+#include <boost/tuple/tuple.hpp>
+
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+
+#include "libtorrent/config.hpp"
+#include "libtorrent/socket.hpp"
+#include <set>
+#include <iostream>
+
+namespace libtorrent
+{
+
+inline bool operator<=(address const& lhs
+ , address const& rhs)
+{
+ return lhs < rhs || lhs == rhs;
+}
+
+template <class Addr>
+struct ip_range
+{
+ Addr first;
+ Addr last;
+ int flags;
+};
+
+namespace detail
+{
+
+ // this is the generic implementation of
+ // a filter for a specific address type.
+ // it works with IPv4 and IPv6
+ template<class Addr>
+ 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));
+ }
+
+ void add_rule(Addr first, Addr last, int flags)
+ {
+ using boost::next;
+ using boost::prior;
+
+ assert(!m_access_list.empty());
+ assert(first < last || first == last);
+
+ typename range_t::iterator i = m_access_list.upper_bound(first);
+ typename range_t::iterator j = m_access_list.upper_bound(last);
+
+ if (i != m_access_list.begin()) --i;
+
+ assert(j != m_access_list.begin());
+ assert(j != i);
+
+ int first_access = i->access;
+ int last_access = prior(j)->access;
+
+ if (i->start != first && first_access != flags)
+ {
+ i = m_access_list.insert(i, range(first, flags));
+ }
+ else if (i != m_access_list.begin() && prior(i)->access == flags)
+ {
+ --i;
+ first_access = i->access;
+ }
+ assert(!m_access_list.empty());
+ assert(i != m_access_list.end());
+
+ if (i != j) m_access_list.erase(next(i), j);
+ if (i->start == first)
+ {
+ // we can do this const-cast because we know that the new
+ // start address will keep the set correctly ordered
+ const_cast<Addr&>(i->start) = first;
+ const_cast<int&>(i->access) = flags;
+ }
+ else if (first_access != flags)
+ {
+ m_access_list.insert(i, range(first, flags));
+ }
+
+ if ((j != m_access_list.end()
+ && minus_one(j->start) != last)
+ || (j == m_access_list.end()
+ && last != max_addr()))
+ {
+ assert(j == m_access_list.end() || last < minus_one(j->start));
+ if (last_access != flags)
+ j = m_access_list.insert(j, range(plus_one(last), last_access));
+ }
+
+ if (j != m_access_list.end() && j->access == flags) m_access_list.erase(j);
+ assert(!m_access_list.empty());
+ }
+
+ int access(Addr const& addr) const
+ {
+ assert(!m_access_list.empty());
+ typename range_t::const_iterator i = m_access_list.upper_bound(addr);
+ if (i != m_access_list.begin()) --i;
+ assert(i != m_access_list.end());
+ assert(i->start <= addr && (boost::next(i) == m_access_list.end()
+ || addr < boost::next(i)->start));
+ return i->access;
+ }
+
+ std::vector<ip_range<Addr> > export_filter() const
+ {
+ std::vector<ip_range<Addr> > ret;
+ ret.reserve(m_access_list.size());
+
+ for (typename range_t::const_iterator i = m_access_list.begin()
+ , end(m_access_list.end()); i != end;)
+ {
+ ip_range<Addr> r;
+ r.first = i->start;
+ r.flags = i->access;
+
+ ++i;
+ if (i == end)
+ r.last = max_addr();
+ else
+ r.last = minus_one(i->start);
+
+ ret.push_back(r);
+ }
+ return ret;
+ }
+
+ 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) {}
+ bool operator<(range const& r) const
+ { return start < r.start; }
+ bool operator<(Addr const& a) const
+ { return start < a; }
+ Addr start;
+ // the end of the range is implicit
+ // and given by the next entry in the set
+ int access;
+ };
+
+ typedef std::set<range> range_t;
+ range_t m_access_list;
+
+ };
+
+}
+
+class TORRENT_EXPORT ip_filter
+{
+public:
+
+ enum access_flags
+ {
+ blocked = 1
+ };
+
+ // both addresses MUST be of the same type (i.e. both must
+ // be either IPv4 or both must be IPv6)
+ void add_rule(address first, address last, int flags);
+ int access(address const& addr) const;
+
+ typedef boost::tuple<std::vector<ip_range<address_v4> >
+ , std::vector<ip_range<address_v6> > > filter_tuple_t;
+
+ filter_tuple_t export_filter() const;
+
+// void print() const;
+
+private:
+
+ detail::filter_impl<address_v4> m_filter4;
+ detail::filter_impl<address_v6> m_filter6;
+};
+
+}
+
+#endif
+
diff --git a/library/include/libtorrent/kademlia/closest_nodes.hpp b/library/include/libtorrent/kademlia/closest_nodes.hpp
new file mode 100644
index 000000000..d5580b9c9
--- /dev/null
+++ b/library/include/libtorrent/kademlia/closest_nodes.hpp
@@ -0,0 +1,86 @@
+/*
+
+Copyright (c) 2006, Arvid Norberg & Daniel Wallin
+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 CLOSEST_NODES_050323_HPP
+#define CLOSEST_NODES_050323_HPP
+
+#include <vector>
+
+#include <libtorrent/kademlia/traversal_algorithm.hpp>
+#include <libtorrent/kademlia/node_id.hpp>
+#include <libtorrent/kademlia/routing_table.hpp>
+
+#include <boost/function.hpp>
+
+namespace libtorrent { namespace dht
+{
+
+class rpc_manager;
+
+// -------- closest nodes -----------
+
+class closest_nodes : public traversal_algorithm
+{
+public:
+ typedef boost::function<
+ void(std::vector<node_entry> const&)
+ > done_callback;
+
+ static void initiate(
+ node_id target
+ , int branch_factor
+ , int max_results
+ , routing_table& table
+ , rpc_manager& rpc
+ , done_callback const& callback
+ );
+
+private:
+ void done();
+ void invoke(node_id const& id, asio::ip::udp::endpoint addr);
+
+ closest_nodes(
+ node_id target
+ , int branch_factor
+ , int max_results
+ , routing_table& table
+ , rpc_manager& rpc
+ , done_callback const& callback
+ );
+
+ done_callback m_done_callback;
+};
+
+} } // namespace libtorrent::dht
+
+#endif // CLOSEST_NODES_050323_HPP
+
diff --git a/library/include/libtorrent/kademlia/dht_tracker.hpp b/library/include/libtorrent/kademlia/dht_tracker.hpp
new file mode 100644
index 000000000..d447d4b23
--- /dev/null
+++ b/library/include/libtorrent/kademlia/dht_tracker.hpp
@@ -0,0 +1,144 @@
+/*
+
+Copyright (c) 2006, 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_DISABLE_DHT
+
+#ifndef TORRENT_DHT_TRACKER
+#define TORRENT_DHT_TRACKER
+
+#include <fstream>
+#include <set>
+#include <numeric>
+#include <boost/bind.hpp>
+#include <boost/date_time/posix_time/posix_time_types.hpp>
+#include <boost/date_time/posix_time/ptime.hpp>
+#include <boost/ref.hpp>
+#include <boost/optional.hpp>
+#include <boost/lexical_cast.hpp>
+#include <boost/filesystem/operations.hpp>
+
+#include "libtorrent/kademlia/node.hpp"
+#include "libtorrent/kademlia/node_id.hpp"
+#include "libtorrent/kademlia/traversal_algorithm.hpp"
+#include "libtorrent/kademlia/packet_iterator.hpp"
+#include "libtorrent/session_settings.hpp"
+#include "libtorrent/session_status.hpp"
+
+namespace libtorrent { namespace dht
+{
+
+#ifdef TORRENT_DHT_VERBOSE_LOGGING
+ TORRENT_DECLARE_LOG(dht_tracker);
+#endif
+
+ struct dht_tracker
+ {
+ dht_tracker(asio::io_service& d, dht_settings const& settings
+ , asio::ip::address listen_interface, entry const& bootstrap);
+
+ void add_node(udp::endpoint node);
+ void add_node(std::pair<std::string, int> const& node);
+ void add_router_node(std::pair<std::string, int> const& node);
+
+ void rebind(asio::ip::address listen_interface, int listen_port);
+
+ entry state() const;
+
+ void announce(sha1_hash const& ih, int listen_port
+ , boost::function<void(std::vector<tcp::endpoint> const&
+ , sha1_hash const&)> f);
+
+ void dht_status(session_status& s);
+
+ private:
+
+ void on_name_lookup(asio::error const& e
+ , udp::resolver::iterator host);
+ void on_router_name_lookup(asio::error const& e
+ , udp::resolver::iterator host);
+ void connection_timeout(asio::error const& e);
+ void refresh_timeout(asio::error const& e);
+ void tick(asio::error const& e);
+
+ // translate bittorrent kademlia message into the generice kademlia message
+ // used by the library
+ void on_receive(asio::error const& error, size_t bytes_transferred);
+ void on_bootstrap();
+ void send_packet(msg const& m);
+
+ asio::io_service& m_demuxer;
+ asio::ip::udp::socket m_socket;
+
+ node_impl m_dht;
+
+ // this is the index of the receive buffer we are currently receiving to
+ // the other buffer is the one containing the last message
+ int m_buffer;
+ std::vector<char> m_in_buf[2];
+ udp::endpoint m_remote_endpoint[2];
+ std::vector<char> m_send_buf;
+
+ boost::posix_time::ptime m_last_refresh;
+ deadline_timer m_timer;
+ deadline_timer m_connection_timer;
+ deadline_timer m_refresh_timer;
+ dht_settings const& m_settings;
+ int m_refresh_bucket;
+
+ // used to resolve hostnames for nodes
+ udp::resolver m_host_resolver;
+
+#ifdef TORRENT_DHT_VERBOSE_LOGGING
+ int m_replies_sent[5];
+ int m_queries_received[5];
+ int m_replies_bytes_sent[5];
+ int m_queries_bytes_received[5];
+ int m_counter;
+ int m_announces;
+ int m_failed_announces;
+
+ int m_total_message_input;
+ int m_ut_message_input;
+ int m_lt_message_input;
+ int m_mp_message_input;
+ int m_gr_message_input;
+
+ int m_total_in_bytes;
+ int m_total_out_bytes;
+
+ int m_queries_out_bytes;
+#endif
+ };
+}}
+
+#endif
+#endif
diff --git a/library/include/libtorrent/kademlia/find_data.hpp b/library/include/libtorrent/kademlia/find_data.hpp
new file mode 100644
index 000000000..bbafcdd77
--- /dev/null
+++ b/library/include/libtorrent/kademlia/find_data.hpp
@@ -0,0 +1,95 @@
+/*
+
+Copyright (c) 2006, Arvid Norberg & Daniel Wallin
+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 FIND_DATA_050323_HPP
+#define FIND_DATA_050323_HPP
+
+#include <vector>
+
+#include <libtorrent/kademlia/traversal_algorithm.hpp>
+#include <libtorrent/kademlia/node_id.hpp>
+#include <libtorrent/kademlia/routing_table.hpp>
+#include <libtorrent/kademlia/rpc_manager.hpp>
+#include <libtorrent/kademlia/packet_iterator.hpp>
+#include <boost/optional.hpp>
+
+#include <boost/function.hpp>
+
+namespace libtorrent { namespace dht
+{
+
+using asio::ip::udp;
+
+typedef std::vector<char> packet_t;
+
+class rpc_manager;
+
+// -------- find data -----------
+
+class find_data : public traversal_algorithm
+{
+public:
+ typedef boost::function<void(msg const*)> done_callback;
+
+ static void initiate(
+ node_id target
+ , int branch_factor
+ , int max_results
+ , routing_table& table
+ , rpc_manager& rpc
+ , done_callback const& callback
+ );
+
+ void got_data(msg const* m);
+
+private:
+ void done();
+ void invoke(node_id const& id, udp::endpoint addr);
+
+ find_data(
+ node_id target
+ , int branch_factor
+ , int max_results
+ , routing_table& table
+ , rpc_manager& rpc
+ , done_callback const& callback
+ );
+
+ done_callback m_done_callback;
+ boost::shared_ptr<packet_t> m_packet;
+ bool m_done;
+};
+
+} } // namespace libtorrent::dht
+
+#endif // FIND_DATA_050323_HPP
+
diff --git a/library/include/libtorrent/kademlia/logging.hpp b/library/include/libtorrent/kademlia/logging.hpp
new file mode 100644
index 000000000..8bd488f1a
--- /dev/null
+++ b/library/include/libtorrent/kademlia/logging.hpp
@@ -0,0 +1,146 @@
+/*
+
+Copyright (c) 2006, Arvid Norberg & Daniel Wallin
+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_LOGGING_HPP
+#define TORRENT_LOGGING_HPP
+
+#include <iostream>
+#include <fstream>
+
+namespace libtorrent { namespace dht
+{
+
+class log
+{
+public:
+ log(char const* id, std::ostream& stream)
+ : m_id(id)
+ , m_enabled(true)
+ , m_stream(stream)
+ {
+ }
+
+ char const* id() const
+ {
+ return m_id;
+ }
+
+ bool enabled() const
+ {
+ return m_enabled;
+ }
+
+ void enable(bool e)
+ {
+ m_enabled = e;
+ }
+
+ void flush() { m_stream.flush(); }
+
+ template<class T>
+ log& operator<<(T const& x)
+ {
+ m_stream << x;
+ return *this;
+ }
+
+private:
+ char const* m_id;
+ bool m_enabled;
+ std::ostream& m_stream;
+};
+
+class log_event
+{
+public:
+ log_event(log& log)
+ : log_(log)
+ {
+ if (log_.enabled())
+ log_ << '[' << log.id() << "] ";
+ }
+
+ ~log_event()
+ {
+ if (log_.enabled())
+ {
+ log_ << "\n";
+ log_.flush();
+ }
+ }
+
+ template<class T>
+ log_event& operator<<(T const& x)
+ {
+ log_ << x;
+ return *this;
+ }
+
+ operator bool() const
+ {
+ return log_.enabled();
+ }
+
+private:
+ log& log_;
+};
+
+class inverted_log_event : public log_event
+{
+public:
+ inverted_log_event(log& log) : log_event(log) {}
+
+ operator bool() const
+ {
+ return !log_event::operator bool();
+ }
+};
+
+} } // namespace libtorrent::dht
+
+#define TORRENT_DECLARE_LOG(name) \
+ libtorrent::dht::log& name ## _log()
+
+#define TORRENT_DEFINE_LOG(name) \
+ libtorrent::dht::log& name ## _log() \
+ { \
+ static std::ofstream log_file("libtorrent_logs/dht.log", std::ios::app); \
+ static libtorrent::dht::log instance(#name, log_file); \
+ return instance; \
+ }
+
+#define TORRENT_LOG(name) \
+ if (libtorrent::dht::inverted_log_event event_object__ = name ## _log()); \
+ else static_cast<log_event&>(event_object__)
+
+#endif
+
diff --git a/library/include/libtorrent/kademlia/node.hpp b/library/include/libtorrent/kademlia/node.hpp
new file mode 100644
index 000000000..c44a21f33
--- /dev/null
+++ b/library/include/libtorrent/kademlia/node.hpp
@@ -0,0 +1,185 @@
+/*
+
+Copyright (c) 2006, 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 NODE_HPP
+#define NODE_HPP
+
+#include <algorithm>
+#include <cassert>
+#include <map>
+#include <set>
+
+#include <libtorrent/kademlia/routing_table.hpp>
+#include <libtorrent/kademlia/rpc_manager.hpp>
+#include <libtorrent/kademlia/node_id.hpp>
+
+#include <libtorrent/io.hpp>
+#include <libtorrent/session_settings.hpp>
+
+#include <boost/cstdint.hpp>
+#include <boost/optional.hpp>
+#include <boost/date_time/posix_time/ptime.hpp>
+#include <boost/date_time/posix_time/posix_time_types.hpp>
+#include <boost/iterator/transform_iterator.hpp>
+#include <boost/ref.hpp>
+
+namespace libtorrent { namespace dht
+{
+
+using asio::ip::udp;
+
+#ifdef TORRENT_DHT_VERBOSE_LOGGING
+TORRENT_DECLARE_LOG(node);
+#endif
+
+// this is the entry for every peer
+// the timestamp is there to make it possible
+// to remove stale peers
+struct peer_entry
+{
+ tcp::endpoint addr;
+ boost::posix_time::ptime added;
+};
+
+// this is a group. It contains a set of group members
+struct torrent_entry
+{
+ std::set<peer_entry> peers;
+};
+
+inline bool operator<(peer_entry const& lhs, peer_entry const& rhs)
+{
+ return lhs.addr.address() == rhs.addr.address()
+ ? lhs.addr.port() < rhs.addr.port()
+ : lhs.addr.address() < rhs.addr.address();
+}
+
+struct null_type {};
+
+class node_impl : boost::noncopyable
+{
+typedef std::map<node_id, torrent_entry> table_t;
+public:
+ node_impl(boost::function<void(msg const&)> const& f
+ , dht_settings const& settings, boost::optional<node_id> node_id);
+
+ virtual ~node_impl() {}
+
+ void refresh(node_id const& id, boost::function0<void> f);
+ void bootstrap(std::vector<udp::endpoint> const& nodes
+ , boost::function0<void> f);
+ void find_node(node_id const& id, boost::function<
+ void(std::vector<node_entry> const&)> f);
+ void add_router_node(udp::endpoint router);
+
+ void incoming(msg const& m);
+
+ void refresh();
+ void refresh_bucket(int bucket);
+ int bucket_size(int bucket);
+
+ typedef routing_table::iterator iterator;
+
+ iterator begin() const { return m_table.begin(); }
+ iterator end() const { return m_table.end(); }
+
+ typedef table_t::iterator data_iterator;
+
+ node_id const& nid() const { return m_id; }
+ boost::tuple<int, int> size() const{ return m_table.size(); }
+
+ data_iterator begin_data() { return m_map.begin(); }
+ data_iterator end_data() { return m_map.end(); }
+ int data_size() const { return int(m_map.size()); }
+
+ void print_state(std::ostream& os) const
+ { m_table.print_state(os); }
+
+ void announce(sha1_hash const& info_hash, int listen_port
+ , boost::function<void(std::vector<tcp::endpoint> const&
+ , sha1_hash const&)> f);
+
+ bool verify_token(msg const& m);
+ entry generate_token(msg const& m);
+
+ // the returned time is the delay until connection_timeout()
+ // should be called again the next time
+ boost::posix_time::time_duration connection_timeout();
+ boost::posix_time::time_duration refresh_timeout();
+
+ // generates a new secret number used to generate write tokens
+ void new_write_key();
+
+ // pings the given node, and adds it to
+ // the routing table if it respons and if the
+ // bucket is not full.
+ void add_node(udp::endpoint node);
+
+ void replacement_cache(bucket_t& nodes) const
+ { m_table.replacement_cache(nodes); }
+
+protected:
+ // is called when a find data request is received. Should
+ // return false if the data is not stored on this node. If
+ // the data is stored, it should be serialized into 'data'.
+ bool on_find(msg const& m, std::vector<tcp::endpoint>& peers) const;
+
+ // this is called when a store request is received. The data
+ // is store-parameters and the data to be stored.
+ void on_announce(msg const& m, msg& reply);
+
+ dht_settings const& m_settings;
+
+ // the maximum number of peers to send in a get_peers
+ // reply. Ordinary trackers usually limit this to 50.
+ // 50 => 6 * 50 = 250 bytes + packet overhead
+ int m_max_peers_reply;
+
+private:
+ void incoming_request(msg const& h);
+
+ node_id m_id;
+ routing_table m_table;
+ rpc_manager m_rpc;
+ table_t m_map;
+
+ boost::posix_time::ptime m_last_tracker_tick;
+
+ // secret random numbers used to create write tokens
+ int m_secret[2];
+};
+
+
+} } // namespace libtorrent::dht
+
+#endif // NODE_HPP
+
diff --git a/library/include/libtorrent/kademlia/node_entry.hpp b/library/include/libtorrent/kademlia/node_entry.hpp
new file mode 100644
index 000000000..edc5dff80
--- /dev/null
+++ b/library/include/libtorrent/kademlia/node_entry.hpp
@@ -0,0 +1,63 @@
+/*
+
+Copyright (c) 2006, 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 KADEMLIA_NODE_ENTRY_HPP
+#define KADEMLIA_NODE_ENTRY_HPP
+
+#include "libtorrent/kademlia/node_id.hpp"
+#include "libtorrent/socket.hpp"
+
+namespace libtorrent { namespace dht
+{
+
+struct node_entry
+{
+ node_entry(node_id const& id_, asio::ip::udp::endpoint addr_)
+ : id(id_)
+ , addr(addr_)
+ , fail_count(0) {}
+ node_entry(asio::ip::udp::endpoint addr_)
+ : id(0)
+ , addr(addr_)
+ , fail_count(0) {}
+
+ node_id id;
+ udp::endpoint addr;
+ // the number of times this node has failed to
+ // respond in a row
+ int fail_count;
+};
+
+} } // namespace libtorrent::dht
+
+#endif
+
diff --git a/library/include/libtorrent/kademlia/node_id.hpp b/library/include/libtorrent/kademlia/node_id.hpp
new file mode 100644
index 000000000..eb4d6c539
--- /dev/null
+++ b/library/include/libtorrent/kademlia/node_id.hpp
@@ -0,0 +1,60 @@
+/*
+
+Copyright (c) 2006, 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 NODE_ID_HPP
+#define NODE_ID_HPP
+
+#include <algorithm>
+#include <cassert>
+
+#include <boost/cstdint.hpp>
+#include "libtorrent/peer_id.hpp"
+
+namespace libtorrent { namespace dht
+{
+
+typedef libtorrent::big_number node_id;
+
+// returns the distance between the two nodes
+// using the kademlia XOR-metric
+node_id distance(node_id const& n1, node_id const& n2);
+
+// returns true if: distance(n1, ref) < distance(n2, ref)
+bool compare_ref(node_id const& n1, node_id const& n2, node_id const& ref);
+
+// returns n in: 2^n <= distance(n1, n2) < 2^(n+1)
+// usefult for finding out which bucket a node belongs to
+int distance_exp(node_id const& n1, node_id const& n2);
+
+} } // namespace libtorrent::dht
+
+#endif // NODE_ID_HPP
+
diff --git a/library/include/libtorrent/kademlia/packet_iterator.hpp b/library/include/libtorrent/kademlia/packet_iterator.hpp
new file mode 100644
index 000000000..e906a90bf
--- /dev/null
+++ b/library/include/libtorrent/kademlia/packet_iterator.hpp
@@ -0,0 +1,95 @@
+/*
+
+Copyright (c) 2006, 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 PACKET_ITERATOR_HPP
+#define PACKET_ITERATOR_HPP
+
+#include <boost/iterator/iterator_facade.hpp>
+#include <vector>
+#include <stdexcept>
+
+namespace libtorrent { namespace dht
+{
+
+class packet_iterator: public boost::iterator_facade<
+ packet_iterator, const char, boost::forward_traversal_tag>
+{
+public:
+ typedef std::vector<char>::const_iterator base_iterator;
+
+ packet_iterator() {}
+
+ packet_iterator(std::vector<char>::const_iterator start
+ , std::vector<char>::const_iterator end
+ , std::string const& error_msg = "")
+ : m_base(start)
+ , m_end(end)
+ , m_msg(error_msg)
+ {}
+
+ base_iterator base() const
+ { return m_base; }
+
+ base_iterator end() const
+ { return m_end; }
+
+ int left() const { return int(m_end - m_base); }
+
+private:
+ friend class boost::iterator_core_access;
+
+ bool equal(packet_iterator const& other) const
+ { return m_base == other.m_base; }
+
+ void advance(int n)
+ {
+ m_base += n;
+ }
+
+ void increment()
+ { ++m_base; }
+
+ char const& dereference() const
+ {
+ if (m_base == m_end) throw std::runtime_error(m_msg);
+ return *m_base;
+ }
+
+ base_iterator m_base;
+ base_iterator m_end;
+ std::string m_msg;
+};
+
+} } // namespace libtorrent::dht
+
+#endif // PACKET_ITERATOR_HPP
+
diff --git a/library/include/libtorrent/kademlia/refresh.hpp b/library/include/libtorrent/kademlia/refresh.hpp
new file mode 100644
index 000000000..f47b80462
--- /dev/null
+++ b/library/include/libtorrent/kademlia/refresh.hpp
@@ -0,0 +1,159 @@
+/*
+
+Copyright (c) 2006, Arvid Norberg & Daniel Wallin
+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 REFRESH_050324_HPP
+#define REFRESH_050324_HPP
+
+#include <vector>
+
+#include <libtorrent/kademlia/traversal_algorithm.hpp>
+#include <libtorrent/kademlia/node_id.hpp>
+
+#include <boost/function.hpp>
+
+namespace libtorrent { namespace dht
+{
+
+#ifdef TORRENT_DHT_VERBOSE_LOGGING
+TORRENT_DECLARE_LOG(refresh);
+#endif
+
+class routing_table;
+class rpc_manager;
+
+class refresh : public traversal_algorithm
+{
+public:
+ typedef boost::function<void()> done_callback;
+
+ template<class InIt>
+ static void initiate(
+ node_id target
+ , int branch_factor
+ , int max_active_pings
+ , int max_results
+ , routing_table& table
+ , InIt first
+ , InIt last
+ , rpc_manager& rpc
+ , done_callback const& callback
+ );
+
+ void ping_reply(node_id id);
+ void ping_timeout(node_id id);
+
+private:
+ template<class InIt>
+ refresh(
+ node_id target
+ , int branch_factor
+ , int max_active_pings
+ , int max_results
+ , routing_table& table
+ , InIt first
+ , InIt last
+ , rpc_manager& rpc
+ , done_callback const& callback
+ );
+
+ void done();
+ void invoke(node_id const& id, udp::endpoint addr);
+
+ void invoke_pings_or_finish();
+
+ int m_max_active_pings;
+ int m_active_pings;
+
+ done_callback m_done_callback;
+
+ std::vector<result>::iterator m_leftover_nodes_iterator;
+};
+
+template<class InIt>
+inline refresh::refresh(
+ node_id target
+ , int branch_factor
+ , int max_active_pings
+ , int max_results
+ , routing_table& table
+ , InIt first
+ , InIt last
+ , rpc_manager& rpc
+ , done_callback const& callback
+)
+ : traversal_algorithm(
+ target
+ , branch_factor
+ , max_results
+ , table
+ , rpc
+ , first
+ , last
+ )
+ , m_max_active_pings(max_active_pings)
+ , m_active_pings(0)
+ , m_done_callback(callback)
+{
+ boost::intrusive_ptr<refresh> self(this);
+ add_requests();
+}
+
+template<class InIt>
+inline void refresh::initiate(
+ node_id target
+ , int branch_factor
+ , int max_active_pings
+ , int max_results
+ , routing_table& table
+ , InIt first
+ , InIt last
+ , rpc_manager& rpc
+ , done_callback const& callback
+)
+{
+ new refresh(
+ target
+ , branch_factor
+ , max_active_pings
+ , max_results
+ , table
+ , first
+ , last
+ , rpc
+ , callback
+ );
+}
+
+} } // namespace libtorrent::dht
+
+#endif // REFRESH_050324_HPP
+
diff --git a/library/include/libtorrent/kademlia/routing_table.hpp b/library/include/libtorrent/kademlia/routing_table.hpp
new file mode 100644
index 000000000..3abc472b4
--- /dev/null
+++ b/library/include/libtorrent/kademlia/routing_table.hpp
@@ -0,0 +1,246 @@
+/*
+
+Copyright (c) 2006, 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 ROUTING_TABLE_HPP
+#define ROUTING_TABLE_HPP
+
+#include <vector>
+#include <deque>
+#include <boost/cstdint.hpp>
+#include <boost/date_time/posix_time/posix_time.hpp>
+
+#include <boost/iterator/iterator_facade.hpp>
+#include <boost/iterator/iterator_categories.hpp>
+#include <boost/utility.hpp>
+#include <boost/tuple/tuple.hpp>
+#include <boost/array.hpp>
+#include <set>
+
+#include <libtorrent/kademlia/logging.hpp>
+
+#include <libtorrent/kademlia/node_id.hpp>
+#include <libtorrent/kademlia/node_entry.hpp>
+#include <libtorrent/session_settings.hpp>
+
+namespace pt = boost::posix_time;
+
+namespace libtorrent { namespace dht
+{
+
+using asio::ip::udp;
+
+//TORRENT_DECLARE_LOG(table);
+
+typedef std::deque<node_entry> bucket_t;
+
+// differences in the implementation from the description in
+// the paper:
+//
+// * The routing table tree is not allocated dynamically, there
+// are always 160 buckets.
+// * Nodes are not marked as being stale, they keep a counter
+// that tells how many times in a row they have failed. When
+// a new node is to be inserted, the node that has failed
+// the most times is replaced. If none of the nodes in the
+// bucket has failed, then it is put in the replacement
+// cache (just like in the paper).
+
+class routing_table;
+
+namespace aux
+{
+
+ // Iterates over a flattened routing_table structure.
+ class routing_table_iterator
+ : public boost::iterator_facade<
+ routing_table_iterator
+ , node_entry const
+ , boost::forward_traversal_tag
+ >
+ {
+ public:
+ routing_table_iterator()
+ {
+ }
+
+ private:
+ friend class libtorrent::dht::routing_table;
+ friend class boost::iterator_core_access;
+
+ typedef boost::array<std::pair<bucket_t, bucket_t>, 160>::const_iterator
+ bucket_iterator_t;
+
+ routing_table_iterator(
+ bucket_iterator_t begin
+ , bucket_iterator_t end)
+ : m_bucket_iterator(begin)
+ , m_bucket_end(end)
+ , m_iterator(begin != end ? begin->first.begin() : bucket_t::iterator())
+ {
+ if (m_bucket_iterator == m_bucket_end) return;
+ while (m_iterator == m_bucket_iterator->first.end())
+ {
+ if (++m_bucket_iterator == m_bucket_end)
+ break;
+ m_iterator = m_bucket_iterator->first.begin();
+ }
+ }
+
+ bool equal(routing_table_iterator const& other) const
+ {
+ return m_bucket_iterator == other.m_bucket_iterator
+ && (m_bucket_iterator == m_bucket_end
+ || m_iterator == other.m_iterator);
+ }
+
+ void increment()
+ {
+ assert(m_bucket_iterator != m_bucket_end);
+ ++m_iterator;
+ while (m_iterator == m_bucket_iterator->first.end())
+ {
+ if (++m_bucket_iterator == m_bucket_end)
+ break;
+ m_iterator = m_bucket_iterator->first.begin();
+ }
+ }
+
+ node_entry const& dereference() const
+ {
+ assert(m_bucket_iterator != m_bucket_end);
+ return *m_iterator;
+ }
+
+ bucket_iterator_t m_bucket_iterator;
+ bucket_iterator_t m_bucket_end;
+ bucket_t::const_iterator m_iterator;
+ };
+
+} // namespace aux
+
+class routing_table
+{
+public:
+ typedef aux::routing_table_iterator iterator;
+ typedef iterator const_iterator;
+
+ routing_table(node_id const& id, int bucket_size
+ , dht_settings const& settings);
+
+ void node_failed(node_id const& id);
+
+ // adds an endpoint that will never be added to
+ // the routing table
+ void add_router_node(udp::endpoint router);
+
+ // iterates over the router nodes added
+ typedef std::set<udp::endpoint>::const_iterator router_iterator;
+ router_iterator router_begin() const { return m_router_nodes.begin(); }
+ router_iterator router_end() const { return m_router_nodes.end(); }
+
+ // this function is called every time the node sees
+ // a sign of a node being alive. This node will either
+ // be inserted in the k-buckets or be moved to the top
+ // of its bucket.
+ bool node_seen(node_id const& id, udp::endpoint addr);
+
+ // returns time when the given bucket needs another refresh.
+ // if the given bucket is empty but there are nodes
+ // in a bucket closer to us, or if the bucket is non-empty and
+ // the time from the last activity is more than 15 minutes
+ boost::posix_time::ptime next_refresh(int bucket);
+
+ // fills the vector with the count nodes from our buckets that
+ // are nearest to the given id.
+ void find_node(node_id const& id, std::vector<node_entry>& l
+ , bool include_self, int count = 0);
+
+ // returns true if the given node would be placed in a bucket
+ // that is not full. If the node already exists in the table
+ // this function returns false
+ bool need_node(node_id const& id);
+
+ // this will set the given bucket's latest activity
+ // to the current time
+ void touch_bucket(int bucket);
+
+ int bucket_size(int bucket)
+ {
+ assert(bucket >= 0 && bucket < 160);
+ return (int)m_buckets[bucket].first.size();
+ }
+ int bucket_size() const { return m_bucket_size; }
+
+ iterator begin() const;
+ iterator end() const;
+
+ boost::tuple<int, int> size() const;
+
+ // returns true if there are no working nodes
+ // in the routing table
+ bool need_bootstrap() const;
+
+ void replacement_cache(bucket_t& nodes) const;
+
+ // used for debug and monitoring purposes. This will print out
+ // the state of the routing table to the given stream
+ void print_state(std::ostream& os) const;
+
+private:
+
+ // constant called k in paper
+ int m_bucket_size;
+
+ dht_settings const& m_settings;
+
+ // 160 (k-bucket, replacement cache) pairs
+ typedef boost::array<std::pair<bucket_t, bucket_t>, 160> table_t;
+ table_t m_buckets;
+ // timestamps of the last activity in each bucket
+ typedef boost::array<boost::posix_time::ptime, 160> table_activity_t;
+ table_activity_t m_bucket_activity;
+ node_id m_id; // our own node id
+
+ // this is a set of all the endpoints that have
+ // been identified as router nodes. They will
+ // be used in searches, but they will never
+ // be added to the routing table.
+ std::set<udp::endpoint> m_router_nodes;
+
+ // this is the lowest bucket index with nodes in it
+ int m_lowest_active_bucket;
+};
+
+} } // namespace libtorrent::dht
+
+#endif // ROUTING_TABLE_HPP
+
diff --git a/library/include/libtorrent/kademlia/rpc_manager.hpp b/library/include/libtorrent/kademlia/rpc_manager.hpp
new file mode 100644
index 000000000..5aa850e95
--- /dev/null
+++ b/library/include/libtorrent/kademlia/rpc_manager.hpp
@@ -0,0 +1,194 @@
+/*
+
+Copyright (c) 2006, 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 RPC_MANAGER_HPP
+#define RPC_MANAGER_HPP
+
+#include <vector>
+#include <map>
+#include <boost/function.hpp>
+#include <boost/date_time/posix_time/posix_time.hpp>
+#include <boost/shared_ptr.hpp>
+#include <boost/noncopyable.hpp>
+#include <boost/cstdint.hpp>
+#include <boost/array.hpp>
+
+#include <libtorrent/socket.hpp>
+#include <libtorrent/entry.hpp>
+#include <libtorrent/kademlia/packet_iterator.hpp>
+#include <libtorrent/kademlia/node_id.hpp>
+#include <libtorrent/kademlia/logging.hpp>
+#include <libtorrent/kademlia/node_entry.hpp>
+
+namespace libtorrent { namespace dht
+{
+
+using asio::ip::udp;
+#ifdef TORRENT_DHT_VERBOSE_LOGGING
+TORRENT_DECLARE_LOG(rpc);
+#endif
+
+typedef std::vector<char> packet_t;
+
+namespace messages
+{
+ enum { ping = 0, find_node = 1, get_peers = 2, announce_peer = 3, error = 4 };
+ char const* const ids[] = { "ping", "find_node", "get_peers", "announce_peer", "error" };
+} // namespace messages
+
+struct msg
+{
+ msg() : reply(false), piggy_backed_ping(false)
+ , port(0) {}
+
+ // true if this message is a reply
+ bool reply;
+ // true if this is a reply with a piggy backed ping
+ bool piggy_backed_ping;
+ // the kind if message
+ int message_id;
+ // if this is a reply, a copy of the transaction id
+ // from the request. If it's a request, a transaction
+ // id that should be sent back in the reply
+ std::string transaction_id;
+ // if this packet has a piggy backed ping, this
+ // is the transaction id of that ping
+ std::string ping_transaction_id;
+ // the node id of the process sending the message
+ node_id id;
+ // the address of the process sending or receiving
+ // the message.
+ udp::endpoint addr;
+ // if this is a nodes response, these are the nodes
+ typedef std::vector<node_entry> nodes_t;
+ nodes_t nodes;
+
+ typedef std::vector<tcp::endpoint> peers_t;
+ peers_t peers;
+
+ // similar to transaction_id but for write operations.
+ entry write_token;
+
+ // the info has for peer_requests, announce_peer
+ // and responses
+ node_id info_hash;
+
+ // port for announce_peer messages
+ int port;
+
+ // ERROR MESSAGES
+ int error_code;
+ std::string error_msg;
+};
+
+struct observer : boost::noncopyable
+{
+ observer()
+ : sent(boost::posix_time::microsec_clock::universal_time())
+ {}
+
+ virtual ~observer() {}
+
+ // this two callbacks lets the observer add
+ // information to the message before it's sent
+ virtual void send(msg& m) = 0;
+
+ // this is called when a reply is received
+ virtual void reply(msg const& m) = 0;
+
+ // this is called when no reply has been received within
+ // some timeout
+ virtual void timeout() = 0;
+
+ udp::endpoint target_addr;
+ boost::posix_time::ptime sent;
+};
+
+class routing_table;
+
+class rpc_manager
+{
+public:
+ typedef boost::function1<void, msg const&> fun;
+ typedef boost::function1<void, msg const&> send_fun;
+
+ rpc_manager(fun const& incoming_fun, node_id const& our_id
+ , routing_table& table, send_fun const& sf);
+ ~rpc_manager();
+
+ // returns true if the node needs a refresh
+ bool incoming(msg const&);
+ boost::posix_time::time_duration tick();
+
+ void invoke(int message_id, udp::endpoint target
+ , boost::shared_ptr<observer> o);
+
+ void reply(msg& m, msg const& reply_to);
+ void reply_with_ping(msg& m, msg const& reply_to);
+
+#ifndef NDEBUG
+ void check_invariant() const;
+#endif
+
+private:
+
+ enum { max_transactions = 2048 };
+ unsigned int new_transaction_id();
+ void update_oldest_transaction_id();
+
+ boost::uint32_t calc_connection_id(udp::endpoint addr);
+
+ typedef boost::array<boost::shared_ptr<observer>, max_transactions>
+ transactions_t;
+ transactions_t m_transactions;
+
+ // this is the next transaction id to be used
+ int m_next_transaction_id;
+ // this is the oldest transaction id still
+ // (possibly) in use. This is the transaction
+ // that will time out first, the one we are
+ // waiting for to time out
+ int m_oldest_transaction_id;
+
+ fun m_incoming;
+ send_fun m_send;
+ node_id m_our_id;
+ routing_table& m_table;
+ boost::posix_time::ptime m_timer;
+ node_id m_random_number;
+};
+
+} } // namespace libtorrent::dht
+
+#endif
+
+
diff --git a/library/include/libtorrent/kademlia/traversal_algorithm.hpp b/library/include/libtorrent/kademlia/traversal_algorithm.hpp
new file mode 100644
index 000000000..a99a9c6ea
--- /dev/null
+++ b/library/include/libtorrent/kademlia/traversal_algorithm.hpp
@@ -0,0 +1,161 @@
+/*
+
+Copyright (c) 2006, Arvid Norberg & Daniel Wallin
+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 TRAVERSAL_ALGORITHM_050324_HPP
+#define TRAVERSAL_ALGORITHM_050324_HPP
+
+#include <vector>
+#include <set>
+
+#include <libtorrent/kademlia/node_id.hpp>
+#include <libtorrent/kademlia/routing_table.hpp>
+#include <libtorrent/kademlia/logging.hpp>
+
+#include <boost/noncopyable.hpp>
+#include <boost/intrusive_ptr.hpp>
+#include <boost/bind.hpp>
+
+namespace libtorrent { namespace dht
+{
+#ifdef TORRENT_DHT_VERBOSE_LOGGING
+TORRENT_DECLARE_LOG(traversal);
+#endif
+
+class rpc_manager;
+
+// this class may not be instantiated as a stack object
+class traversal_algorithm : boost::noncopyable
+{
+public:
+ void traverse(node_id const& id, udp::endpoint addr);
+ void finished(node_id const& id);
+ void failed(node_id const& id);
+ virtual ~traversal_algorithm() {}
+
+protected:
+ template<class InIt>
+ traversal_algorithm(
+ node_id target
+ , int branch_factor
+ , int max_results
+ , routing_table& table
+ , rpc_manager& rpc
+ , InIt start
+ , InIt end
+ );
+
+ void add_request(node_id const& id, udp::endpoint addr);
+ void add_requests();
+ void add_entry(node_id const& id, udp::endpoint addr, unsigned char flags);
+
+ virtual void done() = 0;
+ virtual void invoke(node_id const& id, udp::endpoint addr) = 0;
+
+ struct result
+ {
+ result(node_id const& id, udp::endpoint addr, unsigned char f = 0)
+ : id(id), addr(addr), flags(f)
+ {}
+
+ node_id id;
+ udp::endpoint addr;
+ enum { queried = 1, initial = 2 };
+ unsigned char flags;
+ };
+
+ std::vector<result>::iterator last_iterator();
+
+ friend void intrusive_ptr_add_ref(traversal_algorithm* p)
+ {
+ p->m_ref_count++;
+ }
+
+ friend void intrusive_ptr_release(traversal_algorithm* p)
+ {
+ if (--p->m_ref_count == 0)
+ delete p;
+ }
+
+ int m_ref_count;
+
+ node_id m_target;
+ int m_branch_factor;
+ int m_max_results;
+ std::vector<result> m_results;
+ std::set<udp::endpoint> m_failed;
+ routing_table& m_table;
+ rpc_manager& m_rpc;
+ int m_invoke_count;
+};
+
+template<class InIt>
+traversal_algorithm::traversal_algorithm(
+ node_id target
+ , int branch_factor
+ , int max_results
+ , routing_table& table
+ , rpc_manager& rpc
+ , InIt start // <- nodes to initiate traversal with
+ , InIt end
+)
+ : m_ref_count(0)
+ , m_target(target)
+ , m_branch_factor(branch_factor)
+ , m_max_results(max_results)
+ , m_table(table)
+ , m_rpc(rpc)
+ , m_invoke_count(0)
+{
+ using boost::bind;
+
+ for (InIt i = start; i != end; ++i)
+ {
+ add_entry(i->id, i->addr, result::initial);
+ }
+
+ // in case the routing table is empty, use the
+ // router nodes in the table
+ if (start == end)
+ {
+ for (routing_table::router_iterator i = table.router_begin()
+ , end(table.router_end()); i != end; ++i)
+ {
+ add_entry(node_id(0), *i, result::initial);
+ }
+ }
+
+}
+
+} } // namespace libtorrent::dht
+
+#endif // TRAVERSAL_ALGORITHM_050324_HPP
+
diff --git a/library/include/libtorrent/peer.hpp b/library/include/libtorrent/peer.hpp
new file mode 100755
index 000000000..c404a611d
--- /dev/null
+++ b/library/include/libtorrent/peer.hpp
@@ -0,0 +1,63 @@
+/*
+
+Copyright (c) 2003, 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_PEER_HPP_INCLUDED
+#define TORRENT_PEER_HPP_INCLUDED
+
+#include <string>
+
+#include "libtorrent/peer_id.hpp"
+
+namespace libtorrent
+{
+
+ struct TORRENT_EXPORT peer_entry
+ {
+ std::string ip;
+ int port;
+ peer_id pid;
+
+ bool operator==(const peer_entry& p) const
+ {
+ return pid == p.pid;
+ }
+
+ bool operator<(const peer_entry& p) const
+ {
+ return pid < p.pid;
+ }
+ };
+
+}
+
+#endif // TORRENT_PEER_HPP_INCLUDED
+
diff --git a/library/include/libtorrent/peer_connection.hpp b/library/include/libtorrent/peer_connection.hpp
new file mode 100755
index 000000000..a7f9b7e53
--- /dev/null
+++ b/library/include/libtorrent/peer_connection.hpp
@@ -0,0 +1,557 @@
+/*
+
+Copyright (c) 2003, 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_PEER_CONNECTION_HPP_INCLUDED
+#define TORRENT_PEER_CONNECTION_HPP_INCLUDED
+
+#include <ctime>
+#include <algorithm>
+#include <vector>
+#include <deque>
+#include <string>
+
+#include "libtorrent/debug.hpp"
+
+#ifdef _MSC_VER
+#pragma warning(push, 1)
+#endif
+
+#include <boost/smart_ptr.hpp>
+#include <boost/weak_ptr.hpp>
+#include <boost/noncopyable.hpp>
+#include <boost/array.hpp>
+#include <boost/date_time/posix_time/posix_time.hpp>
+#include <boost/optional.hpp>
+#include <boost/cstdint.hpp>
+#include <boost/detail/atomic_count.hpp>
+
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+#include "libtorrent/buffer.hpp"
+#include "libtorrent/socket.hpp"
+#include "libtorrent/peer_id.hpp"
+#include "libtorrent/storage.hpp"
+#include "libtorrent/stat.hpp"
+#include "libtorrent/alert.hpp"
+#include "libtorrent/torrent_handle.hpp"
+#include "libtorrent/torrent.hpp"
+#include "libtorrent/allocate_resources.hpp"
+#include "libtorrent/peer_request.hpp"
+#include "libtorrent/piece_block_progress.hpp"
+#include "libtorrent/config.hpp"
+#include "libtorrent/session.hpp"
+
+// TODO: each time a block is 'taken over'
+// from another peer. That peer must be given
+// a chance to become not-interested.
+
+namespace libtorrent
+{
+ class torrent;
+
+ namespace detail
+ {
+ 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
+ {
+ friend class invariant_access;
+ friend void intrusive_ptr_add_ref(peer_connection const*);
+ friend void intrusive_ptr_release(peer_connection const*);
+ public:
+
+ // this is the constructor where the we are the active part.
+ // The peer_conenction should handshake and verify that the
+ // other end has the correct id
+ peer_connection(
+ aux::session_impl& ses
+ , boost::weak_ptr<torrent> t
+ , boost::shared_ptr<stream_socket> s
+ , tcp::endpoint const& remote);
+
+ // with this constructor we have been contacted and we still don't
+ // know which torrent the connection belongs to
+ peer_connection(
+ aux::session_impl& ses
+ , boost::shared_ptr<stream_socket> s);
+
+ // this function is called once the torrent associated
+ // with this peer connection has retrieved the meta-
+ // data. If the torrent was spawned with metadata
+ // this is called from the constructor.
+ void init();
+
+ void set_upload_limit(int limit);
+ void set_download_limit(int limit);
+
+ virtual ~peer_connection();
+
+ // this adds an announcement in the announcement queue
+ // it will let the peer know that we have the given piece
+ void announce_piece(int index);
+
+ // tells if this connection has data it want to send
+ // and has enough upload bandwidth quota left to send it.
+ bool can_write() const;
+ bool can_read() const;
+
+ bool is_seed() const;
+
+ bool has_timed_out() const;
+
+ // will send a keep-alive message to the peer
+ void keep_alive();
+
+ peer_id const& pid() const { return m_peer_id; }
+ void set_pid(const peer_id& pid) { m_peer_id = pid; }
+ bool has_piece(int i) const;
+
+ const std::deque<piece_block>& download_queue() const;
+ const std::deque<piece_block>& request_queue() const;
+ const std::deque<peer_request>& upload_queue() const;
+
+ bool is_interesting() const { return m_interesting; }
+ bool is_choked() const { return m_choked; }
+
+ bool is_peer_interested() const { return m_peer_interested; }
+ bool has_peer_choked() const { return m_peer_choked; }
+
+ // returns the torrent this connection is a part of
+ // may be zero if the connection is an incoming connection
+ // and it hasn't received enough information to determine
+ // which torrent it should be associated with
+ boost::weak_ptr<torrent> associated_torrent() const
+ { return m_torrent; }
+
+ const stat& statistics() const { return m_statistics; }
+ void add_stat(size_type downloaded, size_type uploaded);
+
+ // is called once every second by the main loop
+ void second_tick(float tick_interval);
+
+ boost::shared_ptr<stream_socket> get_socket() const { return m_socket; }
+ tcp::endpoint const& remote() const { return m_remote; }
+
+ std::vector<bool> const& get_bitfield() const;
+
+ // this will cause this peer_connection to be disconnected.
+ // what it does is that it puts a reference to it in
+ // m_ses.m_disconnect_peer list, which will be scanned in the
+ // mainloop to disconnect peers.
+ void disconnect();
+ bool is_disconnecting() const { return m_disconnecting; }
+
+ // this is called when the connection attempt has succeeded
+ // and the peer_connection is supposed to set m_connecting
+ // to false, and stop monitor writability
+ void on_connection_complete(asio::error const& e);
+
+ // returns true if this connection is still waiting to
+ // finish the connection attempt
+ bool is_connecting() const { return m_connecting; }
+
+ // returns true if the socket of this peer hasn't been
+ // attempted to connect yet (i.e. it's queued for
+ // connection attempt).
+ bool is_queued() const { return m_queued; }
+
+ // called when it's time for this peer_conncetion to actually
+ // initiate the tcp connection. This may be postponed until
+ // the library isn't using up the limitation of half-open
+ // tcp connections.
+ void connect();
+
+ // This is called for every peer right after the upload
+ // bandwidth has been distributed among them
+ // It will reset the used bandwidth to 0.
+ void reset_upload_quota();
+
+ // free upload.
+ size_type total_free_upload() const;
+ void add_free_upload(size_type free_upload);
+
+ // trust management.
+ void received_valid_data();
+ void received_invalid_data();
+ int trust_points() const;
+
+ size_type share_diff() const;
+
+ // a connection is local if it was initiated by us.
+ // if it was an incoming connection, it is remote
+ bool is_local() const { return m_active; }
+
+ void set_failed() { m_failed = true; }
+ bool failed() const { return m_failed; }
+
+ int desired_queue_size() const { return m_desired_queue_size; }
+
+#ifdef TORRENT_VERBOSE_LOGGING
+ boost::shared_ptr<logger> m_logger;
+#endif
+
+ // the message handlers are called
+ // each time a recv() returns some new
+ // data, the last time it will be called
+ // is when the entire packet has been
+ // received, then it will no longer
+ // be called. i.e. most handlers need
+ // to check how much of the packet they
+ // have received before any processing
+ void incoming_keepalive();
+ void incoming_choke();
+ void incoming_unchoke();
+ void incoming_interested();
+ void incoming_not_interested();
+ void incoming_have(int piece_index);
+ void incoming_bitfield(std::vector<bool> const& bitfield);
+ void incoming_request(peer_request const& r);
+ void incoming_piece(peer_request const& p, char const* data);
+ void incoming_piece_fragment();
+ void incoming_cancel(peer_request const& r);
+ void incoming_dht_port(int listen_port);
+
+ // the following functions appends messages
+ // to the send buffer
+ void send_choke();
+ void send_unchoke();
+ void send_interested();
+ void send_not_interested();
+
+ // adds a block to the request queue
+ void add_request(piece_block const& b);
+ void cancel_request(piece_block const& b);
+ void send_block_requests();
+
+ // how much bandwidth we're using, how much we want,
+ // and how much we are allowed to use.
+ resource_request m_ul_bandwidth_quota;
+ resource_request m_dl_bandwidth_quota;
+
+#ifndef NDEBUG
+ void check_invariant() const;
+ boost::posix_time::ptime m_last_choke;
+#endif
+
+ virtual void get_peer_info(peer_info& p) const = 0;
+
+ // returns the block currently being
+ // downloaded. And the progress of that
+ // block. If the peer isn't downloading
+ // a piece for the moment, the boost::optional
+ // will be invalid.
+ virtual boost::optional<piece_block_progress>
+ downloading_piece_progress() const
+ {
+ #ifdef TORRENT_VERBOSE_LOGGING
+ (*m_logger) << "downloading_piece_progress() dispatched to the base class!\n";
+ #endif
+ return boost::optional<piece_block_progress>();
+ }
+
+ protected:
+
+ virtual void write_choke() = 0;
+ virtual void write_unchoke() = 0;
+ virtual void write_interested() = 0;
+ virtual void write_not_interested() = 0;
+ virtual void write_request(peer_request const& r) = 0;
+ 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 on_connected() = 0;
+ virtual void on_tick() {}
+
+ virtual void on_receive(asio::error const& error
+ , std::size_t bytes_transferred) = 0;
+ virtual void on_sent(asio::error const& error
+ , std::size_t bytes_transferred) = 0;
+
+ void send_buffer(char const* begin, char const* end);
+ buffer::interval allocate_send_buffer(int size);
+ int send_buffer_size() const
+ {
+ return (int)m_send_buffer[0].size()
+ + (int)m_send_buffer[1].size()
+ - m_write_pos;
+ }
+
+ buffer::const_interval receive_buffer() const
+ {
+ return buffer::const_interval(&m_recv_buffer[0]
+ , &m_recv_buffer[0] + m_recv_pos);
+ }
+
+ void cut_receive_buffer(int size, int packet_size);
+
+ void reset_recv_buffer(int packet_size);
+ int packet_size() const { return m_packet_size; }
+
+ bool packet_finished() const
+ {
+ assert(m_recv_pos <= m_packet_size);
+ return m_packet_size == m_recv_pos;
+ }
+
+ void setup_send();
+ void setup_receive();
+
+ void attach_to_torrent(sha1_hash const& ih);
+
+ bool verify_piece(peer_request const& p) const;
+
+ // statistics about upload and download speeds
+ // and total amount of uploads and downloads for
+ // this peer
+ stat m_statistics;
+
+ // a back reference to the session
+ // the peer belongs to.
+ aux::session_impl& m_ses;
+
+ boost::intrusive_ptr<peer_connection> self()
+ { return boost::intrusive_ptr<peer_connection>(this); }
+
+ // called from the main loop when this connection has any
+ // work to do.
+ void on_send_data(asio::error const& error
+ , std::size_t bytes_transferred);
+ void on_receive_data(asio::error const& error
+ , std::size_t bytes_transferred);
+
+ // this is the limit on the number of outstanding requests
+ // we have to this peer. This is initialized to the settings
+ // in the session_settings structure. But it may be lowered
+ // if the peer is known to require a smaller limit (like BitComet).
+ // or if the extended handshake sets a limit.
+ // web seeds also has a limit on the queue size.
+ int m_max_out_request_queue;
+
+ void set_timeout(int s) { m_timeout = s; }
+
+ private:
+
+ void fill_send_buffer();
+
+ // the timeout in seconds
+ int m_timeout;
+
+ // the time when we last got a part of a
+ // piece packet from this peer
+ boost::posix_time::ptime m_last_piece;
+
+ int m_packet_size;
+ int m_recv_pos;
+ std::vector<char> m_recv_buffer;
+
+ // this is the buffer where data that is
+ // to be sent is stored until it gets
+ // consumed by send(). Since asio requires
+ // the memory buffer that is given to async.
+ // operations to remain valid until the operation
+ // finishes, there has to be two buffers. While
+ // waiting for a async_write operation on one
+ // buffer, the other is used to write data to
+ // be queued up.
+ std::vector<char> m_send_buffer[2];
+ // the current send buffer is the one to write to.
+ // (m_current_send_buffer + 1) % 2 is the
+ // buffer we're currently waiting for.
+ int m_current_send_buffer;
+
+ // if the sending buffer doesn't finish in one send
+ // operation, this is the position within that buffer
+ // where the next operation should continue
+ int m_write_pos;
+
+ // timeouts
+ boost::posix_time::ptime m_last_receive;
+ boost::posix_time::ptime m_last_sent;
+
+ boost::shared_ptr<stream_socket> m_socket;
+ tcp::endpoint m_remote;
+
+ // this is the torrent this connection is
+ // associated with. If the connection is an
+ // incoming conncetion, this is set to zero
+ // until the info_hash is received. Then it's
+ // set to the torrent it belongs to.
+ boost::weak_ptr<torrent> m_torrent;
+ // is true if it was we that connected to the peer
+ // and false if we got an incomming connection
+ // could be considered: true = local, false = remote
+ bool m_active;
+
+ // remote peer's id
+ peer_id m_peer_id;
+
+ // other side says that it's interested in downloading
+ // from us.
+ bool m_peer_interested;
+
+ // the other side has told us that it won't send anymore
+ // data to us for a while
+ bool m_peer_choked;
+
+ // the peer has pieces we are interested in
+ bool m_interesting;
+
+ // we have choked the upload to the peer
+ bool m_choked;
+
+ // this is set to true if the connection timed
+ // out or closed the connection. In that
+ // case we will not try to reconnect to
+ // this peer
+ bool m_failed;
+
+ // the pieces the other end have
+ std::vector<bool> m_have_piece;
+
+ // the number of pieces this peer
+ // has. Must be the same as
+ // std::count(m_have_piece.begin(),
+ // m_have_piece.end(), true)
+ int m_num_pieces;
+
+ // the queue of requests we have got
+ // from this peer
+ std::deque<peer_request> m_requests;
+
+ // the blocks we have reserved in the piece
+ // picker and will send to this peer.
+ std::deque<piece_block> m_request_queue;
+
+ // the queue of blocks we have requested
+ // from this peer
+ std::deque<piece_block> m_download_queue;
+
+ // the number of request we should queue up
+ // at the remote end.
+ int m_desired_queue_size;
+
+ // the amount of data this peer has been given
+ // as free upload. This is distributed from
+ // peers from which we get free download
+ // this will be negative on a peer from which
+ // we get free download, and positive on peers
+ // that we give the free upload, to keep the balance.
+ size_type m_free_upload;
+
+ // for every valid piece we receive where this
+ // peer was one of the participants, we increase
+ // this value. For every invalid piece we receive
+ // where this peer was a participant, we decrease
+ // this value. If it sinks below a threshold, its
+ // considered a bad peer and will be banned.
+ int m_trust_points;
+
+ // if this is true, this peer is assumed to handle all piece
+ // requests in fifo order. All skipped blocks are re-requested
+ // immediately instead of having a looser requirement
+ // where blocks can be sent out of order. The default is to
+ // allow non-fifo order.
+ bool m_assume_fifo;
+
+ // the number of invalid piece-requests
+ // we have got from this peer. If the request
+ // queue gets empty, and there have been
+ // invalid requests, we can assume the
+ // peer is waiting for those pieces.
+ // we can then clear its download queue
+ // by sending choke, unchoke.
+ int m_num_invalid_requests;
+
+ // this is true if this connection has been added
+ // to the list of connections that will be closed.
+ bool m_disconnecting;
+
+ // the time when this peer sent us a not_interested message
+ // the last time.
+ boost::posix_time::ptime m_became_uninterested;
+
+ // the time when we sent a not_interested message to
+ // this peer the last time.
+ boost::posix_time::ptime m_became_uninteresting;
+
+ // this is true until this socket has become
+ // writable for the first time (i.e. the
+ // connection completed). While connecting
+ // the timeout will not be triggered. This is
+ // because windows XP SP2 may delay connection
+ // attempts, which means that the connection
+ // may not even have been attempted when the
+ // time out is reached.
+ bool m_connecting;
+
+ // This is true until connect is called on the
+ // peer_connection's socket. It is false on incoming
+ // connections.
+ bool m_queued;
+
+ // these are true when there's a asynchronous write
+ // or read operation running.
+ bool m_writing;
+ // this is the number of bytes sent to the socket last
+ // time it was invoked. This is compared against the
+ // bytes_transferred in the callback function that tells
+ // how much actually was sent. Then the quota can be
+ // corrected according to the actual number of bytes sent
+ int m_last_write_size;
+ bool m_reading;
+ int m_last_read_size;
+ // reference counter for intrusive_ptr
+ mutable boost::detail::atomic_count m_refs;
+
+#ifndef NDEBUG
+ public:
+ bool m_in_constructor;
+#endif
+ };
+}
+
+#endif // TORRENT_PEER_CONNECTION_HPP_INCLUDED
+
diff --git a/library/include/libtorrent/peer_id.hpp b/library/include/libtorrent/peer_id.hpp
new file mode 100755
index 000000000..d3bf624b7
--- /dev/null
+++ b/library/include/libtorrent/peer_id.hpp
@@ -0,0 +1,177 @@
+/*
+
+Copyright (c) 2003, 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_PEER_ID_HPP_INCLUDED
+#define TORRENT_PEER_ID_HPP_INCLUDED
+
+#include <iostream>
+#include <iomanip>
+#include <cassert>
+#include <cctype>
+#include <algorithm>
+
+#include "libtorrent/config.hpp"
+
+namespace libtorrent
+{
+
+ class TORRENT_EXPORT big_number
+ {
+ // private type
+ struct private_pointer {};
+ // the number of bytes of the number
+ enum { number_size = 20 };
+ public:
+ enum { size = number_size };
+
+ big_number() {}
+
+ // when initialized with 0
+ big_number(private_pointer*) { clear(); }
+
+ void clear()
+ {
+ std::fill(m_number,m_number+number_size,0);
+ }
+
+ bool is_all_zeros() const
+ {
+ return std::count(m_number,m_number+number_size,0) == number_size;
+ }
+
+ bool operator==(big_number const& n) const
+ {
+ return std::equal(n.m_number, n.m_number+number_size, m_number);
+ }
+
+ bool operator!=(big_number const& n) const
+ {
+ return !std::equal(n.m_number, n.m_number+number_size, m_number);
+ }
+
+ bool operator<(big_number const& n) const
+ {
+ for (int i = 0; i < number_size; ++i)
+ {
+ if (m_number[i] < n.m_number[i]) return true;
+ if (m_number[i] > n.m_number[i]) return false;
+ }
+ return false;
+ }
+
+ big_number operator~()
+ {
+ big_number ret;
+ for (int i = 0; i< number_size; ++i)
+ ret.m_number[i] = ~m_number[i];
+ return ret;
+ }
+
+ big_number& operator &= (big_number const& n)
+ {
+ for (int i = 0; i< number_size; ++i)
+ m_number[i] &= n.m_number[i];
+ return *this;
+ }
+
+ big_number& operator |= (big_number const& n)
+ {
+ for (int i = 0; i< number_size; ++i)
+ m_number[i] |= n.m_number[i];
+ return *this;
+ }
+
+ unsigned char& operator[](int i)
+ { assert(i >= 0 && i < number_size); return m_number[i]; }
+
+ unsigned char const& operator[](int i) const
+ { assert(i >= 0 && i < number_size); return m_number[i]; }
+
+ typedef const unsigned char* const_iterator;
+ typedef unsigned char* iterator;
+
+ const_iterator begin() const { return m_number; }
+ const_iterator end() const { return m_number+number_size; }
+
+ iterator begin() { return m_number; }
+ iterator end() { return m_number+number_size; }
+
+ private:
+
+ unsigned char m_number[number_size];
+
+ };
+
+ typedef big_number peer_id;
+ typedef big_number sha1_hash;
+
+ inline std::ostream& operator<<(std::ostream& os, big_number const& peer)
+ {
+ for (big_number::const_iterator i = peer.begin();
+ i != peer.end(); ++i)
+ {
+ os << std::hex << std::setw(2) << std::setfill('0')
+ << static_cast<unsigned int>(*i);
+ }
+ os << std::dec << std::setfill(' ');
+ return os;
+ }
+
+ inline std::istream& operator>>(std::istream& is, big_number& peer)
+ {
+ using namespace std;
+
+ for (big_number::iterator i = peer.begin();
+ i != peer.end(); ++i)
+ {
+ char c[2];
+ is >> c[0] >> c[1];
+ c[0] = tolower(c[0]);
+ c[1] = tolower(c[1]);
+ if (
+ ((c[0] < '0' || c[0] > '9') && (c[0] < 'a' || c[0] > 'f'))
+ || ((c[1] < '0' || c[1] > '9') && (c[1] < 'a' || c[1] > 'f'))
+ || is.fail())
+ {
+ is.setstate(ios_base::failbit);
+ return is;
+ }
+ *i = ((isdigit(c[0])?c[0]-'0':c[0]-'a'+10) << 4)
+ + (isdigit(c[1])?c[1]-'0':c[1]-'a'+10);
+ }
+ return is;
+ }
+
+}
+
+#endif // TORRENT_PEER_ID_HPP_INCLUDED
+
diff --git a/library/include/libtorrent/peer_info.hpp b/library/include/libtorrent/peer_info.hpp
new file mode 100755
index 000000000..7a6e50834
--- /dev/null
+++ b/library/include/libtorrent/peer_info.hpp
@@ -0,0 +1,106 @@
+/*
+
+Copyright (c) 2003, 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_PEER_INFO_HPP_INCLUDED
+#define TORRENT_PEER_INFO_HPP_INCLUDED
+
+#include <vector>
+
+#include "libtorrent/socket.hpp"
+#include "libtorrent/peer_id.hpp"
+#include "libtorrent/size_type.hpp"
+#include "libtorrent/config.hpp"
+
+namespace libtorrent
+{
+ struct TORRENT_EXPORT peer_info
+ {
+ enum
+ {
+ interesting = 0x1,
+ choked = 0x2,
+ remote_interested = 0x4,
+ remote_choked = 0x8,
+ supports_extensions = 0x10,
+ local_connection = 0x20,
+ handshake = 0x40,
+ connecting = 0x80,
+ queued = 0x100
+ };
+ unsigned int flags;
+ tcp::endpoint ip;
+ float up_speed;
+ float down_speed;
+ float payload_up_speed;
+ float payload_down_speed;
+ size_type total_download;
+ size_type total_upload;
+ peer_id pid;
+ std::vector<bool> pieces;
+ bool seed; // true if this is a seed
+ int upload_limit;
+ int download_limit;
+
+ size_type load_balancing;
+
+ // this is the number of requests
+ // we have sent to this peer
+ // that we haven't got a response
+ // for yet
+ int download_queue_length;
+
+ // this is the number of requests
+ // the peer has sent to us
+ // that we haven't sent yet
+ int upload_queue_length;
+
+ // the currently downloading piece
+ // if piece index is -1 all associated
+ // members are just set to 0
+ int downloading_piece_index;
+ int downloading_block_index;
+ int downloading_progress;
+ int downloading_total;
+
+ std::string client;
+
+ enum
+ {
+ standard_bittorrent = 0,
+ web_seed = 1
+ };
+ int connection_type;
+ };
+
+}
+
+#endif // TORRENT_PEER_INFO_HPP_INCLUDED
diff --git a/library/include/libtorrent/peer_request.hpp b/library/include/libtorrent/peer_request.hpp
new file mode 100644
index 000000000..445ff4d7e
--- /dev/null
+++ b/library/include/libtorrent/peer_request.hpp
@@ -0,0 +1,49 @@
+/*
+
+Copyright (c) 2006, 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_PEER_REQUEST_HPP_INCLUDED
+#define TORRENT_PEER_REQUEST_HPP_INCLUDED
+
+namespace libtorrent
+{
+ struct TORRENT_EXPORT peer_request
+ {
+ int piece;
+ int start;
+ int length;
+ bool operator==(peer_request const& r) const
+ { return piece == r.piece && start == r.start && length == r.length; }
+ };
+}
+
+#endif // TORRENT_PEER_REQUEST_HPP_INCLUDED
+
diff --git a/library/include/libtorrent/piece_block_progress.hpp b/library/include/libtorrent/piece_block_progress.hpp
new file mode 100644
index 000000000..481ffc971
--- /dev/null
+++ b/library/include/libtorrent/piece_block_progress.hpp
@@ -0,0 +1,57 @@
+/*
+
+Copyright (c) 2003, 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_PIECE_BLOCK_PROGRESS_HPP_INCLUDED
+#define TORRENT_PIECE_BLOCK_PROGRESS_HPP_INCLUDED
+
+#include "libtorrent/config.hpp"
+
+namespace libtorrent
+{
+ struct TORRENT_EXPORT piece_block_progress
+ {
+ // the piece and block index
+ // determines exactly which
+ // part of the torrent that
+ // is currently being downloaded
+ int piece_index;
+ int block_index;
+ // the number of bytes we have received
+ // of this block
+ int bytes_downloaded;
+ // the number of bytes in the block
+ int full_block_bytes;
+ };
+}
+
+#endif // TORRENT_PIECE_BLOCK_PROGRESS_HPP_INCLUDED
+
diff --git a/library/include/libtorrent/piece_picker.hpp b/library/include/libtorrent/piece_picker.hpp
new file mode 100755
index 000000000..23f20a79a
--- /dev/null
+++ b/library/include/libtorrent/piece_picker.hpp
@@ -0,0 +1,355 @@
+/*
+
+Copyright (c) 2003, 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_PIECE_PICKER_HPP_INCLUDED
+#define TORRENT_PIECE_PICKER_HPP_INCLUDED
+
+#include <algorithm>
+#include <vector>
+#include <bitset>
+#include <cassert>
+
+#ifdef _MSC_VER
+#pragma warning(push, 1)
+#endif
+
+#include <boost/optional.hpp>
+
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+#include "libtorrent/peer_id.hpp"
+#include "libtorrent/socket.hpp"
+#include "libtorrent/session_settings.hpp"
+#include "libtorrent/config.hpp"
+
+namespace libtorrent
+{
+
+ class torrent;
+ class peer_connection;
+
+ struct TORRENT_EXPORT piece_block
+ {
+ piece_block(int p_index, int b_index)
+ : piece_index(p_index)
+ , block_index(b_index)
+ {}
+ int piece_index;
+ int block_index;
+
+ bool operator<(piece_block const& b) const
+ {
+ if (piece_index < b.piece_index) return true;
+ if (piece_index == b.piece_index) return block_index < b.block_index;
+ return false;
+ }
+
+ bool operator==(piece_block const& b) const
+ { return piece_index == b.piece_index && block_index == b.block_index; }
+
+ bool operator!=(piece_block const& b) const
+ { return piece_index != b.piece_index || block_index != b.block_index; }
+
+ };
+
+ class TORRENT_EXPORT piece_picker
+ {
+ public:
+
+ enum { max_blocks_per_piece = 256 };
+
+ struct block_info
+ {
+ block_info(): num_downloads(0) {}
+ // the peer this block was requested or
+ // downloaded from
+ tcp::endpoint peer;
+ // the number of times this block has been downloaded
+ int num_downloads;
+ };
+
+ struct downloading_piece
+ {
+ int index;
+ // each bit represents a block in the piece
+ // set to one if the block has been requested
+ std::bitset<max_blocks_per_piece> requested_blocks;
+ // the bit is set to one if the block has been acquired
+ std::bitset<max_blocks_per_piece> finished_blocks;
+ // info about each block
+ block_info info[max_blocks_per_piece];
+ };
+
+ piece_picker(int blocks_per_piece
+ , int total_num_blocks);
+
+ void set_sequenced_download_threshold(int sequenced_download_threshold);
+
+ // this is called before any other method is called
+ // after the local files has been checked.
+ // 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);
+
+ // increases the peer count for the given piece
+ // (is used when a HAVE or BITFIELD message is received)
+ void inc_refcount(int index);
+
+ // decreases the peer count for the given piece
+ // (used when a peer disconnects)
+ void dec_refcount(int index);
+
+ // This indicates that we just received this piece
+ // it means that the refcounter will indicate that
+ // we are not interested in this piece anymore
+ // (i.e. we don't have to maintain a refcount)
+ void we_have(int index);
+
+ // This will mark a piece as unfiltered, and if it was
+ // previously marked as filtered, it will be considered
+ // interesting again and be placed in the piece list available
+ // for downloading.
+ void mark_as_unfiltered(int index);
+
+ // This will mark a piece as filtered. The piece will be
+ // removed from the list of pieces avalable for downloading
+ // and hence, will not be downloaded.
+ void mark_as_filtered(int index);
+
+ // returns true if the pieces at 'index' is marked as filtered
+ bool is_filtered(int index) const;
+
+ // fills the bitmask with 1's for pieces that are filtered
+ void filtered_pieces(std::vector<bool>& mask) const;
+
+ // pieces should be the vector that represents the pieces a
+ // client has. It returns a list of all pieces that this client
+ // has and that are interesting to download. It returns them in
+ // priority order. It doesn't care about the download flag.
+ // The user of this function must lookup if any piece is
+ // marked as being downloaded. If the user of this function
+ // decides to download a piece, it must mark it as being downloaded
+ // itself, by using the mark_as_downloading() member function.
+ // THIS IS DONE BY THE peer_connection::send_request() MEMBER FUNCTION!
+ // The last argument is the tcp::endpoint of the peer that we'll download
+ // from.
+ void pick_pieces(const std::vector<bool>& pieces
+ , std::vector<piece_block>& interesting_blocks
+ , int num_pieces, bool prefer_whole_pieces
+ , tcp::endpoint peer) const;
+
+ // 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_finished(piece_block block) const;
+
+ // marks this piece-block as queued for downloading
+ void mark_as_downloading(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
+ // made available for redownloading
+ void restore_piece(int index);
+
+ // clears the given piece's download flag
+ // this means that this piece-block can be picked again
+ void abort_download(piece_block block);
+
+ bool is_piece_finished(int index) const;
+
+ // returns the number of blocks there is in the given piece
+ int blocks_in_piece(int index) const;
+
+ // the number of downloaded blocks that hasn't passed
+ // the hash-check yet
+ int unverified_blocks() const;
+
+ void get_downloaders(std::vector<tcp::endpoint>& d, int index) const;
+
+ const std::vector<downloading_piece>& get_download_queue() const
+ { return m_downloads; }
+
+ boost::optional<tcp::endpoint> get_downloader(piece_block block) const;
+
+ // the number of filtered pieces we don't have
+ int num_filtered() const { return m_num_filtered; }
+
+ // the number of filtered pieces we already have
+ int num_have_filtered() const { return m_num_have_filtered; }
+#ifndef NDEBUG
+ // used in debug mode
+ void check_invariant(const torrent* t = 0) const;
+#endif
+
+ // functor that compares indices on downloading_pieces
+ struct has_index
+ {
+ has_index(int i): index(i) { assert(i >= 0); }
+ bool operator()(const downloading_piece& p) const
+ { return p.index == index; }
+ int index;
+ };
+
+ int blocks_in_last_piece() const
+ { return m_blocks_in_last_piece; }
+
+ float distributed_copies() const;
+
+ private:
+
+ struct piece_pos
+ {
+ piece_pos() {}
+ piece_pos(int peer_count_, int index_)
+ : peer_count(peer_count_)
+ , downloading(0)
+ , filtered(0)
+ , index(index_)
+ {
+ assert(peer_count_ >= 0);
+ assert(index_ >= 0);
+ }
+
+ // selects which vector to look in
+ unsigned peer_count : 11;
+ // is 1 if the piece is marked as being downloaded
+ unsigned downloading : 1;
+ // is 1 if the piece is filtered (not to be downloaded)
+ unsigned filtered : 1;
+ // index in to the piece_info vector
+ unsigned index : 19;
+
+ enum { we_have_index = 0x3ffff };
+
+ int priority(int limit) const
+ {
+ return peer_count >= (unsigned)limit ? limit : peer_count;
+ }
+
+ bool ordered(int limit) const
+ {
+ return peer_count >= (unsigned)limit;
+ }
+
+ bool operator!=(piece_pos p) const
+ { return index != p.index || peer_count != p.peer_count; }
+
+ bool operator==(piece_pos p) const
+ { return index == p.index && peer_count == p.peer_count; }
+
+ };
+
+
+ void add(int index);
+ void move(bool downloading, bool filtered, int vec_index, int elem_index);
+ void remove(bool downloading, bool filtered, int vec_index, int elem_index);
+ std::vector<std::vector<int> >& pick_piece_info_vector(bool downloading
+ , bool filtered);
+
+ std::vector<std::vector<int> > const& pick_piece_info_vector(
+ bool downloading, bool filtered) const;
+
+ int add_interesting_blocks_free(const std::vector<int>& piece_list
+ , const std::vector<bool>& pieces
+ , std::vector<piece_block>& interesting_blocks
+ , int num_blocks, bool prefer_whole_pieces) const;
+
+ int add_interesting_blocks_partial(const std::vector<int>& piece_list
+ , const std::vector<bool>& pieces
+ , std::vector<piece_block>& interesting_blocks
+ , std::vector<piece_block>& backup_blocks
+ , int num_blocks, bool prefer_whole_pieces
+ , tcp::endpoint peer) const;
+
+
+ // this vector contains all pieces we don't have.
+ // in the first entry (index 0) is a vector of all pieces
+ // that no peer have, the vector at index 1 contains
+ // all pieces that exactly one peer have, index 2 contains
+ // all pieces exactly two peers have and so on.
+ std::vector<std::vector<int> > m_piece_info;
+
+ // this vector has the same structure as m_piece_info
+ // but only contains pieces we are currently downloading
+ // they have higher priority than pieces we aren't downloading
+ // during piece picking
+ std::vector<std::vector<int> > m_downloading_piece_info;
+
+ // this maps indices to number of peers that has this piece and
+ // index into the m_piece_info vectors.
+ // piece_pos::we_have_index means that we have the piece, so it
+ // doesn't exist in the piece_info buckets
+ // pieces with the filtered flag set doesn't have entries in
+ // the m_piece_info buckets either
+ std::vector<piece_pos> m_piece_map;
+
+ // each piece that's currently being downloaded
+ // has an entry in this list with block allocations.
+ // i.e. it says wich parts of the piece that
+ // is being downloaded
+ std::vector<downloading_piece> m_downloads;
+
+ int m_blocks_per_piece;
+ int m_blocks_in_last_piece;
+
+ // the number of filtered pieces that we don't already
+ // have. total_number_of_pieces - number_of_pieces_we_have
+ // - num_filtered is supposed to the number of pieces
+ // we still want to download
+ int m_num_filtered;
+
+ // the number of pieces we have that also are filtered
+ int m_num_have_filtered;
+
+ // the required popularity of a piece in order to download
+ // it in sequence instead of random order.
+ int m_sequenced_download_threshold;
+ };
+
+ inline int piece_picker::blocks_in_piece(int index) const
+ {
+ assert(index >= 0);
+ assert(index < (int)m_piece_map.size());
+ if (index+1 == (int)m_piece_map.size())
+ return m_blocks_in_last_piece;
+ else
+ return m_blocks_per_piece;
+ }
+
+}
+
+#endif // TORRENT_PIECE_PICKER_HPP_INCLUDED
+
diff --git a/library/include/libtorrent/policy.hpp b/library/include/libtorrent/policy.hpp
new file mode 100755
index 000000000..1358e2bdf
--- /dev/null
+++ b/library/include/libtorrent/policy.hpp
@@ -0,0 +1,245 @@
+/*
+
+Copyright (c) 2003, 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_POLICY_HPP_INCLUDED
+#define TORRENT_POLICY_HPP_INCLUDED
+
+#include <algorithm>
+#include <vector>
+
+#ifdef _MSC_VER
+#pragma warning(push, 1)
+#endif
+
+#include <boost/date_time/posix_time/posix_time.hpp>
+
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+#include "libtorrent/peer.hpp"
+#include "libtorrent/piece_picker.hpp"
+#include "libtorrent/socket.hpp"
+#include "libtorrent/size_type.hpp"
+#include "libtorrent/invariant_check.hpp"
+#include "libtorrent/config.hpp"
+
+namespace libtorrent
+{
+
+ class torrent;
+ class peer_connection;
+
+ enum
+ {
+ // the limits of the download queue size
+ min_request_queue = 2,
+
+ // the amount of free upload allowed before
+ // the peer is choked
+ free_upload_amount = 4 * 16 * 1024
+ };
+
+
+ class TORRENT_EXPORT policy
+ {
+ public:
+
+ policy(torrent* t);
+
+ // this is called every 10 seconds to allow
+ // for peer choking management
+ void pulse();
+
+ // this is called once for every peer we get from
+ // the tracker
+ void peer_from_tracker(const tcp::endpoint& remote, const peer_id& pid);
+
+ // called when an incoming connection is accepted
+ // return false if the connection closed
+ void new_connection(peer_connection& c);
+
+ // this is called if a peer timed-out or
+ // forcefully closed the connection. This
+ // will mark the connection as non-reconnectale
+ void peer_failed(peer_connection const& c);
+
+ // the given connection was just closed
+ void connection_closed(const peer_connection& c);
+
+ // is called when a peer is believed to have
+ // sent invalid data
+ void ban_peer(const peer_connection& c);
+
+ // the peer has got at least one interesting piece
+ void peer_is_interesting(peer_connection& c);
+
+ 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);
+
+ // the peer unchoked us
+ void unchoked(peer_connection& c);
+
+ // the peer is interested in our pieces
+ void interested(peer_connection& c);
+
+ // the peer is not interested in our pieces
+ void not_interested(peer_connection& c);
+
+#ifndef NDEBUG
+ bool has_connection(const peer_connection* p);
+
+ void check_invariant() const;
+#endif
+
+ struct peer
+ {
+ enum connection_type { not_connectable,connectable };
+
+ peer(const tcp::endpoint& ip, connection_type t);
+
+ size_type total_download() const;
+ size_type total_upload() const;
+
+ // the ip/port pair this peer is or was connected on
+ // if it was a remote (incoming) connection, type is
+ // set thereafter. If it was a peer we got from the
+ // tracker, type is set to local_connection.
+ tcp::endpoint ip;
+ connection_type type;
+
+ // the time when this peer was optimistically unchoked
+ // the last time.
+ boost::posix_time::ptime last_optimistically_unchoked;
+
+ // the time when the peer connected to us
+ // or disconnected if it isn't connected right now
+ boost::posix_time::ptime connected;
+
+ // this is the accumulated amount of
+ // uploaded and downloaded data to this
+ // peer. It only accounts for what was
+ // shared during the last connection to
+ // this peer. i.e. These are only updated
+ // when the connection is closed. For the
+ // total amount of upload and download
+ // we'll have to add thes figures with the
+ // statistics from the peer_connection.
+ size_type prev_amount_upload;
+ size_type prev_amount_download;
+
+ // is set to true if this peer has been banned
+ bool banned;
+
+ // if the peer is connected now, this
+ // will refer to a valid peer_connection
+ peer_connection* connection;
+ };
+
+ int num_peers() const
+ {
+ return m_peers.size();
+ }
+
+ int num_uploads() const
+ {
+ return m_num_unchoked;
+ }
+
+ typedef std::vector<peer>::iterator iterator;
+ iterator begin_peer() { return m_peers.begin(); }
+ iterator end_peer() { return m_peers.end(); }
+
+ private:
+
+ bool unchoke_one_peer();
+ void choke_one_peer();
+ peer* find_choke_candidate();
+ peer* find_unchoke_candidate();
+
+ // the seed prefix means that the
+ // function is used while seeding.
+ bool seed_unchoke_one_peer();
+ void seed_choke_one_peer();
+ peer* find_seed_choke_candidate();
+ peer* find_seed_unchoke_candidate();
+
+ bool connect_peer(peer *);
+ bool connect_one_peer();
+ bool disconnect_one_peer();
+ peer* find_disconnect_candidate();
+ peer* find_connect_candidate();
+
+ // a functor that identifies peers that have disconnected and that
+ // are too old for still being saved.
+ struct old_disconnected_peer
+ {
+ bool operator()(const peer& p)
+ {
+ using namespace boost::posix_time;
+
+ ptime not_tried_yet(boost::gregorian::date(1970,boost::gregorian::Jan,1));
+
+ // this timeout has to be customizable!
+ return p.connection == 0
+ && p.connected != not_tried_yet
+ && second_clock::universal_time() - p.connected > minutes(30);
+ }
+ };
+
+
+ std::vector<peer> m_peers;
+
+ torrent* m_torrent;
+
+ // the number of unchoked peers
+ // at any given time
+ int m_num_unchoked;
+
+ // free download we have got that hasn't
+ // been distributed yet.
+ size_type m_available_free_upload;
+
+ // if there is a connection limit,
+ // we disconnect one peer every minute in hope of
+ // establishing a connection with a better peer
+ boost::posix_time::ptime m_last_optimistic_disconnect;
+ };
+
+}
+
+#endif // TORRENT_POLICY_HPP_INCLUDED
+
diff --git a/library/include/libtorrent/random_sample.hpp b/library/include/libtorrent/random_sample.hpp
new file mode 100644
index 000000000..741576fd9
--- /dev/null
+++ b/library/include/libtorrent/random_sample.hpp
@@ -0,0 +1,72 @@
+/*
+
+Copyright (c) 2006, Arvid Norberg & Daniel Wallin
+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_RANDOM_SAMPLE_HPP
+#define TORRENT_RANDOM_SAMPLE_HPP
+
+#include <iterator>
+#include <cstdlib>
+
+namespace libtorrent
+{
+
+ template<class InIter, class OutIter, class Distance>
+ inline void random_sample_n(InIter start, InIter end
+ , OutIter out, Distance n)
+ {
+ Distance t = 0;
+ Distance m = 0;
+ Distance N = std::distance(start, end);
+
+ assert(N >= n);
+
+ while (m < n)
+ {
+ if ((rand() / (RAND_MAX + 1.f)) * (N - t) >= n - m)
+ {
+ ++start;
+ ++t;
+ }
+ else
+ {
+ *out = *start;
+ ++out;
+ ++start;
+ ++t;
+ ++m;
+ }
+ }
+ }
+
+}
+
+#endif
diff --git a/library/include/libtorrent/resource_request.hpp b/library/include/libtorrent/resource_request.hpp
new file mode 100755
index 000000000..046b49df9
--- /dev/null
+++ b/library/include/libtorrent/resource_request.hpp
@@ -0,0 +1,91 @@
+/*
+
+Copyright (c) 2003, Magnus Jonsson, 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_RESOURCE_REQUEST_HPP_INCLUDED
+#define TORRENT_RESOURCE_REQUEST_HPP_INCLUDED
+
+#include <boost/integer_traits.hpp>
+
+#ifdef min
+#undef min
+#endif
+
+#ifdef max
+#undef max
+#endif
+
+#include "libtorrent/config.hpp"
+
+namespace libtorrent
+{
+ struct TORRENT_EXPORT resource_request
+ {
+ resource_request()
+ : used(0)
+ , min(0)
+ , max(0)
+ , given(0)
+ {}
+
+ resource_request(int used_, int min_, int max_, int given_)
+ : used(used_)
+ , min(min_)
+ , max(max_)
+ , given(given_)
+ {}
+
+ int left() const
+ {
+ assert(given <= max);
+ assert(given >= min);
+ if (used < 0 && (given - used < 0))
+ return boost::integer_traits<int>::const_max;
+ return given - used;
+ }
+
+ static const int inf = boost::integer_traits<int>::const_max;
+
+ // right now I'm actively using this amount
+ int used;
+
+ // given cannot be smaller than min
+ // and not greater than max.
+ int min;
+ int max;
+
+ // Reply: Okay, you're allowed to use this amount (a compromise):
+ int given;
+ };
+}
+
+
+#endif
diff --git a/library/include/libtorrent/session.hpp b/library/include/libtorrent/session.hpp
new file mode 100755
index 000000000..d62d69493
--- /dev/null
+++ b/library/include/libtorrent/session.hpp
@@ -0,0 +1,226 @@
+/*
+
+Copyright (c) 2006, 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_SESSION_HPP_INCLUDED
+#define TORRENT_SESSION_HPP_INCLUDED
+
+#include <ctime>
+#include <algorithm>
+#include <vector>
+#include <set>
+#include <list>
+#include <deque>
+
+#ifdef _MSC_VER
+#pragma warning(push, 1)
+#endif
+
+#include <boost/limits.hpp>
+#include <boost/tuple/tuple.hpp>
+#include <boost/filesystem/path.hpp>
+#include <boost/thread.hpp>
+
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+#include "libtorrent/torrent_handle.hpp"
+#include "libtorrent/entry.hpp"
+#include "libtorrent/alert.hpp"
+#include "libtorrent/session_status.hpp"
+#include "libtorrent/version.hpp"
+#include "libtorrent/fingerprint.hpp"
+
+
+#if !defined(NDEBUG) && defined(_MSC_VER)
+# include <float.h>
+# include <eh.h>
+#endif
+
+namespace libtorrent
+{
+ class torrent;
+ class ip_filter;
+
+ enum extension_index
+ {
+ extended_handshake,
+ extended_chat_message,
+ extended_metadata_message,
+ extended_peer_exchange_message,
+ num_supported_extensions
+ };
+
+ namespace aux
+ {
+ // workaround for microsofts
+ // hardware exceptions that makes
+ // it hard to debug stuff
+#if defined(_MSC_VER)
+ struct eh_initializer
+ {
+ eh_initializer()
+ {
+#ifndef NDEBUG
+ _clearfp();
+ _controlfp(_EM_INEXACT | _EM_UNDERFLOW, _MCW_EM );
+ ::_set_se_translator(straight_to_debugger);
+#endif
+ }
+
+ static void straight_to_debugger(unsigned int, _EXCEPTION_POINTERS*)
+ { throw; }
+ };
+#else
+ struct eh_initializer {};
+#endif
+ struct session_impl;
+ }
+
+ class TORRENT_EXPORT session_proxy
+ {
+ friend class session;
+ public:
+ session_proxy() {}
+ private:
+ session_proxy(boost::shared_ptr<aux::session_impl> impl)
+ : m_impl(impl) {}
+ boost::shared_ptr<aux::session_impl> m_impl;
+ };
+
+ class TORRENT_EXPORT session: public boost::noncopyable, aux::eh_initializer
+ {
+ public:
+
+ session(fingerprint const& print = fingerprint("LT"
+ , LIBTORRENT_VERSION_MAJOR, LIBTORRENT_VERSION_MINOR, 0, 0));
+ session(
+ fingerprint const& print
+ , std::pair<int, int> listen_port_range
+ , char const* listen_interface = "0.0.0.0");
+
+ ~session();
+
+ std::vector<torrent_handle> get_torrents() const;
+
+ // 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
+ , entry const& resume_data = entry()
+ , bool compact_mode = true
+ , int block_size = 16 * 1024);
+
+ // TODO: deprecated, this is for backwards compatibility only
+ torrent_handle add_torrent(
+ entry const& e
+ , boost::filesystem::path const& save_path
+ , entry const& resume_data = entry()
+ , bool compact_mode = true
+ , int block_size = 16 * 1024)
+ {
+ return add_torrent(torrent_info(e), save_path, resume_data
+ , compact_mode, block_size);
+ }
+
+ torrent_handle add_torrent(
+ char const* tracker_url
+ , sha1_hash const& info_hash
+ , boost::filesystem::path const& save_path
+ , entry const& resume_data = entry()
+ , bool compact_mode = true
+ , int block_size = 16 * 1024);
+
+ session_proxy abort() { return session_proxy(m_impl); }
+
+ session_status status() const;
+
+#ifndef TORRENT_DISABLE_DHT
+ void start_dht(entry const& startup_state = entry());
+ void stop_dht();
+ void set_dht_settings(dht_settings const& settings);
+ entry dht_state() const;
+ void add_dht_node(std::pair<std::string, int> const& node);
+ void add_dht_router(std::pair<std::string, int> const& node);
+#endif
+
+ void enable_extension(extension_index i);
+ void disable_extensions();
+
+ void set_ip_filter(ip_filter const& f);
+ void set_peer_id(peer_id const& pid);
+ void set_key(int key);
+
+ bool is_listening() const;
+
+ // if the listen port failed in some way
+ // you can retry to listen on another port-
+ // range with this function. If the listener
+ // succeeded and is currently listening,
+ // a call to this function will shut down the
+ // listen port and reopen it using these new
+ // properties (the given interface and port range).
+ // As usual, if the interface is left as 0
+ // this function will return false on failure.
+ // If it fails, it will also generate alerts describing
+ // the error. It will return true on success.
+ bool listen_on(
+ std::pair<int, int> const& port_range
+ , const char* net_interface = 0);
+
+ // returns the port we ended up listening on
+ unsigned short listen_port() const;
+
+ void remove_torrent(const torrent_handle& h);
+
+ void set_settings(session_settings const& s);
+ session_settings const& settings();
+ void set_upload_rate_limit(int bytes_per_second);
+ void set_download_rate_limit(int bytes_per_second);
+ void set_max_uploads(int limit);
+ void set_max_connections(int limit);
+ void set_max_half_open_connections(int limit);
+
+ std::auto_ptr<alert> pop_alert();
+ void set_severity_level(alert::severity_t s);
+
+ private:
+
+ // data shared between the main thread
+ // and the working thread
+ boost::shared_ptr<aux::session_impl> m_impl;
+ };
+
+}
+
+#endif // TORRENT_SESSION_HPP_INCLUDED
+
diff --git a/library/include/libtorrent/session_settings.hpp b/library/include/libtorrent/session_settings.hpp
new file mode 100644
index 000000000..80d328611
--- /dev/null
+++ b/library/include/libtorrent/session_settings.hpp
@@ -0,0 +1,164 @@
+/*
+
+Copyright (c) 2003, 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_SESSION_SETTINGS_HPP_INCLUDED
+#define TORRENT_SESSION_SETTINGS_HPP_INCLUDED
+
+#include "libtorrent/version.hpp"
+
+namespace libtorrent
+{
+
+ struct TORRENT_EXPORT session_settings
+ {
+ session_settings(std::string const& user_agent_ = "libtorrent/"
+ LIBTORRENT_VERSION)
+ : proxy_port(0)
+ , user_agent(user_agent_)
+ , tracker_completion_timeout(60)
+ , tracker_receive_timeout(20)
+ , stop_tracker_timeout(10)
+ , tracker_maximum_response_length(1024*1024)
+ , piece_timeout(120)
+ , request_queue_time(3.f)
+ , max_allowed_in_request_queue(250)
+ , max_out_request_queue(200)
+ , whole_pieces_threshold(20)
+ , peer_timeout(120)
+ , urlseed_timeout(20)
+ , urlseed_pipeline_size(5)
+ {}
+
+ std::string proxy_ip;
+ int proxy_port;
+ std::string proxy_login;
+ std::string proxy_password;
+
+ // this is the user agent that will be sent to the tracker
+ // when doing requests. It is used to identify the client.
+ // It cannot contain \r or \n
+ std::string user_agent;
+
+ // the number of seconds to wait until giving up on a
+ // tracker request if it hasn't finished
+ int tracker_completion_timeout;
+
+ // the number of seconds where no data is received
+ // from the tracker until it should be considered
+ // as timed out
+ int tracker_receive_timeout;
+
+ // the time to wait when sending a stopped message
+ // before considering a tracker to have timed out.
+ // this is usually shorter, to make the client quit
+ // faster
+ int stop_tracker_timeout;
+
+ // if the content-length is greater than this value
+ // the tracker connection will be aborted
+ int tracker_maximum_response_length;
+
+ // the number of seconds from a request is sent until
+ // it times out if no piece response is returned.
+ int piece_timeout;
+
+ // the length of the request queue given in the number
+ // of seconds it should take for the other end to send
+ // all the pieces. i.e. the actual number of requests
+ // depends on the download rate and this number.
+ float request_queue_time;
+
+ // the number of outstanding block requests a peer is
+ // allowed to queue up in the client. If a peer sends
+ // more requests than this (before the first one has
+ // been sent) the last request will be dropped.
+ // the higher this is, the faster upload speeds the
+ // client can get to a single peer.
+ int max_allowed_in_request_queue;
+
+ // the maximum number of outstanding requests to
+ // send to a peer. This limit takes precedence over
+ // request_queue_time.
+ int max_out_request_queue;
+
+ // if a whole piece can be downloaded in this number
+ // of seconds, or less, the peer_connection will prefer
+ // to request whole pieces at a time from this peer.
+ // The benefit of this is to better utilize disk caches by
+ // doing localized accesses and also to make it easier
+ // to identify bad peers if a piece fails the hash check.
+ int whole_pieces_threshold;
+
+ // the number of seconds to wait for any activity on
+ // the peer wire before closing the connectiong due
+ // to time out.
+ int peer_timeout;
+
+ // same as peer_timeout, but only applies to url-seeds.
+ // this is usually set lower, because web servers are
+ // expected to be more reliable.
+ int urlseed_timeout;
+
+ // controls the pipelining size of url-seeds
+ int urlseed_pipeline_size;
+ };
+
+#ifndef TORRENT_DISABLE_DHT
+ struct dht_settings
+ {
+ dht_settings()
+ : max_peers_reply(50)
+ , search_branching(5)
+ , service_port(6881)
+ , max_fail_count(20)
+ {}
+
+ // the maximum number of peers to send in a
+ // reply to get_peers
+ int max_peers_reply;
+
+ // the number of simultanous "connections" when
+ // searching the DHT.
+ int search_branching;
+
+ // the listen port for the dht. This is a UDP port.
+ int service_port;
+
+ // the maximum number of times a node can fail
+ // in a row before it is removed from the table.
+ int max_fail_count;
+ };
+#endif
+
+}
+
+#endif
diff --git a/library/include/libtorrent/session_status.hpp b/library/include/libtorrent/session_status.hpp
new file mode 100644
index 000000000..7d12674fd
--- /dev/null
+++ b/library/include/libtorrent/session_status.hpp
@@ -0,0 +1,68 @@
+/*
+
+Copyright (c) 2006, 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_SESSION_STATUS_HPP_INCLUDED
+#define TORRENT_SESSION_STATUS_HPP_INCLUDED
+
+
+namespace libtorrent
+{
+
+ struct TORRENT_EXPORT session_status
+ {
+ bool has_incoming_connections;
+
+ float upload_rate;
+ float download_rate;
+
+ float payload_upload_rate;
+ float payload_download_rate;
+
+ size_type total_download;
+ size_type total_upload;
+
+ size_type total_payload_download;
+ size_type total_payload_upload;
+
+ int num_peers;
+
+#ifndef TORRENT_DISABLE_DHT
+ int m_dht_nodes;
+ int m_dht_node_cache;
+ int m_dht_torrents;
+#endif
+ };
+
+}
+
+#endif // TORRENT_SESSION_STATUS_HPP_INCLUDED
+
diff --git a/library/include/libtorrent/size_type.hpp b/library/include/libtorrent/size_type.hpp
new file mode 100755
index 000000000..6020a5ac3
--- /dev/null
+++ b/library/include/libtorrent/size_type.hpp
@@ -0,0 +1,52 @@
+/*
+
+Copyright (c) 2003, 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_SIZE_TYPE_HPP_INCLUDED
+#define TORRENT_SIZE_TYPE_HPP_INCLUDED
+
+#ifdef _MSC_VER
+#pragma warning(push, 1)
+#endif
+
+#include <boost/cstdint.hpp>
+
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+namespace libtorrent
+{
+ typedef boost::int64_t size_type;
+}
+
+
+#endif
diff --git a/library/include/libtorrent/socket.hpp b/library/include/libtorrent/socket.hpp
new file mode 100755
index 000000000..394c2a133
--- /dev/null
+++ b/library/include/libtorrent/socket.hpp
@@ -0,0 +1,156 @@
+/*
+
+Copyright (c) 2003, 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_SOCKET_HPP_INCLUDED
+#define TORRENT_SOCKET_HPP_INCLUDED
+
+#ifdef _MSC_VER
+#pragma warning(push, 1)
+#endif
+
+// if building as Objective C++, asio's template
+// parameters Protocol has to be renamed to avoid
+// colliding with keywords
+
+#ifdef __OBJC__
+#define Protocol Protocol_
+#endif
+
+#include <asio/ip/tcp.hpp>
+#include <asio/ip/udp.hpp>
+#include <asio/io_service.hpp>
+#include <asio/deadline_timer.hpp>
+#include <asio/write.hpp>
+
+#ifdef __OBJC__
+#undef Protocol
+#endif
+
+#include "libtorrent/io.hpp"
+
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+namespace libtorrent
+{
+/*
+ namespace asio = boost::asio;
+
+ using boost::asio::ipv4::tcp;
+ using boost::asio::ipv4::address;
+ using boost::asio::stream_socket;
+ using boost::asio::datagram_socket;
+ using boost::asio::socket_acceptor;
+ using boost::asio::demuxer;
+ using boost::asio::ipv4::host_resolver;
+ using boost::asio::async_write;
+ using boost::asio::ipv4::host;
+ using boost::asio::deadline_timer;
+*/
+// namespace asio = ::asio;
+
+ using asio::ip::tcp;
+ using asio::ip::udp;
+ typedef asio::ip::tcp::socket stream_socket;
+ typedef asio::ip::address address;
+ typedef asio::ip::address_v4 address_v4;
+ typedef asio::ip::address_v6 address_v6;
+ typedef asio::ip::udp::socket datagram_socket;
+ typedef asio::ip::tcp::acceptor socket_acceptor;
+ typedef asio::io_service demuxer;
+
+ using asio::async_write;
+ using asio::deadline_timer;
+
+ namespace detail
+ {
+ template<class OutIt>
+ void write_address(address const& a, OutIt& out)
+ {
+ if (a.is_v4())
+ {
+ write_uint32(a.to_v4().to_ulong(), out);
+ }
+ else if (a.is_v6())
+ {
+ asio::ip::address_v6::bytes_type bytes
+ = a.to_v6().to_bytes();
+ std::copy(bytes.begin(), bytes.end(), out);
+ }
+ }
+
+ template<class InIt>
+ address read_v4_address(InIt& in)
+ {
+ unsigned long ip = read_uint32(in);
+ return asio::ip::address_v4(ip);
+ }
+
+ template<class InIt>
+ address read_v6_address(InIt& in)
+ {
+ typedef asio::ip::address_v6::bytes_type bytes_t;
+ bytes_t bytes;
+ for (bytes_t::iterator i = bytes.begin()
+ , end(bytes.end()); i != end; ++i)
+ *i = read_uint8(in);
+ return asio::ip::address_v6(bytes);
+ }
+
+ template<class Endpoint, class OutIt>
+ void write_endpoint(Endpoint const& e, OutIt& out)
+ {
+ write_address(e.address(), out);
+ write_uint16(e.port(), out);
+ }
+
+ template<class Endpoint, class InIt>
+ Endpoint read_v4_endpoint(InIt& in)
+ {
+ address addr = read_v4_address(in);
+ int port = read_uint16(in);
+ return Endpoint(addr, port);
+ }
+
+ template<class Endpoint, class InIt>
+ Endpoint read_v6_endpoint(InIt& in)
+ {
+ address addr = read_v6_address(in);
+ int port = read_uint16(in);
+ return Endpoint(addr, port);
+ }
+ }
+}
+
+#endif // TORRENT_SOCKET_HPP_INCLUDED
+
diff --git a/library/include/libtorrent/stat.hpp b/library/include/libtorrent/stat.hpp
new file mode 100755
index 000000000..8e92c12a1
--- /dev/null
+++ b/library/include/libtorrent/stat.hpp
@@ -0,0 +1,193 @@
+/*
+
+Copyright (c) 2003, 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_STAT_HPP_INCLUDED
+#define TORRENT_STAT_HPP_INCLUDED
+
+#include <algorithm>
+#include <vector>
+#include <assert.h>
+
+#include "libtorrent/size_type.hpp"
+#include "libtorrent/invariant_check.hpp"
+#include "libtorrent/config.hpp"
+
+namespace libtorrent
+{
+
+ class TORRENT_EXPORT stat
+ {
+ friend class invariant_access;
+ enum { history = 10 };
+ public:
+
+ stat()
+ : m_downloaded_payload(0)
+ , m_uploaded_payload(0)
+ , m_downloaded_protocol(0)
+ , m_uploaded_protocol(0)
+ , m_total_download_payload(0)
+ , m_total_upload_payload(0)
+ , m_total_download_protocol(0)
+ , m_total_upload_protocol(0)
+ , m_mean_download_rate(0)
+ , m_mean_upload_rate(0)
+ , m_mean_download_payload_rate(0)
+ , m_mean_upload_payload_rate(0)
+ {
+ std::fill(m_download_rate_history, m_download_rate_history+history, 0.f);
+ std::fill(m_upload_rate_history, m_upload_rate_history+history, 0.f);
+ std::fill(m_download_payload_rate_history, m_download_payload_rate_history+history, 0.f);
+ std::fill(m_upload_payload_rate_history, m_upload_payload_rate_history+history, 0.f);
+ }
+
+ void operator+=(const stat& s)
+ {
+ INVARIANT_CHECK;
+
+ m_downloaded_payload += s.m_downloaded_payload;
+ m_total_download_payload += s.m_downloaded_payload;
+ m_downloaded_protocol += s.m_downloaded_protocol;
+ m_total_download_protocol += s.m_downloaded_protocol;
+
+ m_uploaded_payload += s.m_uploaded_payload;
+ m_total_upload_payload += s.m_uploaded_payload;
+ m_uploaded_protocol += s.m_uploaded_protocol;
+ m_total_upload_protocol += s.m_uploaded_protocol;
+ }
+
+ void received_bytes(int bytes_payload, int bytes_protocol)
+ {
+ INVARIANT_CHECK;
+
+ assert(bytes_payload >= 0);
+ assert(bytes_protocol >= 0);
+
+ m_downloaded_payload += bytes_payload;
+ m_total_download_payload += bytes_payload;
+ m_downloaded_protocol += bytes_protocol;
+ m_total_download_protocol += bytes_protocol;
+ }
+
+ void sent_bytes(int bytes_payload, int bytes_protocol)
+ {
+ INVARIANT_CHECK;
+
+ assert(bytes_payload >= 0);
+ assert(bytes_protocol >= 0);
+
+ m_uploaded_payload += bytes_payload;
+ m_total_upload_payload += bytes_payload;
+ m_uploaded_protocol += bytes_protocol;
+ m_total_upload_protocol += bytes_protocol;
+ }
+
+ // should be called once every second
+ void second_tick(float tick_interval);
+
+ float upload_rate() const { return m_mean_upload_rate; }
+ float download_rate() const { return m_mean_download_rate; }
+
+ float upload_payload_rate() const { return m_mean_upload_payload_rate; }
+ float download_payload_rate() const { return m_mean_download_payload_rate; }
+
+ size_type total_payload_upload() const { return m_total_upload_payload; }
+ size_type total_payload_download() const { return m_total_download_payload; }
+
+ size_type total_protocol_upload() const { return m_total_upload_protocol; }
+ size_type total_protocol_download() const { return m_total_download_protocol; }
+
+ // this is used to offset the statistics when a
+ // peer_connection is opened and have some previous
+ // transfers from earlier connections.
+ void add_stat(size_type downloaded, size_type uploaded)
+ {
+ m_total_download_payload += downloaded;
+ m_total_upload_payload += uploaded;
+ }
+
+ private:
+
+#ifndef NDEBUG
+ void check_invariant() const
+ {
+ assert(m_mean_upload_rate >= 0);
+ assert(m_mean_download_rate >= 0);
+ assert(m_mean_upload_payload_rate >= 0);
+ assert(m_mean_download_payload_rate >= 0);
+ assert(m_total_upload_payload >= 0);
+ assert(m_total_download_payload >= 0);
+ assert(m_total_upload_protocol >= 0);
+ assert(m_total_download_protocol >= 0);
+ }
+#endif
+
+ // history of download/upload speeds a few seconds back
+ float m_download_rate_history[history];
+ float m_upload_rate_history[history];
+
+ float m_download_payload_rate_history[history];
+ float m_upload_payload_rate_history[history];
+
+ // the accumulators we are adding the downloads/uploads
+ // to this second. This only counts the actual payload
+ // and ignores the bytes sent as protocol chatter.
+ int m_downloaded_payload;
+ int m_uploaded_payload;
+
+ // the accumulators we are adding the downloads/uploads
+ // to this second. This only counts the protocol
+ // chatter and ignores the actual payload
+ int m_downloaded_protocol;
+ int m_uploaded_protocol;
+
+ // total download/upload counters
+ // only counting payload data
+ size_type m_total_download_payload;
+ size_type m_total_upload_payload;
+
+ // total download/upload counters
+ // only counting protocol chatter
+ size_type m_total_download_protocol;
+ size_type m_total_upload_protocol;
+
+ // current mean download/upload rates
+ float m_mean_download_rate;
+ float m_mean_upload_rate;
+
+ float m_mean_download_payload_rate;
+ float m_mean_upload_payload_rate;
+ };
+
+}
+
+#endif // TORRENT_STAT_HPP_INCLUDED
diff --git a/library/include/libtorrent/storage.hpp b/library/include/libtorrent/storage.hpp
new file mode 100755
index 000000000..44f956786
--- /dev/null
+++ b/library/include/libtorrent/storage.hpp
@@ -0,0 +1,178 @@
+/*
+
+Copyright (c) 2003, 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_STORAGE_HPP_INCLUDE
+#define TORRENT_STORAGE_HPP_INCLUDE
+
+#include <vector>
+#include <bitset>
+
+#ifdef _MSC_VER
+#pragma warning(push, 1)
+#endif
+
+#include <boost/limits.hpp>
+#include <boost/filesystem/path.hpp>
+#include <boost/thread.hpp>
+#include <boost/shared_ptr.hpp>
+
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+
+#include "libtorrent/torrent_info.hpp"
+#include "libtorrent/config.hpp"
+
+namespace libtorrent
+{
+ namespace aux
+ {
+ struct piece_checker_data;
+ }
+
+ class session;
+
+#if defined(_WIN32) && defined(UNICODE)
+
+ TORRENT_EXPORT std::wstring safe_convert(std::string const& s);
+
+#endif
+
+ TORRENT_EXPORT std::vector<std::pair<size_type, std::time_t> > get_filesizes(
+ torrent_info const& t
+ , boost::filesystem::path p);
+
+ TORRENT_EXPORT bool match_filesizes(
+ torrent_info const& t
+ , boost::filesystem::path p
+ , std::vector<std::pair<size_type, std::time_t> > const& sizes
+ , std::string* error = 0);
+
+ struct TORRENT_EXPORT file_allocation_failed: std::exception
+ {
+ file_allocation_failed(const char* error_msg): m_msg(error_msg) {}
+ virtual const char* what() const throw() { return m_msg.c_str(); }
+ virtual ~file_allocation_failed() throw() {}
+ std::string m_msg;
+ };
+
+ class TORRENT_EXPORT storage
+ {
+ public:
+ storage(
+ const torrent_info& info
+ , const boost::filesystem::path& path);
+
+ void swap(storage&);
+
+ // may throw file_error if storage for slot does not exist
+ size_type read(char* buf, int slot, int offset, int size);
+
+ // may throw file_error if storage for slot hasn't been allocated
+ void write(const char* buf, int slot, int offset, int size);
+
+ bool move_storage(boost::filesystem::path save_path);
+
+ // this will close all open files that are opened for
+ // writing. This is called when a torrent has finished
+ // downloading.
+ void release_files();
+
+#ifndef NDEBUG
+ // overwrites some slots with the
+ // contents of others
+ void shuffle();
+#endif
+
+ private:
+ class impl;
+ boost::shared_ptr<impl> m_pimpl;
+ };
+
+ class TORRENT_EXPORT piece_manager : boost::noncopyable
+ {
+ public:
+
+ piece_manager(
+ const torrent_info& info
+ , const boost::filesystem::path& path);
+
+ ~piece_manager();
+
+ bool check_fastresume(aux::piece_checker_data& d
+ , std::vector<bool>& pieces, int& num_pieces, bool compact_mode);
+ std::pair<bool, float> check_files(std::vector<bool>& pieces
+ , int& num_pieces);
+
+ void release_files();
+
+ bool is_allocating() const;
+ void allocate_slots(int num_slots);
+ void mark_failed(int index);
+
+ unsigned long piece_crc(
+ int slot_index
+ , int block_size
+ , const std::bitset<256>& bitmask);
+ int slot_for_piece(int piece_index) const;
+
+ size_type read(
+ char* buf
+ , int piece_index
+ , int offset
+ , int size);
+
+ void write(
+ const char* buf
+ , int piece_index
+ , int offset
+ , int size);
+
+ boost::filesystem::path const& save_path() const;
+ bool move_storage(boost::filesystem::path const&);
+
+ // fills the vector that maps all allocated
+ // slots to the piece that is stored (or
+ // partially stored) there. -2 is the index
+ // of unassigned pieces and -1 is unallocated
+ void export_piece_map(std::vector<int>& pieces) const;
+
+ private:
+ class impl;
+ std::auto_ptr<impl> m_pimpl;
+ };
+
+}
+
+#endif // TORRENT_STORAGE_HPP_INCLUDED
+
diff --git a/library/include/libtorrent/torrent.hpp b/library/include/libtorrent/torrent.hpp
new file mode 100755
index 000000000..17bdc50d7
--- /dev/null
+++ b/library/include/libtorrent/torrent.hpp
@@ -0,0 +1,654 @@
+/*
+
+Copyright (c) 2003, 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_TORRENT_HPP_INCLUDE
+#define TORRENT_TORRENT_HPP_INCLUDE
+
+#include <algorithm>
+#include <vector>
+#include <set>
+#include <list>
+#include <iostream>
+
+#ifdef _MSC_VER
+#pragma warning(push, 1)
+#endif
+
+#include <boost/limits.hpp>
+#include <boost/filesystem/path.hpp>
+#include <boost/date_time/posix_time/posix_time.hpp>
+#include <boost/tuple/tuple.hpp>
+#include <boost/enable_shared_from_this.hpp>
+#include <boost/scoped_ptr.hpp>
+
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+#include "libtorrent/torrent_handle.hpp"
+#include "libtorrent/entry.hpp"
+#include "libtorrent/torrent_info.hpp"
+#include "libtorrent/socket.hpp"
+#include "libtorrent/policy.hpp"
+#include "libtorrent/tracker_manager.hpp"
+#include "libtorrent/stat.hpp"
+#include "libtorrent/alert.hpp"
+#include "libtorrent/resource_request.hpp"
+#include "libtorrent/piece_picker.hpp"
+#include "libtorrent/config.hpp"
+#include "libtorrent/escape_string.hpp"
+
+namespace libtorrent
+{
+#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
+ struct logger;
+#endif
+
+ class piece_manager;
+
+ namespace aux
+ {
+ struct session_impl;
+ struct piece_checker_data;
+ }
+
+ int div_round_up(int numerator, int denominator);
+ std::pair<int, int> req_to_offset(std::pair<int, int> req, int total_size);
+ std::pair<int, int> offset_to_req(std::pair<int, int> offset, int total_size);
+
+ // a torrent is a class that holds information
+ // for a specific download. It updates itself against
+ // the tracker
+ class TORRENT_EXPORT torrent: public request_callback
+ , public boost::enable_shared_from_this<torrent>
+ {
+ public:
+
+ torrent(
+ aux::session_impl& ses
+ , aux::checker_impl& checker
+ , torrent_info const& tf
+ , boost::filesystem::path const& save_path
+ , tcp::endpoint const& net_interface
+ , bool compact_mode
+ , int block_size
+ , session_settings const& s);
+
+ // used with metadata-less torrents
+ // (the metadata is downloaded from the peers)
+ torrent(
+ aux::session_impl& ses
+ , aux::checker_impl& checker
+ , char const* tracker_url
+ , sha1_hash const& info_hash
+ , boost::filesystem::path const& save_path
+ , tcp::endpoint const& net_interface
+ , bool compact_mode
+ , int block_size
+ , session_settings const& s);
+
+ ~torrent();
+
+ // this is called when the torrent has metadata.
+ // it will initialize the storage and the piece-picker
+ void init();
+
+ // this will flag the torrent as aborted. The main
+ // loop in session_impl will check for this state
+ // on all torrents once every second, and take
+ // the necessary actions then.
+ void abort();
+ bool is_aborted() const { return m_abort; }
+
+ // returns true if this torrent is being allocated
+ // by the checker thread.
+ bool is_allocating() const;
+
+ session_settings const& settings() const;
+
+ void set_sequenced_download_threshold(int threshold);
+
+ // is called every second by session. This will
+ // caclulate the upload/download and number
+ // of connections this torrent needs. And prepare
+ // it for being used by allocate_resources.
+ void second_tick(stat& accumulator, float tick_interval);
+
+ // debug purpose only
+ void print(std::ostream& os) const;
+
+ // this is called from the peer_connection for
+ // each piece of metadata it receives
+ void metadata_progress(int total_size, int received);
+
+ bool check_fastresume(aux::piece_checker_data&);
+ std::pair<bool, float> check_files();
+ void files_checked(std::vector<piece_picker::downloading_piece> const&
+ unfinished_pieces);
+
+ stat statistics() const { return m_stat; }
+ size_type bytes_left() const;
+ boost::tuples::tuple<size_type, size_type> bytes_done() const;
+ size_type quantized_bytes_done() const;
+
+ void pause();
+ void resume();
+ bool is_paused() const { return m_paused; }
+
+ void filter_piece(int index, bool filter);
+ void filter_pieces(std::vector<bool> const& bitmask);
+ bool is_piece_filtered(int index) const;
+ void filtered_pieces(std::vector<bool>& bitmask) const;
+
+ void filter_files(std::vector<bool> const& files);
+
+ torrent_status status() const;
+ void file_progress(std::vector<float>& fp) const;
+
+ void use_interface(const char* net_interface);
+ tcp::endpoint const& get_interface() const { return m_net_interface; }
+
+ void connect_to_url_seed(std::string const& url);
+ peer_connection& connect_to_peer(tcp::endpoint const& a);
+
+ void set_ratio(float ratio)
+ { assert(ratio >= 0.0f); m_ratio = ratio; }
+
+ float ratio() const
+ { return m_ratio; }
+
+// --------------------------------------------
+ // PEER MANAGEMENT
+
+ // add or remove a url that will be attempted for
+ // finding the file(s) in this torrent.
+ void add_url_seed(std::string const& url)
+ { m_web_seeds.insert(url); }
+
+ void remove_url_seed(std::string const& url)
+ { m_web_seeds.erase(url); }
+
+ // used by peer_connection to attach itself to a torrent
+ // since incoming connections don't know what torrent
+ // they're a part of until they have received an info_hash.
+ void attach_peer(peer_connection* p);
+
+ // this will remove the peer and make sure all
+ // the pieces it had have their reference counter
+ // decreased in the piece_picker
+ void remove_peer(peer_connection* p);
+
+ peer_connection* connection_for(tcp::endpoint const& a)
+ {
+ peer_iterator i = m_connections.find(a);
+ if (i == m_connections.end()) return 0;
+ return i->second;
+ }
+
+ // the number of peers that belong to this torrent
+ int num_peers() const { return (int)m_connections.size(); }
+ int num_seeds() const;
+
+ typedef std::map<tcp::endpoint, peer_connection*>::iterator peer_iterator;
+ typedef std::map<tcp::endpoint, peer_connection*>::const_iterator const_peer_iterator;
+
+ const_peer_iterator begin() const { return m_connections.begin(); }
+ const_peer_iterator end() const { return m_connections.end(); }
+
+ peer_iterator begin() { return m_connections.begin(); }
+ peer_iterator end() { return m_connections.end(); }
+
+
+// --------------------------------------------
+ // TRACKER MANAGEMENT
+
+ // these are callbacks called by the tracker_connection instance
+ // (either http_tracker_connection or udp_tracker_connection)
+ // when this torrent got a response from its tracker request
+ // or when a failure occured
+ virtual void tracker_response(
+ tracker_request const& r
+ , std::vector<peer_entry>& e, int interval
+ , int complete, int incomplete);
+ virtual void tracker_request_timed_out(
+ tracker_request const& r);
+ virtual void tracker_request_error(tracker_request const& r
+ , int response_code, const std::string& str);
+ virtual void tracker_warning(std::string const& msg);
+
+ // generates a request string for sending
+ // to the tracker
+ tracker_request generate_tracker_request();
+
+ // if no password and username is set
+ // this will return an empty string, otherwise
+ // it will concatenate the login and password
+ // ready to be sent over http (but without
+ // base64 encoding).
+ std::string tracker_login() const;
+
+ // returns the absolute time when the next tracker
+ // announce will take place.
+ boost::posix_time::ptime next_announce() const;
+
+ // returns true if it is time for this torrent to make another
+ // tracker request
+ bool should_request();
+
+ // forcefully sets next_announce to the current time
+ void force_tracker_request();
+ void force_tracker_request(boost::posix_time::ptime);
+
+ // sets the username and password that will be sent to
+ // the tracker
+ void set_tracker_login(std::string const& name, std::string const& pw);
+
+ // the tcp::endpoint of the tracker that we managed to
+ // announce ourself at the last time we tried to announce
+ const tcp::endpoint& current_tracker() const;
+
+// --------------------------------------------
+ // PIECE MANAGEMENT
+
+ // returns true if we have downloaded the given piece
+ bool have_piece(int index) const
+ {
+ assert(index >= 0 && index < (signed)m_have_pieces.size());
+ return m_have_pieces[index];
+ }
+
+ const std::vector<bool>& pieces() const
+ { return m_have_pieces; }
+
+ int num_pieces() const { return m_num_pieces; }
+
+ // when we get a have- or bitfield- messages, this is called for every
+ // piece a peer has gained.
+ void peer_has(int index)
+ {
+ assert(m_picker.get());
+ assert(index >= 0 && index < (signed)m_have_pieces.size());
+ m_picker->inc_refcount(index);
+ }
+
+ // when peer disconnects, this is called for every piece it had
+ void peer_lost(int index)
+ {
+ assert(m_picker.get());
+ assert(index >= 0 && index < (signed)m_have_pieces.size());
+ m_picker->dec_refcount(index);
+ }
+
+ int block_size() const { assert(m_block_size > 0); return m_block_size; }
+
+ // this will tell all peers that we just got his piece
+ // and also let the piece picker know that we have this piece
+ // so it wont pick it for download
+ void announce_piece(int index);
+
+ void disconnect_all();
+
+ // this is called wheh the torrent has completed
+ // the download. It will post an event, disconnect
+ // all seeds and let the tracker know we're finished.
+ void completed();
+
+ // this is the asio callback that is called when a name
+ // lookup for a web seed is completed.
+ void on_name_lookup(asio::error const& e, tcp::resolver::iterator i
+ , std::string url);
+
+ // this is called when the torrent has finished. i.e.
+ // all the pieces we have not filtered have been downloaded.
+ // If no pieces are filtered, this is called first and then
+ // completed() is called immediately after it.
+ void finished();
+
+ bool verify_piece(int piece_index);
+
+ // this is called from the peer_connection
+ // each time a piece has failed the hash
+ // test
+ void piece_failed(int index);
+ void received_redundant_data(int num_bytes)
+ { assert(num_bytes > 0); m_total_redundant_bytes += num_bytes; }
+
+ float priority() const
+ { return m_priority; }
+
+ void set_priority(float p)
+ {
+ assert(p >= 0.f && p <= 1.f);
+ m_priority = p;
+ }
+
+ bool is_seed() const
+ {
+ return valid_metadata()
+ && m_num_pieces == m_torrent_file.num_pieces();
+ }
+
+ boost::filesystem::path save_path() const;
+ alert_manager& alerts() const;
+ piece_picker& picker()
+ {
+ assert(m_picker.get());
+ return *m_picker;
+ }
+ policy& get_policy()
+ {
+ assert(m_policy);
+ return *m_policy;
+ }
+ piece_manager& filesystem();
+ torrent_info const& torrent_file() const
+ { return m_torrent_file; }
+
+ std::vector<announce_entry> const& trackers() const
+ { return m_trackers; }
+
+ void replace_trackers(std::vector<announce_entry> const& urls);
+
+ torrent_handle get_handle() const;
+
+ // LOGGING
+#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
+ logger* spawn_logger(const char* title);
+
+ virtual void debug_log(const std::string& line);
+#endif
+
+ // DEBUG
+#ifndef NDEBUG
+ void check_invariant() const;
+#endif
+
+// --------------------------------------------
+ // RESOURCE MANAGEMENT
+
+ // this will distribute the given upload/download
+ // quotas and number of connections, among the peers
+ void distribute_resources();
+
+ resource_request m_ul_bandwidth_quota;
+ resource_request m_dl_bandwidth_quota;
+ resource_request m_uploads_quota;
+ resource_request m_connections_quota;
+
+ void set_peer_upload_limit(tcp::endpoint ip, int limit);
+ void set_peer_download_limit(tcp::endpoint ip, int limit);
+
+ void set_upload_limit(int limit);
+ void set_download_limit(int limit);
+ void set_max_uploads(int limit);
+ void set_max_connections(int limit);
+ bool move_storage(boost::filesystem::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; }
+ std::vector<char> const& metadata() const;
+
+ bool received_metadata(
+ char const* buf
+ , int size
+ , int offset
+ , int total_size);
+
+ // returns a range of the metadata that
+ // we should request.
+ std::pair<int, int> metadata_request();
+ void cancel_metadata_request(std::pair<int, int> req);
+
+ private:
+
+ void try_next_tracker();
+ int prioritize_tracker(int tracker_index);
+
+ torrent_info m_torrent_file;
+
+ // is set to true when the torrent has
+ // been aborted.
+ bool m_abort;
+
+ // is true if this torrent has been paused
+ bool m_paused;
+ // this is true from the time when the torrent was
+ // paused to the time should_request() is called
+ bool m_just_paused;
+
+ tracker_request::event_t m_event;
+
+ void parse_response(const entry& e, std::vector<peer_entry>& peer_list);
+
+ // the size of a request block
+ // each piece is divided into these
+ // blocks when requested
+ int m_block_size;
+
+ // 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 time of next tracker request
+ boost::posix_time::ptime m_next_request;
+
+ // -----------------------------
+ // DATA FROM TRACKER RESPONSE
+
+ // the number number of seconds between requests
+ // from the tracker
+ int m_duration;
+
+ // the scrape data from the tracker response, this
+ // is optional and may be -1.
+ int m_complete;
+ int m_incomplete;
+
+#ifndef NDEBUG
+ public:
+#endif
+ std::map<tcp::endpoint, peer_connection*> m_connections;
+#ifndef NDEBUG
+ private:
+#endif
+
+ // The list of web seeds in this torrent. Seeds
+ // with fatal errors are removed from the set
+ std::set<std::string> m_web_seeds;
+
+ // urls of the web seeds that we are currently
+ // resolving the address for
+ std::set<std::string> m_resolving_web_seeds;
+
+ // used to resolve the names of web seeds
+ tcp::resolver m_host_resolver;
+
+#ifndef TORRENT_DISABLE_DHT
+ static void on_dht_announce_response_disp(boost::weak_ptr<torrent> t
+ , std::vector<tcp::endpoint> const& peers);
+ deadline_timer m_dht_announce_timer;
+ void on_dht_announce(asio::error const& e);
+ void on_dht_announce_response(std::vector<tcp::endpoint> const& peers);
+#endif
+
+ // this is the upload and download statistics for the whole torrent.
+ // it's updated from all its peers once every second.
+ libtorrent::stat m_stat;
+
+ // -----------------------------
+
+ boost::shared_ptr<policy> m_policy;
+
+ // a back reference to the session
+ // this torrent belongs to.
+ aux::session_impl& m_ses;
+ aux::checker_impl& m_checker;
+
+ boost::scoped_ptr<piece_picker> m_picker;
+
+ std::vector<announce_entry> m_trackers;
+ // this is an index into m_torrent_file.trackers()
+ int m_last_working_tracker;
+ int m_currently_trying_tracker;
+ // the number of connection attempts that has
+ // failed in a row, this is currently used to
+ // determine the timeout until next try.
+ int m_failed_trackers;
+
+ // this is a counter that is increased every
+ // second, and when it reaches 10, the policy::pulse()
+ // is called and the time scaler is reset to 0.
+ int m_time_scaler;
+
+ // this is the priority of this torrent. It is used
+ // to weight the assigned upload bandwidth between peers
+ // it should be within the range [0, 1]
+ float m_priority;
+
+ // the bitmask that says which pieces we have
+ std::vector<bool> m_have_pieces;
+
+ // the number of pieces we have. The same as
+ // std::accumulate(m_have_pieces.begin(),
+ // m_have_pieces.end(), 0)
+ int m_num_pieces;
+
+ // is false by default and set to
+ // true when the first tracker reponse
+ // is received
+ bool m_got_tracker_response;
+
+ // the upload/download ratio that each peer
+ // tries to maintain.
+ // 0 is infinite
+ float m_ratio;
+
+ // the number of bytes that has been
+ // downloaded that failed the hash-test
+ size_type m_total_failed_bytes;
+ size_type m_total_redundant_bytes;
+
+ std::string m_username;
+ std::string m_password;
+
+ // the network interface all outgoing connections
+ // are opened through
+ tcp::endpoint m_net_interface;
+
+ // the max number of bytes this torrent
+ // can upload per second
+ int m_upload_bandwidth_limit;
+ int m_download_bandwidth_limit;
+
+ // this buffer is filled with the info-section of
+ // the metadata file while downloading it from
+ // peers, and while sending it.
+ // it is mutable because it's generated lazily
+ mutable std::vector<char> m_metadata;
+
+ // this is a bitfield of size 256, each bit represents
+ // a piece of the metadata. It is set to one if we
+ // have that piece. This vector may be empty
+ // (size 0) if we haven't received any metadata
+ // or if we already have all metadata
+ std::vector<bool> m_have_metadata;
+ // this vector keeps track of how many times each meatdata
+ // block has been requested
+ std::vector<int> m_requested_metadata;
+
+ boost::filesystem::path m_save_path;
+
+ // determines the storage state for this torrent.
+ const bool m_compact_mode;
+
+ int m_metadata_progress;
+ int m_metadata_size;
+
+ // defaults to 16 kiB, but can be set by the user
+ // when creating the torrent
+ const int m_default_block_size;
+
+ // this is set to false as long as the connections
+ // of this torrent hasn't been initialized. If we
+ // have metadata from the start, connections are
+ // initialized immediately, if we didn't have metadata,
+ // they are initialized right after files_checked().
+ // valid_resume_data() will return false as long as
+ // the connections aren't initialized, to avoid
+ // them from altering the piece-picker before it
+ // has been initialized with files_checked().
+ bool m_connections_initialized;
+
+ session_settings const& m_settings;
+
+#ifndef NDEBUG
+ // this is the amount downloaded when this torrent
+ // is started. i.e.
+ // total_done - m_initial_done <= total_payload_download
+ size_type m_initial_done;
+#endif
+ };
+
+ inline boost::posix_time::ptime torrent::next_announce() const
+ {
+ return m_next_request;
+ }
+
+ inline void torrent::force_tracker_request()
+ {
+ using boost::posix_time::second_clock;
+ m_next_request = second_clock::universal_time();
+ }
+
+ inline void torrent::force_tracker_request(boost::posix_time::ptime t)
+ {
+ namespace time = boost::posix_time;
+ m_next_request = t;
+ }
+
+ inline void torrent::set_tracker_login(
+ std::string const& name
+ , std::string const& pw)
+ {
+ m_username = name;
+ m_password = pw;
+ }
+
+}
+
+#endif // TORRENT_TORRENT_HPP_INCLUDED
+
diff --git a/library/include/libtorrent/torrent_handle.hpp b/library/include/libtorrent/torrent_handle.hpp
new file mode 100755
index 000000000..80d57fec7
--- /dev/null
+++ b/library/include/libtorrent/torrent_handle.hpp
@@ -0,0 +1,350 @@
+/*
+
+Copyright (c) 2003, 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_TORRENT_HANDLE_HPP_INCLUDED
+#define TORRENT_TORRENT_HANDLE_HPP_INCLUDED
+
+#include <vector>
+
+#ifdef _MSC_VER
+#pragma warning(push, 1)
+#endif
+
+#include <boost/date_time/posix_time/posix_time.hpp>
+
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+#include "libtorrent/peer_id.hpp"
+#include "libtorrent/peer_info.hpp"
+#include "libtorrent/piece_picker.hpp"
+#include "libtorrent/torrent_info.hpp"
+#include "libtorrent/config.hpp"
+
+namespace libtorrent
+{
+ namespace aux
+ {
+ struct session_impl;
+ struct checker_impl;
+ }
+
+ struct TORRENT_EXPORT duplicate_torrent: std::exception
+ {
+ virtual const char* what() const throw()
+ { return "torrent already exists in session"; }
+ };
+
+ struct TORRENT_EXPORT invalid_handle: std::exception
+ {
+ virtual const char* what() const throw()
+ { return "invalid torrent handle used"; }
+ };
+
+ struct TORRENT_EXPORT torrent_status
+ {
+ torrent_status()
+ : state(queued_for_checking)
+ , paused(false)
+ , progress(0.f)
+ , total_download(0)
+ , total_upload(0)
+ , total_payload_download(0)
+ , total_payload_upload(0)
+ , total_failed_bytes(0)
+ , total_redundant_bytes(0)
+ , download_rate(0)
+ , upload_rate(0)
+ , download_payload_rate(0)
+ , upload_payload_rate(0)
+ , num_peers(0)
+ , num_complete(-1)
+ , num_incomplete(-1)
+ , pieces(0)
+ , num_pieces(0)
+ , total_done(0)
+ , total_wanted_done(0)
+ , total_wanted(0)
+ , num_seeds(0)
+ , distributed_copies(0.f)
+ , block_size(0)
+ {}
+
+ enum state_t
+ {
+ queued_for_checking,
+ checking_files,
+ connecting_to_tracker,
+ downloading_metadata,
+ downloading,
+ finished,
+ seeding,
+ allocating
+ };
+
+ state_t state;
+ bool paused;
+ float progress;
+ boost::posix_time::time_duration next_announce;
+ boost::posix_time::time_duration announce_interval;
+
+ std::string current_tracker;
+
+ // transferred this session!
+ // total, payload plus protocol
+ size_type total_download;
+ size_type total_upload;
+
+ // payload only
+ size_type total_payload_download;
+ size_type total_payload_upload;
+
+ // the amount of payload bytes that
+ // has failed their hash test
+ size_type total_failed_bytes;
+
+ // the number of payload bytes that
+ // has been received redundantly.
+ size_type total_redundant_bytes;
+
+ // current transfer rate
+ // payload plus protocol
+ float download_rate;
+ float upload_rate;
+
+ // the rate of payload that is
+ // sent and received
+ float download_payload_rate;
+ float upload_payload_rate;
+
+ // the number of peers this torrent
+ // is connected to.
+ int num_peers;
+
+ // if the tracker sends scrape info in its
+ // announce reply, these fields will be
+ // set to the total number of peers that
+ // have the whole file and the total number
+ // of peers that are still downloading
+ int num_complete;
+ int num_incomplete;
+
+ const std::vector<bool>* pieces;
+
+ // this is the number of pieces the client has
+ // downloaded. it is equal to:
+ // std::accumulate(pieces->begin(), pieces->end());
+ int num_pieces;
+
+ // the number of bytes of the file we have
+ // including pieces that may have been filtered
+ // after we downloaded them
+ size_type total_done;
+
+ // the number of bytes we have of those that we
+ // want. i.e. not counting bytes from pieces that
+ // are filtered as not wanted.
+ size_type total_wanted_done;
+
+ // the total number of bytes we want to download
+ // this may be smaller than the total torrent size
+ // in case any pieces are filtered as not wanted
+ size_type total_wanted;
+
+ // the number of peers this torrent is connected to
+ // that are seeding.
+ int num_seeds;
+
+ // the number of distributed copies of the file.
+ // note that one copy may be spread out among many peers.
+ //
+ // the whole number part tells how many copies
+ // there are of the rarest piece(s)
+ //
+ // the fractional part tells the fraction of pieces that
+ // have more copies than the rarest piece(s).
+ float distributed_copies;
+
+ // the block size that is used in this torrent. i.e.
+ // the number of bytes each piece request asks for
+ // and each bit in the download queue bitfield represents
+ int block_size;
+ };
+
+ struct TORRENT_EXPORT partial_piece_info
+ {
+ enum { max_blocks_per_piece = piece_picker::max_blocks_per_piece };
+ 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];
+ };
+
+ struct TORRENT_EXPORT torrent_handle
+ {
+ friend class invariant_access;
+ friend struct aux::session_impl;
+ friend class torrent;
+
+ torrent_handle(): m_ses(0), m_chk(0) {}
+
+ void get_peer_info(std::vector<peer_info>& v) const;
+ bool send_chat_message(tcp::endpoint ip, std::string message) const;
+ torrent_status status() const;
+ void get_download_queue(std::vector<partial_piece_info>& queue) const;
+
+ // fills the specified vector with the download progress [0, 1]
+ // of each file in the torrent. The files are ordered as in
+ // the torrent_info.
+ void file_progress(std::vector<float>& progress);
+
+ std::vector<announce_entry> const& trackers() const;
+ void replace_trackers(std::vector<announce_entry> const&) const;
+
+ void add_url_seed(std::string const& url);
+
+ bool has_metadata() const;
+ const torrent_info& get_torrent_info() const;
+ bool is_valid() const;
+
+ bool is_seed() const;
+ bool is_paused() const;
+ void pause() const;
+ void resume() const;
+
+ // marks the piece with the given index as filtered
+ // it will not be downloaded
+ void filter_piece(int index, bool filter) const;
+ void filter_pieces(std::vector<bool> const& pieces) const;
+ bool is_piece_filtered(int index) const;
+ std::vector<bool> filtered_pieces() const;
+
+ // marks the file with the given index as filtered
+ // it will not be downloaded
+ void filter_files(std::vector<bool> const& files) const;
+
+ // set the interface to bind outgoing connections
+ // to.
+ void use_interface(const char* net_interface) const;
+
+ entry write_resume_data() const;
+
+ // kind of similar to get_torrent_info() but this
+ // is lower level, returning the exact info-part of
+ // the .torrent file. When hashed, this buffer
+ // will produce the info hash. The reference is valid
+ // only as long as the torrent is running.
+ std::vector<char> const& metadata() const;
+
+ // forces this torrent to reannounce
+ // (make a rerequest from the tracker)
+ void force_reannounce() const;
+
+ // forces a reannounce in the specified amount of time.
+ // This overrides the default announce interval, and no
+ // announce will take place until the given time has
+ // timed out.
+ void force_reannounce(boost::posix_time::time_duration) const;
+
+ // TODO: add a feature where the user can tell the torrent
+ // to finish all pieces currently in the pipeline, and then
+ // abort the torrent.
+
+ void set_upload_limit(int limit) const;
+ void set_download_limit(int limit) const;
+ void set_sequenced_download_threshold(int threshold) const;
+
+ void set_peer_upload_limit(tcp::endpoint ip, int limit) const;
+ void set_peer_download_limit(tcp::endpoint ip, int limit) const;
+
+ // manually connect a peer
+ void connect_peer(tcp::endpoint const& adr) const;
+
+ // valid ratios are 0 (infinite ratio) or [ 1.0 , inf )
+ // 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;
+
+ // -1 means unlimited unchokes
+ void set_max_uploads(int max_uploads) const;
+
+ // -1 means unlimited connections
+ void set_max_connections(int max_connections) const;
+
+ void set_tracker_login(std::string const& name
+ , 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;
+
+ const sha1_hash& info_hash() const
+ { return m_info_hash; }
+
+ bool operator==(const torrent_handle& h) const
+ { return m_info_hash == h.m_info_hash; }
+
+ bool operator!=(const torrent_handle& h) const
+ { return m_info_hash != h.m_info_hash; }
+
+ bool operator<(const torrent_handle& h) const
+ { return m_info_hash < h.m_info_hash; }
+
+ private:
+
+ torrent_handle(aux::session_impl* s,
+ aux::checker_impl* c,
+ const sha1_hash& h)
+ : m_ses(s)
+ , m_chk(c)
+ , m_info_hash(h)
+ {
+ assert(m_ses != 0);
+ }
+
+#ifndef NDEBUG
+ void check_invariant() const;
+#endif
+
+ aux::session_impl* m_ses;
+ aux::checker_impl* m_chk;
+ sha1_hash m_info_hash;
+
+ };
+
+
+}
+
+#endif // TORRENT_TORRENT_HANDLE_HPP_INCLUDED
diff --git a/library/include/libtorrent/torrent_info.hpp b/library/include/libtorrent/torrent_info.hpp
new file mode 100755
index 000000000..4e6e20d9e
--- /dev/null
+++ b/library/include/libtorrent/torrent_info.hpp
@@ -0,0 +1,242 @@
+/*
+
+Copyright (c) 2003, 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_TORRENT_INFO_HPP_INCLUDE
+#define TORRENT_TORRENT_INFO_HPP_INCLUDE
+
+
+#include <string>
+#include <vector>
+
+#ifdef _MSC_VER
+#pragma warning(push, 1)
+#endif
+
+#include <boost/date_time/posix_time/posix_time.hpp>
+#include <boost/date_time/gregorian/gregorian_types.hpp>
+#include <boost/optional.hpp>
+#include <boost/filesystem/path.hpp>
+#include <boost/shared_ptr.hpp>
+
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+#include "libtorrent/entry.hpp"
+#include "libtorrent/socket.hpp"
+#include "libtorrent/peer_id.hpp"
+#include "libtorrent/size_type.hpp"
+#include "libtorrent/peer_request.hpp"
+#include "libtorrent/config.hpp"
+
+namespace libtorrent
+{
+
+ struct TORRENT_EXPORT file_entry
+ {
+ boost::filesystem::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;
+ };
+
+ struct TORRENT_EXPORT file_slice
+ {
+ int file_index;
+ size_type offset;
+ size_type size;
+ };
+
+ struct TORRENT_EXPORT announce_entry
+ {
+ announce_entry(std::string const& u): url(u), tier(0) {}
+ std::string url;
+ int tier;
+ };
+
+ struct TORRENT_EXPORT invalid_torrent_file: std::exception
+ {
+ virtual const char* what() const throw() { return "invalid torrent file"; }
+ };
+
+ class TORRENT_EXPORT torrent_info
+ {
+ public:
+
+ torrent_info();
+ torrent_info(sha1_hash const& info_hash);
+ torrent_info(entry const& torrent_file);
+ ~torrent_info();
+
+ entry create_torrent() const;
+ entry create_info_metadata() const;
+ void set_comment(char const* str);
+ void set_creator(char const* str);
+ 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_url_seed(std::string const& url);
+
+ std::vector<file_slice> map_block(int piece, size_type offset, int size) const;
+ peer_request map_file(int file, size_type offset, int size) const;
+
+ std::vector<std::string> const& url_seeds() const { return m_url_seeds; }
+
+ typedef std::vector<file_entry>::const_iterator file_iterator;
+ typedef std::vector<file_entry>::const_reverse_iterator reverse_file_iterator;
+
+ // list the files in the torrent file
+ file_iterator begin_files() const { return m_files.begin(); }
+ file_iterator end_files() const { return m_files.end(); }
+ reverse_file_iterator rbegin_files() const { return m_files.rbegin(); }
+ reverse_file_iterator rend_files() const { return m_files.rend(); }
+
+ int num_files() const
+ { assert(m_piece_length > 0); return (int)m_files.size(); }
+ const file_entry& file_at(int index) const
+ { assert(index >= 0 && index < (int)m_files.size()); return m_files[index]; }
+
+ const std::vector<announce_entry>& trackers() const { return m_urls; }
+
+ size_type total_size() const { assert(m_piece_length > 0); return m_total_size; }
+ size_type piece_length() const { assert(m_piece_length > 0); return m_piece_length; }
+ int num_pieces() const { assert(m_piece_length > 0); return (int)m_piece_hash.size(); }
+ const sha1_hash& info_hash() const { return m_info_hash; }
+ const std::string& name() const { assert(m_piece_length > 0); return m_name; }
+ void print(std::ostream& os) const;
+ bool is_valid() const { return m_piece_length > 0; }
+
+ bool priv() const { return m_private; }
+ void set_priv(bool v) { m_private = v; }
+
+ void convert_file_names();
+
+ size_type piece_size(int index) const;
+
+ const sha1_hash& hash_for_piece(int index) const
+ {
+ assert(index >= 0);
+ assert(index < (int)m_piece_hash.size());
+ return m_piece_hash[index];
+ }
+
+ boost::optional<boost::posix_time::ptime>
+ creation_date() const;
+
+ const std::string& creator() const
+ { return m_created_by; }
+
+ const std::string& comment() const
+ { return m_comment; }
+
+ // dht nodes to add to the routing table/bootstrap from
+ typedef std::vector<std::pair<std::string, int> > nodes_t;
+
+ nodes_t const& nodes() const
+ { return m_nodes; }
+
+ void add_node(std::pair<std::string, int> const& node);
+
+ void parse_info_section(entry const& e);
+
+ private:
+
+ void read_torrent_info(const entry& libtorrent);
+
+ // the urls to the trackers
+ std::vector<announce_entry> m_urls;
+
+ std::vector<std::string> m_url_seeds;
+
+ // the length of one piece
+ // if this is 0, the torrent_info is
+ // in an uninitialized state
+ size_type m_piece_length;
+
+ // the sha-1 hashes of each piece
+ std::vector<sha1_hash> m_piece_hash;
+
+ // the list of files that this torrent consists of
+ std::vector<file_entry> m_files;
+
+ nodes_t m_nodes;
+
+ // the sum of all filesizes
+ size_type m_total_size;
+
+ // the hash that identifies this torrent
+ // is mutable because it's calculated
+ // lazily
+ mutable sha1_hash m_info_hash;
+
+ std::string m_name;
+
+ // if a creation date is found in the torrent file
+ // this will be set to that, otherwise it'll be
+ // 1970, Jan 1
+ boost::posix_time::ptime m_creation_date;
+
+ // if a comment is found in the torrent file
+ // this will be set to that comment
+ std::string m_comment;
+
+ // an optional string naming the software used
+ // to create the torrent file
+ std::string m_created_by;
+
+ // this is used when creating a torrent. If there's
+ // only one file there are cases where it's impossible
+ // to know if it should be written as a multifile torrent
+ // or not. e.g. test/test there's one file and one directory
+ // and they have the same name.
+ bool m_multifile;
+
+ // this is true if the torrent is private. i.e., is should not
+ // be announced on the dht
+ bool m_private;
+
+ // contains any non-parsed entries from the info-section
+ // these are kept in order to be able to accurately
+ // reproduce the info-section when sending the metadata
+ // to peers.
+ entry m_extra_info;
+ };
+
+}
+
+#endif // TORRENT_TORRENT_INFO_HPP_INCLUDED
+
diff --git a/library/include/libtorrent/tracker_manager.hpp b/library/include/libtorrent/tracker_manager.hpp
new file mode 100755
index 000000000..a45f88b59
--- /dev/null
+++ b/library/include/libtorrent/tracker_manager.hpp
@@ -0,0 +1,248 @@
+/*
+
+Copyright (c) 2003, 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_TRACKER_MANAGER_HPP_INCLUDED
+#define TORRENT_TRACKER_MANAGER_HPP_INCLUDED
+
+#include <vector>
+#include <string>
+#include <utility>
+#include <ctime>
+
+#ifdef _MSC_VER
+#pragma warning(push, 1)
+#endif
+
+#include <boost/shared_ptr.hpp>
+#include <boost/date_time/posix_time/posix_time.hpp>
+#include <boost/cstdint.hpp>
+#include <boost/weak_ptr.hpp>
+#include <boost/intrusive_ptr.hpp>
+#include <boost/thread/mutex.hpp>
+#include <boost/thread/recursive_mutex.hpp>
+#include <boost/tuple/tuple.hpp>
+
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+#include "libtorrent/socket.hpp"
+#include "libtorrent/entry.hpp"
+#include "libtorrent/session_settings.hpp"
+#include "libtorrent/peer_id.hpp"
+#include "libtorrent/peer.hpp"
+#include "libtorrent/config.hpp"
+
+namespace libtorrent
+{
+ struct request_callback;
+ class tracker_manager;
+ struct timeout_handler;
+ struct tracker_connection;
+
+ // encodes a string using the base64 scheme
+ TORRENT_EXPORT std::string base64encode(const std::string& s);
+
+ // returns -1 if gzip header is invalid or the header size in bytes
+ TORRENT_EXPORT int gzip_header(const char* buf, int size);
+
+ TORRENT_EXPORT boost::tuple<std::string, std::string, int, std::string>
+ parse_url_components(std::string url);
+
+ struct TORRENT_EXPORT tracker_request
+ {
+ tracker_request()
+ : kind(announce_request)
+ , event(none)
+ , key(0)
+ , num_want(0)
+ {}
+
+ enum
+ {
+ announce_request,
+ scrape_request
+ } kind;
+
+ enum event_t
+ {
+ none,
+ completed,
+ started,
+ stopped
+ };
+
+ sha1_hash info_hash;
+ peer_id pid;
+ size_type downloaded;
+ size_type uploaded;
+ size_type left;
+ unsigned short listen_port;
+ event_t event;
+ std::string url;
+ int key;
+ int num_want;
+ };
+
+ struct TORRENT_EXPORT request_callback
+ {
+ friend class tracker_manager;
+ request_callback(): m_manager(0) {}
+ virtual ~request_callback() {}
+ virtual void tracker_warning(std::string const& msg) = 0;
+ virtual void tracker_response(
+ tracker_request const&
+ , std::vector<peer_entry>& peers
+ , int interval
+ , int complete
+ , int incomplete) = 0;
+ virtual void tracker_request_timed_out(
+ tracker_request const&) = 0;
+ virtual void tracker_request_error(
+ tracker_request const&
+ , int response_code
+ , const std::string& description) = 0;
+
+ tcp::endpoint m_tracker_address;
+
+#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
+ virtual void debug_log(const std::string& line) = 0;
+#endif
+ private:
+ tracker_manager* m_manager;
+ };
+
+ TORRENT_EXPORT bool inflate_gzip(
+ std::vector<char>& buffer
+ , tracker_request const& req
+ , 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
+ {
+ friend void intrusive_ptr_add_ref(timeout_handler const*);
+ friend void intrusive_ptr_release(timeout_handler const*);
+
+ timeout_handler(demuxer& d);
+
+ void set_timeout(int completion_timeout, int read_timeout);
+ void restart_read_timeout();
+ void cancel();
+
+ virtual void on_timeout() = 0;
+ virtual ~timeout_handler() {}
+
+ private:
+
+ void timeout_callback(asio::error const&);
+
+ boost::intrusive_ptr<timeout_handler> self()
+ { return boost::intrusive_ptr<timeout_handler>(this); }
+
+ demuxer& m_demuxer;
+ // used for timeouts
+ // this is set when the request has been sent
+ boost::posix_time::ptime m_start_time;
+ // this is set every time something is received
+ boost::posix_time::ptime m_read_time;
+ // the asio async operation
+ asio::deadline_timer m_timeout;
+
+ int m_completion_timeout;
+ int m_read_timeout;
+
+ typedef boost::mutex mutex_t;
+ mutable mutex_t m_mutex;
+ mutable int m_refs;
+ };
+
+ struct TORRENT_EXPORT tracker_connection
+ : timeout_handler
+ {
+ tracker_connection(tracker_manager& man
+ , tracker_request req
+ , demuxer& d
+ , boost::weak_ptr<request_callback> r);
+
+ request_callback& requester();
+ virtual ~tracker_connection() {}
+
+ tracker_request const& tracker_req() const { return m_req; }
+ bool has_requester() const { return !m_requester.expired(); }
+
+ void fail(int code, char const* msg);
+ void fail_timeout();
+ void close();
+
+ protected:
+ boost::weak_ptr<request_callback> m_requester;
+ private:
+ tracker_manager& m_man;
+ const tracker_request m_req;
+ };
+
+ class TORRENT_EXPORT tracker_manager: boost::noncopyable
+ {
+ public:
+
+ tracker_manager(const session_settings& s)
+ : m_settings(s) {}
+
+ void queue_request(
+ demuxer& d
+ , tracker_request r
+ , std::string const& auth
+ , boost::weak_ptr<request_callback> c
+ = boost::weak_ptr<request_callback>());
+ void abort_all_requests();
+
+ void remove_request(tracker_connection const*);
+ bool empty() const;
+
+ private:
+
+ typedef boost::recursive_mutex mutex_t;
+ mutable mutex_t m_mutex;
+
+ typedef std::list<boost::intrusive_ptr<tracker_connection> >
+ tracker_connections_t;
+ tracker_connections_t m_connections;
+ session_settings const& m_settings;
+ };
+}
+
+#endif // TORRENT_TRACKER_MANAGER_HPP_INCLUDED
+
diff --git a/library/include/libtorrent/udp_tracker_connection.hpp b/library/include/libtorrent/udp_tracker_connection.hpp
new file mode 100755
index 000000000..43671ed86
--- /dev/null
+++ b/library/include/libtorrent/udp_tracker_connection.hpp
@@ -0,0 +1,122 @@
+/*
+
+Copyright (c) 2003, 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_UDP_TRACKER_CONNECTION_HPP_INCLUDED
+#define TORRENT_UDP_TRACKER_CONNECTION_HPP_INCLUDED
+
+#include <vector>
+#include <string>
+#include <utility>
+#include <ctime>
+
+#ifdef _MSC_VER
+#pragma warning(push, 1)
+#endif
+
+#include <boost/shared_ptr.hpp>
+#include <boost/date_time/posix_time/posix_time.hpp>
+#include <boost/cstdint.hpp>
+
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+#include "libtorrent/socket.hpp"
+#include "libtorrent/entry.hpp"
+#include "libtorrent/session_settings.hpp"
+#include "libtorrent/peer_id.hpp"
+#include "libtorrent/peer.hpp"
+#include "libtorrent/tracker_manager.hpp"
+#include "libtorrent/config.hpp"
+
+namespace libtorrent
+{
+ class TORRENT_EXPORT udp_tracker_connection: public tracker_connection
+ {
+ friend class tracker_manager;
+ public:
+
+ udp_tracker_connection(
+ demuxer& d
+ , tracker_manager& man
+ , tracker_request const& req
+ , std::string const& hostname
+ , unsigned short port
+ , boost::weak_ptr<request_callback> c
+ , session_settings const& stn);
+
+ private:
+
+ enum action_t
+ {
+ action_connect,
+ action_announce,
+ action_scrape,
+ action_error
+ };
+
+ boost::intrusive_ptr<udp_tracker_connection> self()
+ { return boost::intrusive_ptr<udp_tracker_connection>(this); }
+
+ void name_lookup(asio::error const& error, tcp::resolver::iterator i);
+ void timeout(asio::error const& error);
+
+ void send_udp_connect();
+ void connect_response(asio::error const& error, std::size_t bytes_transferred);
+
+ void send_udp_announce();
+ void announce_response(asio::error const& error, std::size_t bytes_transferred);
+
+ void send_udp_scrape();
+ void scrape_response(asio::error const& error, std::size_t bytes_transferred);
+
+ virtual void on_timeout();
+
+ tracker_manager& m_man;
+
+ tcp::resolver m_name_lookup;
+ int m_port;
+ boost::shared_ptr<datagram_socket> m_socket;
+ udp::endpoint m_target;
+ udp::endpoint m_sender;
+
+ int m_transaction_id;
+ boost::int64_t m_connection_id;
+ session_settings const& m_settings;
+ int m_attempts;
+ std::vector<char> m_buffer;
+ };
+
+}
+
+#endif // TORRENT_UDP_TRACKER_CONNECTION_HPP_INCLUDED
+
diff --git a/library/include/libtorrent/utf8.hpp b/library/include/libtorrent/utf8.hpp
new file mode 100644
index 000000000..7a31af804
--- /dev/null
+++ b/library/include/libtorrent/utf8.hpp
@@ -0,0 +1,160 @@
+/*
+ Copyright (C) 2004-2005 Cory Nelson
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+*/
+
+// namespaces added by Arvid Norberg
+
+#ifndef __UTF8_H__
+#define __UTF8_H__
+
+#include <string>
+#include <iterator>
+#include <stdexcept>
+
+namespace libtorrent {
+namespace detail {
+
+template<typename InputIterator>
+wchar_t decode_utf8_mb(InputIterator &iter, InputIterator last)
+{
+ if (iter == last) throw std::runtime_error("incomplete UTF-8 sequence");
+ if (((*iter) & 0xc0) != 0x80) throw std::runtime_error("invalid UTF-8 sequence");
+
+ return (wchar_t)((*iter++) & 0x3f);
+}
+
+template<typename InputIterator>
+wchar_t decode_utf8(InputIterator &iter, InputIterator last)
+{
+ wchar_t ret;
+
+ if (((*iter) & 0x80) == 0) // one byte
+ {
+ ret = *iter++;
+ }
+ else if (((*iter) & 0xe0) == 0xc0) // two bytes
+ {
+ wchar_t byte1 = (*iter++) & 0x1f;
+ wchar_t byte2 = decode_utf8_mb(iter, last);
+ ret = (byte1 << 6) | byte2;
+ }
+ else if (((*iter) & 0xf0) == 0xe0) // three bytes
+ {
+ wchar_t byte1 = (*iter++) & 0x0f;
+ wchar_t byte2 = decode_utf8_mb(iter, last);
+ wchar_t byte3 = decode_utf8_mb(iter, last);
+ ret = (byte1 << 12) | (byte2 << 6) | byte3;
+ }
+ // TODO: support surrogate pairs
+ else throw std::runtime_error("UTF-8 not convertable to UTF-16");
+
+ return ret;
+}
+
+template<typename InputIterator, typename OutputIterator>
+OutputIterator utf8_wchar(InputIterator first, InputIterator last, OutputIterator dest)
+{
+ for(; first!=last; ++dest)
+ *dest = decode_utf8(first, last);
+ return dest;
+}
+
+template<typename InputIterator, typename OutputIterator>
+void encode_wchar(InputIterator iter, OutputIterator &dest)
+{
+ if(*iter <= 0x007F)
+ {
+ *dest=(char)*iter;
+ ++dest;
+ }
+ else if(*iter <= 0x07FF)
+ {
+ *dest = (char)(
+ 0xC0 |
+ ((*iter & 0x07C0) >> 6)
+ );
+ ++dest;
+
+ *dest = (char)(
+ 0x80 |
+ (*iter & 0x003F)
+ );
+ ++dest;
+ }
+ else if(*iter <= 0xFFFF)
+ {
+ *dest = (char)(
+ 0xE0 |
+ ((*iter & 0xF000) >> 12)
+ );
+ ++dest;
+
+ *dest = (char)(
+ 0x80 |
+ ((*iter & 0x0FC0) >> 6)
+ );
+ ++dest;
+
+ *dest = (char)(
+ 0x80 |
+ (*iter & 0x003F)
+ );
+ ++dest;
+ }
+}
+
+template<typename InputIterator, typename OutputIterator>
+OutputIterator wchar_utf8(InputIterator first, InputIterator last, OutputIterator dest)
+{
+ for(; first!=last; ++first)
+ encode_wchar(first, dest);
+ return dest;
+}
+
+}
+
+inline void utf8_wchar(const std::string &utf8, std::wstring &wide)
+{
+ wide.clear();
+ detail::utf8_wchar(utf8.begin(), utf8.end(), std::insert_iterator<std::wstring>(wide, wide.end()));
+}
+
+inline std::wstring utf8_wchar(const std::string &str)
+{
+ std::wstring ret;
+ utf8_wchar(str, ret);
+ return ret;
+}
+
+inline void wchar_utf8(const std::wstring &wide, std::string &utf8)
+{
+ utf8.clear();
+ detail::wchar_utf8(wide.begin(), wide.end(), std::insert_iterator<std::string>(utf8, utf8.end()));
+}
+
+inline std::string wchar_utf8(const std::wstring &str)
+{
+ std::string ret;
+ wchar_utf8(str, ret);
+ return ret;
+}
+
+}
+
+#endif
diff --git a/library/include/libtorrent/version.hpp b/library/include/libtorrent/version.hpp
new file mode 100755
index 000000000..25c7a9f05
--- /dev/null
+++ b/library/include/libtorrent/version.hpp
@@ -0,0 +1,41 @@
+/*
+
+Copyright (c) 2003, 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_VERSION_HPP_INCLUDED
+#define TORRENT_VERSION_HPP_INCLUDED
+
+#define LIBTORRENT_VERSION_MAJOR 0
+#define LIBTORRENT_VERSION_MINOR 11
+
+#define LIBTORRENT_VERSION "0.11.0.0"
+
+#endif
diff --git a/library/include/libtorrent/web_peer_connection.hpp b/library/include/libtorrent/web_peer_connection.hpp
new file mode 100755
index 000000000..77f840409
--- /dev/null
+++ b/library/include/libtorrent/web_peer_connection.hpp
@@ -0,0 +1,169 @@
+/*
+
+Copyright (c) 2003, 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_WEB_PEER_CONNECTION_HPP_INCLUDED
+#define TORRENT_WEB_PEER_CONNECTION_HPP_INCLUDED
+
+#include <ctime>
+#include <algorithm>
+#include <vector>
+#include <deque>
+#include <string>
+
+#include "libtorrent/debug.hpp"
+
+#ifdef _MSC_VER
+#pragma warning(push, 1)
+#endif
+
+#include <boost/smart_ptr.hpp>
+#include <boost/weak_ptr.hpp>
+#include <boost/noncopyable.hpp>
+#include <boost/array.hpp>
+#include <boost/date_time/posix_time/posix_time.hpp>
+#include <boost/optional.hpp>
+#include <boost/cstdint.hpp>
+
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+#include "libtorrent/buffer.hpp"
+#include "libtorrent/peer_connection.hpp"
+#include "libtorrent/socket.hpp"
+#include "libtorrent/peer_id.hpp"
+#include "libtorrent/storage.hpp"
+#include "libtorrent/stat.hpp"
+#include "libtorrent/alert.hpp"
+#include "libtorrent/torrent_handle.hpp"
+#include "libtorrent/torrent.hpp"
+#include "libtorrent/allocate_resources.hpp"
+#include "libtorrent/peer_request.hpp"
+#include "libtorrent/piece_block_progress.hpp"
+#include "libtorrent/config.hpp"
+// parse_url
+#include "libtorrent/tracker_manager.hpp"
+// http_parser
+#include "libtorrent/http_tracker_connection.hpp"
+
+namespace libtorrent
+{
+ class torrent;
+
+ namespace detail
+ {
+ struct session_impl;
+ }
+
+ class TORRENT_EXPORT web_peer_connection
+ : public peer_connection
+ {
+ friend class invariant_access;
+ public:
+
+ // this is the constructor where the we are the active part.
+ // The peer_conenction should handshake and verify that the
+ // other end has the correct id
+ web_peer_connection(
+ aux::session_impl& ses
+ , boost::weak_ptr<torrent> t
+ , boost::shared_ptr<stream_socket> s
+ , tcp::endpoint const& remote
+ , std::string const& url);
+
+ ~web_peer_connection();
+
+ // called from the main loop when this connection has any
+ // work to do.
+ void on_sent(asio::error const& error
+ , std::size_t bytes_transferred);
+ void on_receive(asio::error const& error
+ , std::size_t bytes_transferred);
+
+ std::string const& url() const { return m_url; }
+
+ virtual void get_peer_info(peer_info& p) const;
+
+ // the following functions appends messages
+ // to the send buffer
+ void write_choke() {}
+ void write_unchoke() {}
+ void write_interested() {}
+ void write_not_interested() {}
+ 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_keepalive() {}
+ void on_connected();
+
+#ifndef NDEBUG
+ void check_invariant() const;
+#endif
+
+ private:
+
+ // returns the block currently being
+ // downloaded. And the progress of that
+ // block. If the peer isn't downloading
+ // a piece for the moment, the boost::optional
+ // will be invalid.
+ boost::optional<piece_block_progress> downloading_piece_progress() const;
+
+ // this has one entry per bittorrent request
+ std::deque<peer_request> m_requests;
+ // this has one entry per http-request
+ // (might be more than the bt requests)
+ std::deque<int> m_file_requests;
+
+ std::string m_server_string;
+ http_parser m_parser;
+ std::string m_host;
+ int m_port;
+ std::string m_path;
+ std::string m_url;
+
+ // the first request will contain a little bit more data
+ // than subsequent ones, things that aren't critical are left
+ // out to save bandwidth.
+ bool m_first_request;
+
+ // this is used for intermediate storage of pieces
+ // that is received in more than on HTTP responses
+ std::vector<char> m_piece;
+ // the mapping of the data in the m_piece buffer
+ peer_request m_intermediate_piece;
+ };
+}
+
+#endif // TORRENT_WEB_PEER_CONNECTION_HPP_INCLUDED
+
diff --git a/library/installit b/library/installit
new file mode 100755
index 000000000..b3fb5468d
--- /dev/null
+++ b/library/installit
@@ -0,0 +1 @@
+sudo python setup.py install
diff --git a/library/ip_filter.cpp b/library/ip_filter.cpp
new file mode 100644
index 000000000..0afb5a346
--- /dev/null
+++ b/library/ip_filter.cpp
@@ -0,0 +1,80 @@
+/*
+
+Copyright (c) 2005, 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.
+
+*/
+
+#include "libtorrent/ip_filter.hpp"
+#include <boost/utility.hpp>
+//#include <iostream>
+
+
+namespace libtorrent
+{
+ void ip_filter::add_rule(address first, address last, int flags)
+ {
+ if (first.is_v4())
+ {
+ assert(last.is_v4());
+ m_filter4.add_rule(first.to_v4(), last.to_v4(), flags);
+ }
+ else if (first.is_v6())
+ {
+ assert(last.is_v6());
+ m_filter6.add_rule(first.to_v6(), last.to_v6(), flags);
+ }
+ else
+ assert(false);
+ }
+
+ int ip_filter::access(address const& addr) const
+ {
+ if (addr.is_v4())
+ return m_filter4.access(addr.to_v4());
+ assert(addr.is_v6());
+ return m_filter6.access(addr.to_v6());
+ }
+
+ ip_filter::filter_tuple_t ip_filter::export_filter() const
+ {
+ return boost::make_tuple(m_filter4.export_filter()
+ , m_filter6.export_filter());
+ }
+
+/*
+ void ip_filter::print() const
+ {
+ for (range_t::iterator i = m_access_list.begin(); i != m_access_list.end(); ++i)
+ {
+ std::cout << i->start.as_string() << " " << i->access << "\n";
+ }
+ }
+*/
+}
+
diff --git a/library/kademlia/closest_nodes.cpp b/library/kademlia/closest_nodes.cpp
new file mode 100644
index 000000000..8d0ccea87
--- /dev/null
+++ b/library/kademlia/closest_nodes.cpp
@@ -0,0 +1,145 @@
+/*
+
+Copyright (c) 2006, Arvid Norberg & Daniel Wallin
+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.
+
+*/
+
+#include <libtorrent/kademlia/closest_nodes.hpp>
+#include <libtorrent/kademlia/routing_table.hpp>
+#include <libtorrent/kademlia/rpc_manager.hpp>
+
+namespace libtorrent { namespace dht
+{
+
+using asio::ip::udp;
+
+typedef boost::shared_ptr<observer> observer_ptr;
+
+class closest_nodes_observer : public observer
+{
+public:
+ closest_nodes_observer(
+ boost::intrusive_ptr<traversal_algorithm> const& algorithm
+ , node_id self
+ , node_id target
+ )
+ : m_algorithm(algorithm)
+ , m_target(target)
+ , m_self(self)
+ {}
+
+ void send(msg& p)
+ {
+ p.info_hash = m_target;
+ }
+
+ void timeout();
+ void reply(msg const&);
+
+private:
+ boost::intrusive_ptr<traversal_algorithm> m_algorithm;
+ node_id const m_target;
+ node_id const m_self;
+};
+
+void closest_nodes_observer::reply(msg const& in)
+{
+ if (!in.nodes.empty())
+ {
+ for (msg::nodes_t::const_iterator i = in.nodes.begin()
+ , end(in.nodes.end()); i != end; ++i)
+ {
+ m_algorithm->traverse(i->id, i->addr);
+ }
+ }
+ m_algorithm->finished(m_self);
+}
+
+void closest_nodes_observer::timeout()
+{
+ m_algorithm->failed(m_self);
+}
+
+
+closest_nodes::closest_nodes(
+ node_id target
+ , int branch_factor
+ , int max_results
+ , routing_table& table
+ , rpc_manager& rpc
+ , done_callback const& callback
+)
+ : traversal_algorithm(
+ target
+ , branch_factor
+ , max_results
+ , table
+ , rpc
+ , table.begin()
+ , table.end()
+ )
+ , m_done_callback(callback)
+{
+ boost::intrusive_ptr<closest_nodes> self(this);
+ add_requests();
+}
+
+void closest_nodes::invoke(node_id const& id, udp::endpoint addr)
+{
+ observer_ptr p(new closest_nodes_observer(this, id, m_target));
+ m_rpc.invoke(messages::find_node, addr, p);
+}
+
+void closest_nodes::done()
+{
+ std::vector<node_entry> results;
+ int result_size = m_table.bucket_size();
+ if (result_size > (int)m_results.size()) result_size = (int)m_results.size();
+ for (std::vector<result>::iterator i = m_results.begin()
+ , end(m_results.begin() + result_size); i != end; ++i)
+ {
+ results.push_back(node_entry(i->id, i->addr));
+ }
+ m_done_callback(results);
+}
+
+void closest_nodes::initiate(
+ node_id target
+ , int branch_factor
+ , int max_results
+ , routing_table& table
+ , rpc_manager& rpc
+ , done_callback const& callback
+)
+{
+ new closest_nodes(target, branch_factor, max_results, table, rpc, callback);
+}
+
+} } // namespace libtorrent::dht
+
diff --git a/library/kademlia/dht_tracker.cpp b/library/kademlia/dht_tracker.cpp
new file mode 100644
index 000000000..ca003e2b1
--- /dev/null
+++ b/library/kademlia/dht_tracker.cpp
@@ -0,0 +1,905 @@
+/*
+
+Copyright (c) 2006, 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.
+
+*/
+
+#include <fstream>
+#include <set>
+#include <numeric>
+#include <stdexcept>
+#include <boost/bind.hpp>
+#include <boost/date_time/posix_time/posix_time_types.hpp>
+#include <boost/date_time/posix_time/ptime.hpp>
+#include <boost/ref.hpp>
+#include <boost/optional.hpp>
+#include <boost/lexical_cast.hpp>
+#include <boost/filesystem/operations.hpp>
+
+#include "libtorrent/kademlia/node.hpp"
+#include "libtorrent/kademlia/node_id.hpp"
+#include "libtorrent/kademlia/traversal_algorithm.hpp"
+#include "libtorrent/kademlia/dht_tracker.hpp"
+
+#include "libtorrent/socket.hpp"
+#include "libtorrent/bencode.hpp"
+#include "libtorrent/io.hpp"
+#include "libtorrent/version.hpp"
+
+using boost::posix_time::ptime;
+using boost::posix_time::time_duration;
+using boost::posix_time::second_clock;
+using boost::posix_time::microsec_clock;
+using boost::posix_time::seconds;
+using boost::posix_time::minutes;
+using boost::posix_time::hours;
+using boost::posix_time::milliseconds;
+using boost::ref;
+using boost::lexical_cast;
+using libtorrent::dht::node_impl;
+using libtorrent::dht::node_id;
+using libtorrent::dht::packet_t;
+using libtorrent::dht::msg;
+using libtorrent::dht::packet_iterator;
+namespace messages = libtorrent::dht::messages;
+using namespace libtorrent::detail;
+
+using asio::ip::udp;
+typedef asio::ip::address_v4 address;
+
+namespace
+{
+ const int tick_period = 1; // minutes
+
+ struct count_peers
+ {
+ int& count;
+ count_peers(int& c): count(c) {}
+ void operator()(std::pair<libtorrent::dht::node_id
+ , libtorrent::dht::torrent_entry> const& t)
+ {
+ count += std::distance(t.second.peers.begin()
+ , t.second.peers.end());
+ }
+ };
+
+ boost::optional<node_id> read_id(libtorrent::entry const& d)
+ {
+ using namespace libtorrent;
+ using libtorrent::dht::node_id;
+
+ if (d.type() != entry::dictionary_t) return boost::optional<node_id>();
+ entry const* nid = d.find_key("node-id");
+ if (!nid
+ || nid->type() != entry::string_t
+ || nid->string().length() != 40)
+ return boost::optional<node_id>();
+ return boost::optional<node_id>(
+ boost::lexical_cast<node_id>(nid->string()));
+ }
+
+ template <class EndpointType>
+ void read_endpoint_list(libtorrent::entry const* n, std::vector<EndpointType>& epl)
+ {
+ using namespace libtorrent;
+ entry::list_type const& contacts = n->list();
+ for (entry::list_type::const_iterator i = contacts.begin()
+ , end(contacts.end()); i != end; ++i)
+ {
+ std::string const& p = i->string();
+ if (p.size() < 6) continue;
+ std::string::const_iterator in = p.begin();
+ if (p.size() == 6)
+ epl.push_back(read_v4_endpoint<EndpointType>(in));
+ else if (p.size() == 18)
+ epl.push_back(read_v6_endpoint<EndpointType>(in));
+ }
+ }
+
+}
+
+namespace libtorrent { namespace dht
+{
+#ifdef TORRENT_DHT_VERBOSE_LOGGING
+ TORRENT_DEFINE_LOG(dht_tracker)
+#endif
+
+ // class that puts the networking and the kademlia node in a single
+ // unit and connecting them together.
+ dht_tracker::dht_tracker(asio::io_service& d, dht_settings const& settings
+ , asio::ip::address listen_interface, entry const& bootstrap)
+ : m_demuxer(d)
+ , m_socket(m_demuxer, udp::endpoint(listen_interface, settings.service_port))
+ , m_dht(bind(&dht_tracker::send_packet, this, _1), settings
+ , read_id(bootstrap))
+ , m_buffer(0)
+ , m_last_refresh(second_clock::universal_time() - hours(1))
+ , m_timer(m_demuxer)
+ , m_connection_timer(m_demuxer)
+ , m_refresh_timer(m_demuxer)
+ , m_settings(settings)
+ , m_refresh_bucket(160)
+ , m_host_resolver(d)
+ {
+ using boost::bind;
+
+ m_in_buf[0].resize(1000);
+ m_in_buf[1].resize(1000);
+#ifdef TORRENT_DHT_VERBOSE_LOGGING
+ m_counter = 0;
+ std::fill_n(m_replies_bytes_sent, 5, 0);
+ std::fill_n(m_queries_bytes_received, 5, 0);
+ std::fill_n(m_replies_sent, 5, 0);
+ std::fill_n(m_queries_received, 5, 0);
+ m_announces = 0;
+ m_failed_announces = 0;
+ m_total_message_input = 0;
+ m_ut_message_input = 0;
+ m_lt_message_input = 0;
+ m_mp_message_input = 0;
+ m_gr_message_input = 0;
+ m_total_in_bytes = 0;
+ m_total_out_bytes = 0;
+ m_queries_out_bytes = 0;
+
+ // turns on and off individual components' logging
+
+// rpc_log().enable(false);
+// node_log().enable(false);
+// traversal_log().enable(false);
+// dht_tracker_log.enable(false);
+
+#endif
+ std::vector<udp::endpoint> initial_nodes;
+
+ if (bootstrap.type() == entry::dictionary_t)
+ {
+ try
+ {
+ if (entry const* nodes = bootstrap.find_key("nodes"))
+ read_endpoint_list<udp::endpoint>(nodes, initial_nodes);
+ } catch (std::exception&) {}
+ }
+
+ m_dht.bootstrap(initial_nodes, bind(&dht_tracker::on_bootstrap, this));
+
+ m_socket.async_receive_from(asio::buffer(&m_in_buf[m_buffer][0]
+ , m_in_buf[m_buffer].size()), m_remote_endpoint[m_buffer]
+ , bind(&dht_tracker::on_receive, this, _1, _2));
+ m_timer.expires_from_now(seconds(1));
+ m_timer.async_wait(bind(&dht_tracker::tick, this, _1));
+
+ m_connection_timer.expires_from_now(seconds(10));
+ m_connection_timer.async_wait(bind(&dht_tracker::connection_timeout, this, _1));
+
+ m_refresh_timer.expires_from_now(minutes(15));
+ m_refresh_timer.async_wait(bind(&dht_tracker::refresh_timeout, this, _1));
+ }
+
+ void dht_tracker::dht_status(session_status& s)
+ {
+ boost::tie(s.m_dht_nodes, s.m_dht_node_cache) = m_dht.size();
+ s.m_dht_torrents = m_dht.data_size();
+ }
+
+ void dht_tracker::connection_timeout(asio::error const& e)
+ try
+ {
+ if (e) return;
+ time_duration d = m_dht.connection_timeout();
+ m_connection_timer.expires_from_now(d);
+ m_connection_timer.async_wait(bind(&dht_tracker::connection_timeout, this, _1));
+ }
+ catch (std::exception&)
+ {
+ assert(false);
+ };
+
+ void dht_tracker::refresh_timeout(asio::error const& e)
+ try
+ {
+ if (e) return;
+ time_duration d = m_dht.refresh_timeout();
+ m_refresh_timer.expires_from_now(d);
+ m_refresh_timer.async_wait(bind(&dht_tracker::refresh_timeout, this, _1));
+ }
+ catch (std::exception&)
+ {
+ assert(false);
+ };
+
+ void dht_tracker::rebind(asio::ip::address listen_interface, int listen_port)
+ {
+ m_socket.close();
+ m_socket.open(asio::ip::udp::v4());
+ m_socket.bind(udp::endpoint(listen_interface, listen_port));
+ }
+
+ void dht_tracker::tick(asio::error const& e)
+ try
+ {
+ if (e) return;
+ m_timer.expires_from_now(minutes(tick_period));
+ m_timer.async_wait(bind(&dht_tracker::tick, this, _1));
+
+ m_dht.new_write_key();
+
+#ifdef TORRENT_DHT_VERBOSE_LOGGING
+ static bool first = true;
+ if (first)
+ {
+ boost::filesystem::create_directory("libtorrent_logs");
+ }
+
+ std::ofstream st("libtorrent_logs/routing_table_state.txt", std::ios_base::trunc);
+ m_dht.print_state(st);
+
+ // count torrents
+ int torrents = std::distance(m_dht.begin_data(), m_dht.end_data());
+
+ // count peers
+ int peers = 0;
+ std::for_each(m_dht.begin_data(), m_dht.end_data(), count_peers(peers));
+
+ std::ofstream pc("libtorrent_logs/dht_stats.log", std::ios_base::app);
+ if (first)
+ {
+ first = false;
+ using boost::posix_time::to_simple_string;
+ pc << "\n\n ***** starting log at " << to_simple_string(
+ second_clock::universal_time()) << " *****\n\n"
+ << "minute:active nodes:passive nodes"
+ ":ping replies sent:ping queries recvd:ping"
+ ":ping replies sent:ping queries recvd:ping"
+ ":find_node replies bytes sent:find_node queries bytes recv"
+ ":find_node replies bytes sent:find_node queries bytes recv"
+ ":get_peers replies sent:get_peers queries recvd:get_peers"
+ ":get_peers replies bytes sent:get_peers queries bytes recv"
+ ":announce_peer replies sent:announce_peer queries recvd:announce_peer"
+ ":announce_peer replies bytes sent:announce_peer queries bytes recv"
+ ":error replies sent:error queries recvd:error"
+ ":error replies bytes sent:error queries bytes recv"
+ ":num torrents:num peers:announces per min"
+ ":failed announces per min:total msgs per min"
+ ":ut msgs per min:lt msgs per min:mp msgs per min"
+ ":gr msgs per min:bytes in per sec:bytes out per sec"
+ ":queries out bytes per sec\n\n";
+ }
+
+ int active;
+ int passive;
+ boost::tie(active, passive) = m_dht.size();
+ pc << (m_counter * tick_period)
+ << "\t" << active
+ << "\t" << passive;
+ for (int i = 0; i < 5; ++i)
+ pc << "\t" << (m_replies_sent[i] / float(tick_period))
+ << "\t" << (m_queries_received[i] / float(tick_period))
+ << "\t" << (m_replies_bytes_sent[i] / float(tick_period*60))
+ << "\t" << (m_queries_bytes_received[i] / float(tick_period*60));
+
+ pc << "\t" << torrents
+ << "\t" << peers
+ << "\t" << m_announces / float(tick_period)
+ << "\t" << m_failed_announces / float(tick_period)
+ << "\t" << (m_total_message_input / float(tick_period))
+ << "\t" << (m_ut_message_input / float(tick_period))
+ << "\t" << (m_lt_message_input / float(tick_period))
+ << "\t" << (m_mp_message_input / float(tick_period))
+ << "\t" << (m_gr_message_input / float(tick_period))
+ << "\t" << (m_total_in_bytes / float(tick_period*60))
+ << "\t" << (m_total_out_bytes / float(tick_period*60))
+ << "\t" << (m_queries_out_bytes / float(tick_period*60))
+ << std::endl;
+ ++m_counter;
+ std::fill_n(m_replies_bytes_sent, 5, 0);
+ std::fill_n(m_queries_bytes_received, 5, 0);
+ std::fill_n(m_replies_sent, 5, 0);
+ std::fill_n(m_queries_received, 5, 0);
+ m_announces = 0;
+ m_failed_announces = 0;
+ m_total_message_input = 0;
+ m_ut_message_input = 0;
+ m_lt_message_input = 0;
+ m_total_in_bytes = 0;
+ m_total_out_bytes = 0;
+ m_queries_out_bytes = 0;
+#endif
+ }
+ catch (std::exception&)
+ {
+ assert(false);
+ };
+
+ void dht_tracker::announce(sha1_hash const& ih, int listen_port
+ , boost::function<void(std::vector<tcp::endpoint> const&
+ , sha1_hash const&)> f)
+ {
+ m_dht.announce(ih, listen_port, f);
+ }
+
+ // translate bittorrent kademlia message into the generice kademlia message
+ // used by the library
+ void dht_tracker::on_receive(asio::error const& error, size_t bytes_transferred)
+ try
+ {
+ if (error == asio::error::operation_aborted) return;
+
+ int current_buffer = m_buffer;
+ m_buffer = (m_buffer + 1) & 1;
+ m_socket.async_receive_from(asio::buffer(&m_in_buf[m_buffer][0]
+ , m_in_buf[m_buffer].size()), m_remote_endpoint[m_buffer]
+ , bind(&dht_tracker::on_receive, this, _1, _2));
+
+ if (error) return;
+
+#ifdef TORRENT_DHT_VERBOSE_LOGGING
+ ++m_total_message_input;
+ m_total_in_bytes += bytes_transferred;
+#endif
+
+ try
+ {
+ using libtorrent::entry;
+ using libtorrent::bdecode;
+
+ assert(bytes_transferred > 0);
+
+ entry e = bdecode(m_in_buf[current_buffer].begin()
+ , m_in_buf[current_buffer].end());
+
+#ifdef TORRENT_DHT_VERBOSE_LOGGING
+ TORRENT_LOG(dht_tracker) << microsec_clock::universal_time()
+ << " RECEIVED [" << m_remote_endpoint[current_buffer]
+ << "]:";
+#endif
+
+ libtorrent::dht::msg m;
+ m.message_id = 0;
+ m.addr = m_remote_endpoint[current_buffer];
+ m.transaction_id = e["t"].string();
+
+#ifdef TORRENT_DHT_VERBOSE_LOGGING
+ try
+ {
+ entry const* ver = e.find_key("v");
+ if (!ver) throw std::exception();
+
+ std::string const& client = ver->string();
+ if (client.size() > 1 && std::equal(client.begin(), client.begin() + 2, "UT"))
+ {
+ ++m_ut_message_input;
+ TORRENT_LOG(dht_tracker) << " client: uTorrent";
+ }
+ else if (client.size() > 1 && std::equal(client.begin(), client.begin() + 2, "LT"))
+ {
+ ++m_lt_message_input;
+ TORRENT_LOG(dht_tracker) << " client: libtorrent";
+ }
+ else if (client.size() > 1 && std::equal(client.begin(), client.begin() + 2, "MP"))
+ {
+ ++m_mp_message_input;
+ TORRENT_LOG(dht_tracker) << " client: MooPolice";
+ }
+ else if (client.size() > 1 && std::equal(client.begin(), client.begin() + 2, "GR"))
+ {
+ ++m_gr_message_input;
+ TORRENT_LOG(dht_tracker) << " client: GetRight";
+ }
+ else
+ {
+ TORRENT_LOG(dht_tracker) << " client: generic";
+ }
+ }
+ catch (std::exception&)
+ {
+ TORRENT_LOG(dht_tracker) << " client: generic";
+ };
+#endif
+
+ std::string const& msg_type = e["y"].string();
+
+ if (msg_type == "r")
+ {
+#ifdef TORRENT_DHT_VERBOSE_LOGGING
+ TORRENT_LOG(dht_tracker) << " reply: transaction: "
+ << m.transaction_id;
+#endif
+
+ m.reply = true;
+ entry const& r = e["r"];
+ std::string const& id = r["id"].string();
+ if (id.size() != 20) throw std::runtime_error("invalid size of id");
+ std::copy(id.begin(), id.end(), m.id.begin());
+
+ if (entry const* n = r.find_key("values"))
+ {
+ m.peers.clear();
+ read_endpoint_list<tcp::endpoint>(n, m.peers);
+#ifdef TORRENT_DHT_VERBOSE_LOGGING
+ TORRENT_LOG(dht_tracker) << " peers: " << m.peers.size();
+#endif
+ }
+
+ m.nodes.clear();
+ if (entry const* n = r.find_key("nodes"))
+ {
+ std::string const& nodes = n->string();
+ std::string::const_iterator i = nodes.begin();
+ std::string::const_iterator end = nodes.end();
+
+ while (std::distance(i, end) >= 26)
+ {
+ node_id id;
+ std::copy(i, i + 20, id.begin());
+ i += 20;
+ m.nodes.push_back(libtorrent::dht::node_entry(
+ id, read_v4_endpoint<udp::endpoint>(i)));
+ }
+#ifdef TORRENT_DHT_VERBOSE_LOGGING
+ TORRENT_LOG(dht_tracker) << " nodes: " << m.nodes.size();
+#endif
+ }
+
+ if (entry const* n = r.find_key("nodes2"))
+ {
+ entry::list_type const& contacts = n->list();
+ for (entry::list_type::const_iterator i = contacts.begin()
+ , end(contacts.end()); i != end; ++i)
+ {
+ std::string const& p = i->string();
+ if (p.size() < 6 + 20) continue;
+ std::string::const_iterator in = p.begin();
+
+ node_id id;
+ std::copy(in, in + 20, id.begin());
+ in += 20;
+ if (p.size() == 6 + 20)
+ m.nodes.push_back(libtorrent::dht::node_entry(
+ id, read_v4_endpoint<udp::endpoint>(in)));
+ else if (p.size() == 18 + 20)
+ m.nodes.push_back(libtorrent::dht::node_entry(
+ id, read_v6_endpoint<udp::endpoint>(in)));
+ }
+#ifdef TORRENT_DHT_VERBOSE_LOGGING
+ TORRENT_LOG(dht_tracker) << " nodes2 + nodes: " << m.nodes.size();
+#endif
+ }
+
+ entry const* token = r.find_key("token");
+ if (token) m.write_token = *token;
+ }
+ else if (msg_type == "q")
+ {
+ m.reply = false;
+ entry const& a = e["a"];
+ std::string const& id = a["id"].string();
+ if (id.size() != 20) throw std::runtime_error("invalid size of id");
+ std::copy(id.begin(), id.end(), m.id.begin());
+
+ std::string request_kind(e["q"].string());
+#ifdef TORRENT_DHT_VERBOSE_LOGGING
+ TORRENT_LOG(dht_tracker) << " query: " << request_kind;
+#endif
+
+ if (request_kind == "ping")
+ {
+ m.message_id = libtorrent::dht::messages::ping;
+ }
+ else if (request_kind == "find_node")
+ {
+ std::string const& target = a["target"].string();
+ if (target.size() != 20) throw std::runtime_error("invalid size of target id");
+ std::copy(target.begin(), target.end(), m.info_hash.begin());
+#ifdef TORRENT_DHT_VERBOSE_LOGGING
+ TORRENT_LOG(dht_tracker) << " target: "
+ << boost::lexical_cast<std::string>(m.info_hash);
+#endif
+
+ m.message_id = libtorrent::dht::messages::find_node;
+ }
+ else if (request_kind == "get_peers")
+ {
+ std::string const& info_hash = a["info_hash"].string();
+ if (info_hash.size() != 20) throw std::runtime_error("invalid size of info-hash");
+ std::copy(info_hash.begin(), info_hash.end(), m.info_hash.begin());
+ m.message_id = libtorrent::dht::messages::get_peers;
+#ifdef TORRENT_DHT_VERBOSE_LOGGING
+ TORRENT_LOG(dht_tracker) << " info_hash: "
+ << boost::lexical_cast<std::string>(m.info_hash);
+#endif
+ }
+ else if (request_kind == "announce_peer")
+ {
+#ifdef TORRENT_DHT_VERBOSE_LOGGING
+ ++m_announces;
+#endif
+ std::string const& info_hash = a["info_hash"].string();
+ if (info_hash.size() != 20)
+ throw std::runtime_error("invalid size of info-hash");
+ std::copy(info_hash.begin(), info_hash.end(), m.info_hash.begin());
+ m.port = a["port"].integer();
+ m.write_token = a["token"];
+ m.message_id = libtorrent::dht::messages::announce_peer;
+#ifdef TORRENT_DHT_VERBOSE_LOGGING
+ TORRENT_LOG(dht_tracker) << " info_hash: "
+ << boost::lexical_cast<std::string>(m.info_hash);
+ TORRENT_LOG(dht_tracker) << " port: " << m.port;
+
+ if (!m_dht.verify_token(m))
+ ++m_failed_announces;
+#endif
+ }
+ else
+ {
+#ifdef TORRENT_DHT_VERBOSE_LOGGING
+ TORRENT_LOG(dht_tracker) << " *** UNSUPPORTED REQUEST *** : "
+ << request_kind;
+#endif
+ throw std::runtime_error("unsupported request: " + request_kind);
+ }
+ }
+ else if (msg_type == "e")
+ {
+ entry::list_type const& list = e["e"].list();
+ m.message_id = messages::error;
+ m.error_msg = list.back().string();
+ m.error_code = list.front().integer();
+#ifdef TORRENT_DHT_VERBOSE_LOGGING
+ TORRENT_LOG(dht_tracker) << " error: " << m.error_code << " "
+ << m.error_msg;
+#endif
+ }
+ else
+ {
+#ifdef TORRENT_DHT_VERBOSE_LOGGING
+ TORRENT_LOG(dht_tracker) << " *** UNSUPPORTED MESSAGE TYPE *** : "
+ << msg_type;
+#endif
+ throw std::runtime_error("unsupported message type: " + msg_type);
+ }
+
+#ifdef TORRENT_DHT_VERBOSE_LOGGING
+ if (!m.reply)
+ {
+ ++m_queries_received[m.message_id];
+ m_queries_bytes_received[m.message_id] += int(bytes_transferred);
+ }
+ TORRENT_LOG(dht_tracker) << e;
+#endif
+
+ m_dht.incoming(m);
+ }
+ catch (std::exception& e)
+ {
+#ifdef TORRENT_DHT_VERBOSE_LOGGING
+ TORRENT_LOG(dht_tracker) << "invalid incoming packet: "
+ << e.what();
+#endif
+ }
+ }
+ catch (std::exception& e)
+ {
+ assert(false);
+ };
+
+ entry dht_tracker::state() const
+ {
+ entry ret(entry::dictionary_t);
+ {
+ entry nodes(entry::list_t);
+ for (node_impl::iterator i(m_dht.begin())
+ , end(m_dht.end()); i != end; ++i)
+ {
+ std::string node;
+ std::back_insert_iterator<std::string> out(node);
+ write_endpoint(i->addr, out);
+ nodes.list().push_back(entry(node));
+ }
+ bucket_t cache;
+ m_dht.replacement_cache(cache);
+ for (bucket_t::iterator i(cache.begin())
+ , end(cache.end()); i != end; ++i)
+ {
+ std::string node;
+ std::back_insert_iterator<std::string> out(node);
+ write_endpoint(i->addr, out);
+ nodes.list().push_back(entry(node));
+ }
+ if (!nodes.list().empty())
+ ret["nodes"] = nodes;
+ }
+
+ ret["node-id"] = boost::lexical_cast<std::string>(m_dht.nid());
+ return ret;
+ }
+
+ void dht_tracker::add_node(udp::endpoint node)
+ {
+ m_dht.add_node(node);
+ }
+
+ void dht_tracker::add_node(std::pair<std::string, int> const& node)
+ {
+ udp::resolver::query q(node.first, lexical_cast<std::string>(node.second));
+ m_host_resolver.async_resolve(q, bind(&dht_tracker::on_name_lookup
+ , this, _1, _2));
+ }
+
+ void dht_tracker::on_name_lookup(asio::error const& e
+ , udp::resolver::iterator host) try
+ {
+ if (e || host == udp::resolver::iterator()) return;
+ add_node(host->endpoint());
+ }
+ catch (std::exception&)
+ {
+ assert(false);
+ };
+
+ void dht_tracker::add_router_node(std::pair<std::string, int> const& node)
+ {
+ udp::resolver::query q(node.first, lexical_cast<std::string>(node.second));
+ m_host_resolver.async_resolve(q, bind(&dht_tracker::on_router_name_lookup
+ , this, _1, _2));
+ }
+
+ void dht_tracker::on_router_name_lookup(asio::error const& e
+ , udp::resolver::iterator host) try
+ {
+ if (e || host == udp::resolver::iterator()) return;
+ m_dht.add_router_node(host->endpoint());
+ }
+ catch (std::exception&)
+ {
+ assert(false);
+ };
+
+ void dht_tracker::on_bootstrap()
+ {}
+
+ void dht_tracker::send_packet(msg const& m)
+ {
+ using libtorrent::bencode;
+ using libtorrent::entry;
+ entry e(entry::dictionary_t);
+ e["t"] = m.transaction_id;
+ std::string version_str("LT ");
+ std::string::iterator i = version_str.begin() + 2;
+ detail::write_uint8(LIBTORRENT_VERSION_MAJOR, i);
+ detail::write_uint8(LIBTORRENT_VERSION_MINOR, i);
+ e["v"] = version_str;
+
+#ifdef TORRENT_DHT_VERBOSE_LOGGING
+ TORRENT_LOG(dht_tracker) << microsec_clock::universal_time()
+ << " SENDING [" << m.addr << "]:";
+ TORRENT_LOG(dht_tracker) << " transaction: " << m.transaction_id;
+// e.print(std::cerr);
+#endif
+
+ if (m.message_id == messages::error)
+ {
+ assert(m.reply);
+ e["y"] = "e";
+ entry error_list(entry::list_t);
+ error_list.list().push_back(entry(m.error_code));
+ error_list.list().push_back(entry(m.error_msg));
+ e["e"] = error_list;
+#ifdef TORRENT_DHT_VERBOSE_LOGGING
+ TORRENT_LOG(dht_tracker) << " error: " << m.error_code << " "
+ << m.error_msg;
+#endif
+ }
+ else if (m.reply)
+ {
+ e["y"] = "r";
+ e["r"] = entry(entry::dictionary_t);
+ entry& r = e["r"];
+ r["id"] = std::string(m.id.begin(), m.id.end());
+
+#ifdef TORRENT_DHT_VERBOSE_LOGGING
+ TORRENT_LOG(dht_tracker) << " reply: "
+ << messages::ids[m.message_id];
+#endif
+
+ if (m.write_token.type() != entry::undefined_t)
+ r["token"] = m.write_token;
+
+ switch (m.message_id)
+ {
+ case messages::ping:
+ break;
+ case messages::find_node:
+ {
+ bool ipv6_nodes = false;
+ r["nodes"] = entry(entry::string_t);
+ entry& n = r["nodes"];
+ std::back_insert_iterator<std::string> out(n.string());
+ for (msg::nodes_t::const_iterator i = m.nodes.begin()
+ , end(m.nodes.end()); i != end; ++i)
+ {
+ if (!i->addr.address().is_v4())
+ {
+ ipv6_nodes = true;
+ continue;
+ }
+ std::copy(i->id.begin(), i->id.end(), out);
+ write_endpoint(i->addr, out);
+ }
+
+ if (ipv6_nodes)
+ {
+ r["nodes2"] = entry(entry::list_t);
+ entry& p = r["nodes2"];
+ std::string endpoint;
+ endpoint.resize(6);
+ for (msg::nodes_t::const_iterator i = m.nodes.begin()
+ , end(m.nodes.end()); i != end; ++i)
+ {
+ std::string::iterator out = endpoint.begin();
+ std::copy(i->id.begin(), i->id.end(), out);
+ write_endpoint(i->addr, out);
+ p.list().push_back(entry(endpoint));
+ }
+ }
+#ifdef TORRENT_DHT_VERBOSE_LOGGING
+ TORRENT_LOG(dht_tracker) << " nodes: " << m.nodes.size();
+#endif
+ break;
+ }
+ case messages::get_peers:
+ {
+ if (m.peers.empty())
+ {
+ r["nodes"] = entry(entry::string_t);
+ entry& n = r["nodes"];
+ std::back_insert_iterator<std::string> out(n.string());
+ for (msg::nodes_t::const_iterator i = m.nodes.begin()
+ , end(m.nodes.end()); i != end; ++i)
+ {
+ if (!i->addr.address().is_v4()) continue;
+ std::copy(i->id.begin(), i->id.end(), out);
+ write_endpoint(i->addr, out);
+ }
+#ifdef TORRENT_DHT_VERBOSE_LOGGING
+ TORRENT_LOG(dht_tracker) << " nodes: " << m.nodes.size();
+#endif
+ }
+ else
+ {
+ r["values"] = entry(entry::list_t);
+ entry& p = r["values"];
+ std::string endpoint;
+ endpoint.resize(6);
+ for (msg::peers_t::const_iterator i = m.peers.begin()
+ , end(m.peers.end()); i != end; ++i)
+ {
+ std::string::iterator out = endpoint.begin();
+ write_endpoint(*i, out);
+ p.list().push_back(entry(endpoint));
+ }
+#ifdef TORRENT_DHT_VERBOSE_LOGGING
+ TORRENT_LOG(dht_tracker) << " peers: " << m.peers.size();
+#endif
+ }
+ break;
+ }
+
+ case messages::announce_peer:
+ break;
+ break;
+ }
+ }
+ else
+ {
+ e["y"] = "q";
+ e["a"] = entry(entry::dictionary_t);
+ entry& a = e["a"];
+ a["id"] = std::string(m.id.begin(), m.id.end());
+
+ if (m.write_token.type() != entry::undefined_t)
+ a["token"] = m.write_token;
+ assert(m.message_id <= messages::error);
+ e["q"] = messages::ids[m.message_id];
+
+#ifdef TORRENT_DHT_VERBOSE_LOGGING
+ TORRENT_LOG(dht_tracker) << " query: "
+ << messages::ids[m.message_id];
+#endif
+
+ switch (m.message_id)
+ {
+ case messages::find_node:
+ {
+ a["target"] = std::string(m.info_hash.begin(), m.info_hash.end());
+#ifdef TORRENT_DHT_VERBOSE_LOGGING
+ TORRENT_LOG(dht_tracker) << " target: "
+ << boost::lexical_cast<std::string>(m.info_hash);
+#endif
+ break;
+ }
+ case messages::get_peers:
+ {
+ a["info_hash"] = std::string(m.info_hash.begin(), m.info_hash.end());
+#ifdef TORRENT_DHT_VERBOSE_LOGGING
+ TORRENT_LOG(dht_tracker) << " info_hash: "
+ << boost::lexical_cast<std::string>(m.info_hash);
+#endif
+ break;
+ }
+ case messages::announce_peer:
+ a["port"] = m_settings.service_port;
+ a["info_hash"] = boost::lexical_cast<std::string>(m.info_hash);
+ a["token"] = m.write_token;
+#ifdef TORRENT_DHT_VERBOSE_LOGGING
+ TORRENT_LOG(dht_tracker) << " port: "
+ << m_settings.service_port
+ << " info_hash: " << boost::lexical_cast<std::string>(m.info_hash);
+#endif
+ break;
+ default: break;
+ }
+
+ }
+
+ m_send_buf.clear();
+ bencode(std::back_inserter(m_send_buf), e);
+ m_socket.send_to(asio::buffer(&m_send_buf[0]
+ , (int)m_send_buf.size()), m.addr);
+
+#ifdef TORRENT_DHT_VERBOSE_LOGGING
+ m_total_out_bytes += m_send_buf.size();
+
+ if (m.reply)
+ {
+ ++m_replies_sent[m.message_id];
+ m_replies_bytes_sent[m.message_id] += int(m_send_buf.size());
+ }
+ else
+ {
+ m_queries_out_bytes += m_send_buf.size();
+ }
+ TORRENT_LOG(dht_tracker) << e;
+#endif
+
+ if (!m.piggy_backed_ping) return;
+
+ msg pm;
+ pm.reply = false;
+ pm.piggy_backed_ping = false;
+ pm.message_id = messages::ping;
+ pm.transaction_id = m.ping_transaction_id;
+ pm.id = m.id;
+ pm.addr = m.addr;
+
+ send_packet(pm);
+ }
+
+}}
+
diff --git a/library/kademlia/find_data.cpp b/library/kademlia/find_data.cpp
new file mode 100644
index 000000000..e1e09925b
--- /dev/null
+++ b/library/kademlia/find_data.cpp
@@ -0,0 +1,156 @@
+/*
+
+Copyright (c) 2006, Arvid Norberg & Daniel Wallin
+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.
+
+*/
+
+#include <libtorrent/kademlia/find_data.hpp>
+#include <libtorrent/kademlia/routing_table.hpp>
+#include <libtorrent/kademlia/rpc_manager.hpp>
+#include <libtorrent/io.hpp>
+
+namespace libtorrent { namespace dht
+{
+
+typedef boost::shared_ptr<observer> observer_ptr;
+
+class find_data_observer : public observer
+{
+public:
+ find_data_observer(
+ boost::intrusive_ptr<find_data> const& algorithm
+ , node_id self
+ , node_id target)
+ : m_algorithm(algorithm)
+ , m_target(target)
+ , m_self(self)
+ {}
+
+ void send(msg& m)
+ {
+ m.reply = false;
+ m.message_id = messages::get_peers;
+ m.info_hash = m_target;
+ }
+
+ void timeout();
+ void reply(msg const&);
+
+private:
+ boost::intrusive_ptr<find_data> m_algorithm;
+ node_id const m_target;
+ node_id const m_self;
+};
+
+void find_data_observer::reply(msg const& m)
+{
+ if (!m.peers.empty())
+ {
+ m_algorithm->got_data(&m);
+ }
+ else
+ {
+ for (msg::nodes_t::const_iterator i = m.nodes.begin()
+ , end(m.nodes.end()); i != end; ++i)
+ {
+ m_algorithm->traverse(i->id, i->addr);
+ }
+ }
+ m_algorithm->finished(m_self);
+}
+
+void find_data_observer::timeout()
+{
+ m_algorithm->failed(m_self);
+}
+
+
+find_data::find_data(
+ node_id target
+ , int branch_factor
+ , int max_results
+ , routing_table& table
+ , rpc_manager& rpc
+ , done_callback const& callback
+)
+ : traversal_algorithm(
+ target
+ , branch_factor
+ , max_results
+ , table
+ , rpc
+ , table.begin()
+ , table.end()
+ )
+ , m_done_callback(callback)
+ , m_done(false)
+{
+ boost::intrusive_ptr<find_data> self(this);
+ add_requests();
+}
+
+void find_data::invoke(node_id const& id, asio::ip::udp::endpoint addr)
+{
+ if (m_done)
+ {
+ m_invoke_count = -1;
+ return;
+ }
+
+ observer_ptr p(new find_data_observer(this, id, m_target));
+ m_rpc.invoke(messages::get_peers, addr, p);
+}
+
+void find_data::got_data(msg const* m)
+{
+ m_done = true;
+ m_done_callback(m);
+}
+
+void find_data::done()
+{
+ if (m_invoke_count != 0) return;
+ if (!m_done) m_done_callback(0);
+}
+
+void find_data::initiate(
+ node_id target
+ , int branch_factor
+ , int max_results
+ , routing_table& table
+ , rpc_manager& rpc
+ , done_callback const& callback
+)
+{
+ std::cerr << "find_data::initiate, key: " << target << "\n";
+ new find_data(target, branch_factor, max_results, table, rpc, callback);
+}
+
+} } // namespace libtorrent::dht
+
diff --git a/library/kademlia/node.cpp b/library/kademlia/node.cpp
new file mode 100644
index 000000000..8a9d17818
--- /dev/null
+++ b/library/kademlia/node.cpp
@@ -0,0 +1,539 @@
+/*
+
+Copyright (c) 2006, 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.
+
+*/
+
+#include <utility>
+#include <boost/bind.hpp>
+#include <boost/optional.hpp>
+#include <boost/function.hpp>
+#include <boost/iterator_adaptors.hpp>
+
+#include "libtorrent/io.hpp"
+#include "libtorrent/hasher.hpp"
+#include "libtorrent/random_sample.hpp"
+#include "libtorrent/kademlia/node_id.hpp"
+#include "libtorrent/kademlia/rpc_manager.hpp"
+#include "libtorrent/kademlia/packet_iterator.hpp"
+#include "libtorrent/kademlia/routing_table.hpp"
+#include "libtorrent/kademlia/node.hpp"
+
+#include "libtorrent/kademlia/refresh.hpp"
+#include "libtorrent/kademlia/closest_nodes.hpp"
+#include "libtorrent/kademlia/find_data.hpp"
+
+using boost::bind;
+using boost::posix_time::second_clock;
+using boost::posix_time::seconds;
+using boost::posix_time::minutes;
+using boost::posix_time::ptime;
+using boost::posix_time::time_duration;
+
+namespace libtorrent { namespace dht
+{
+
+#ifdef _MSC_VER
+namespace
+{
+ char rand() { return (char)std::rand(); }
+}
+#endif
+
+typedef boost::shared_ptr<observer> observer_ptr;
+
+// TODO: configurable?
+enum { announce_interval = 30 };
+
+using asio::ip::udp;
+
+#ifdef TORRENT_DHT_VERBOSE_LOGGING
+TORRENT_DEFINE_LOG(node)
+#endif
+
+node_id generate_id()
+{
+ char random[20];
+ std::srand(std::time(0));
+#ifdef _MSC_VER
+ std::generate(random, random + 20, &rand);
+#else
+ std::generate(random, random + 20, &std::rand);
+#endif
+
+ hasher h;
+ h.update(random, 20);
+ return h.final();
+}
+
+// remove peers that have timed out
+void purge_peers(std::set<peer_entry>& peers)
+{
+ for (std::set<peer_entry>::iterator i = peers.begin()
+ , end(peers.end()); i != end;)
+ {
+ // the peer has timed out
+ if (i->added + minutes(int(announce_interval * 1.5f)) < second_clock::universal_time())
+ {
+#ifdef TORRENT_DHT_VERBOSE_LOGGING
+ TORRENT_LOG(node) << "peer timed out at: " << i->addr.address();
+#endif
+ peers.erase(i++);
+ }
+ else
+ ++i;
+ }
+}
+
+void nop() {}
+
+node_impl::node_impl(boost::function<void(msg const&)> const& f
+ , dht_settings const& settings, boost::optional<node_id> node_id)
+ : m_settings(settings)
+ , m_id(node_id ? *node_id : generate_id())
+ , m_table(m_id, 8, settings)
+ , m_rpc(bind(&node_impl::incoming_request, this, _1)
+ , m_id, m_table, f)
+ , m_last_tracker_tick(boost::posix_time::second_clock::universal_time())
+{
+ m_secret[0] = std::rand();
+ m_secret[1] = std::rand();
+}
+
+bool node_impl::verify_token(msg const& m)
+{
+ if (m.write_token.type() != entry::string_t)
+ return false;
+ std::string const& token = m.write_token.string();
+ if (token.length() != 4) return false;
+
+ hasher h1;
+ std::string address = m.addr.address().to_string();
+ h1.update(&address[0], address.length());
+ h1.update((char*)&m_secret[0], sizeof(m_secret[0]));
+ h1.update((char*)&m.info_hash[0], sha1_hash::size);
+
+ sha1_hash h = h1.final();
+ if (std::equal(token.begin(), token.end(), (signed char*)&h[0]))
+ return true;
+
+ hasher h2;
+ h2.update(&address[0], address.length());
+ h2.update((char*)&m_secret[1], sizeof(m_secret[1]));
+ h = h2.final();
+ if (std::equal(token.begin(), token.end(), (signed char*)&h[0]))
+ return true;
+ return false;
+}
+
+entry node_impl::generate_token(msg const& m)
+{
+ std::string token;
+ token.resize(4);
+ hasher h;
+ std::string address = m.addr.address().to_string();
+ h.update(&address[0], address.length());
+ h.update((char*)&m_secret[0], sizeof(m_secret[0]));
+ h.update((char*)&m.info_hash[0], sha1_hash::size);
+
+ sha1_hash hash = h.final();
+ std::copy(hash.begin(), hash.begin() + 4, (signed char*)&token[0]);
+ return entry(token);
+}
+
+void node_impl::refresh(node_id const& id
+ , boost::function0<void> f)
+{
+ // use the 'bucket size' closest nodes
+ // to start the refresh with
+ std::vector<node_entry> start;
+ start.reserve(m_table.bucket_size());
+ m_table.find_node(id, start, false);
+ refresh::initiate(id, m_settings.search_branching, 10, m_table.bucket_size()
+ , m_table, start.begin(), start.end(), m_rpc, f);
+}
+
+void node_impl::bootstrap(std::vector<udp::endpoint> const& nodes
+ , boost::function0<void> f)
+{
+ std::vector<node_entry> start;
+ start.reserve(nodes.size());
+ std::copy(nodes.begin(), nodes.end(), std::back_inserter(start));
+ refresh::initiate(m_id, m_settings.search_branching, 10, m_table.bucket_size()
+ , m_table, start.begin(), start.end(), m_rpc, f);
+}
+
+void node_impl::refresh()
+{
+ std::vector<node_entry> start;
+ start.reserve(m_table.size().get<0>());
+ std::copy(m_table.begin(), m_table.end(), std::back_inserter(start));
+
+ refresh::initiate(m_id, m_settings.search_branching, 10, m_table.bucket_size()
+ , m_table, start.begin(), start.end(), m_rpc, bind(&nop));
+}
+
+int node_impl::bucket_size(int bucket)
+{
+ return m_table.bucket_size(bucket);
+}
+
+void node_impl::new_write_key()
+{
+ m_secret[1] = m_secret[0];
+ m_secret[0] = std::rand();
+}
+
+void node_impl::refresh_bucket(int bucket)
+{
+ assert(bucket >= 0 && bucket < 160);
+
+ // generate a random node_id within the given bucket
+ node_id target = generate_id();
+ int num_bits = 160 - bucket;
+ node_id mask(0);
+ for (int i = 0; i < num_bits; ++i)
+ {
+ int byte = i / 8;
+ mask[byte] |= 0x80 >> (i % 8);
+ }
+
+ node_id root = m_id;
+ root &= mask;
+ target &= ~mask;
+ target |= root;
+
+ // make sure this is in another subtree than m_id
+ // clear the (num_bits - 1) bit and then set it to the
+ // inverse of m_id's corresponding bit.
+ target[(num_bits - 1) / 8] &= ~(0x80 >> ((num_bits - 1) % 8));
+ target[(num_bits - 1) / 8] |=
+ (~(m_id[(num_bits - 1) / 8])) & (0x80 >> ((num_bits - 1) % 8));
+
+ assert(distance_exp(m_id, target) == bucket);
+
+ std::vector<node_entry> start;
+ start.reserve(m_table.bucket_size());
+ m_table.find_node(target, start, false, m_table.bucket_size());
+
+ refresh::initiate(target, m_settings.search_branching, 10, m_table.bucket_size()
+ , m_table, start.begin(), start.end(), m_rpc, bind(&nop));
+ m_table.touch_bucket(bucket);
+}
+
+
+void node_impl::incoming(msg const& m)
+{
+ if (m_rpc.incoming(m))
+ {
+ refresh();
+ }
+}
+
+namespace
+{
+
+ class announce_observer : public observer
+ {
+ public:
+ announce_observer(sha1_hash const& info_hash, int listen_port
+ , entry const& write_token)
+ : m_info_hash(info_hash)
+ , m_listen_port(listen_port)
+ , m_token(write_token)
+ {}
+
+ void send(msg& m)
+ {
+ m.port = m_listen_port;
+ m.info_hash = m_info_hash;
+ m.write_token = m_token;
+ }
+
+ void timeout() {}
+ void reply(msg const&) {}
+
+ private:
+ sha1_hash m_info_hash;
+ int m_listen_port;
+ entry m_token;
+ };
+
+ class get_peers_observer : public observer
+ {
+ public:
+ get_peers_observer(sha1_hash const& info_hash, int listen_port
+ , rpc_manager& rpc
+ , boost::function<void(std::vector<tcp::endpoint> const&, sha1_hash const&)> f)
+ : m_info_hash(info_hash)
+ , m_listen_port(listen_port)
+ , m_rpc(rpc)
+ , m_fun(f)
+ {}
+
+ void send(msg& m)
+ {
+ m.port = m_listen_port;
+ m.info_hash = m_info_hash;
+ }
+
+ void timeout() {}
+ void reply(msg const& r)
+ {
+ m_rpc.invoke(messages::announce_peer, r.addr
+ , boost::shared_ptr<observer>(
+ new announce_observer(m_info_hash, m_listen_port, r.write_token)));
+ m_fun(r.peers, m_info_hash);
+ }
+
+ private:
+ sha1_hash m_info_hash;
+ int m_listen_port;
+ rpc_manager& m_rpc;
+ boost::function<void(std::vector<tcp::endpoint> const&, sha1_hash const&)> m_fun;
+ };
+
+
+ void announce_fun(std::vector<node_entry> const& v, rpc_manager& rpc
+ , int listen_port, sha1_hash const& ih
+ , boost::function<void(std::vector<tcp::endpoint> const&, sha1_hash const&)> f)
+ {
+ bool nodes = false;
+ // only store on the first k nodes
+ for (std::vector<node_entry>::const_iterator i = v.begin()
+ , end(v.end()); i != end; ++i)
+ {
+ rpc.invoke(messages::get_peers, i->addr, boost::shared_ptr<observer>(
+ new get_peers_observer(ih, listen_port, rpc, f)));
+ nodes = true;
+ }
+ }
+
+}
+
+namespace
+{
+ struct dummy_observer : observer
+ {
+ virtual void reply(msg const&) {}
+ virtual void timeout() {}
+ virtual void send(msg&) {}
+ };
+}
+
+void node_impl::add_router_node(udp::endpoint router)
+{
+ m_table.add_router_node(router);
+}
+
+void node_impl::add_node(udp::endpoint node)
+{
+ // ping the node, and if we get a reply, it
+ // will be added to the routing table
+ observer_ptr p(new dummy_observer());
+ m_rpc.invoke(messages::ping, node, p);
+}
+
+void node_impl::announce(sha1_hash const& info_hash, int listen_port
+ , boost::function<void(std::vector<tcp::endpoint> const&, sha1_hash const&)> f)
+{
+ // search for nodes with ids close to id, and then invoke the
+ // get_peers and then announce_peer rpc on them.
+ closest_nodes::initiate(info_hash, m_settings.search_branching
+ , m_table.bucket_size(), m_table, m_rpc
+ , boost::bind(&announce_fun, _1, boost::ref(m_rpc), listen_port
+ , info_hash, f));
+}
+
+time_duration node_impl::refresh_timeout()
+{
+ int refresh = -1;
+ ptime now = second_clock::universal_time();
+ ptime next = now + minutes(15);
+ for (int i = 0; i < 160; ++i)
+ {
+ ptime r = m_table.next_refresh(i);
+ if (r <= now)
+ {
+ if (refresh == -1) refresh = i;
+ }
+ else if (r < next)
+ {
+ next = r;
+ }
+ }
+ if (refresh != -1)
+ {
+#ifdef TORRENT_DHT_VERBOSE_LOGGING
+ TORRENT_LOG(node) << "refreshing bucket: " << refresh;
+#endif
+ refresh_bucket(refresh);
+ }
+ if (next < now + seconds(5)) return seconds(5);
+ return next - now;
+}
+
+time_duration node_impl::connection_timeout()
+{
+ time_duration d = m_rpc.tick();
+
+ ptime now(second_clock::universal_time());
+ if (now - m_last_tracker_tick < minutes(10)) return d;
+ m_last_tracker_tick = now;
+
+ // look through all peers and see if any have timed out
+ for (data_iterator i = begin_data(), end(end_data()); i != end;)
+ {
+ torrent_entry& t = i->second;
+ node_id const& key = i->first;
+ ++i;
+ purge_peers(t.peers);
+
+ // if there are no more peers, remove the entry altogether
+ if (t.peers.empty())
+ {
+ table_t::iterator i = m_map.find(key);
+ if (i != m_map.end()) m_map.erase(i);
+ }
+ }
+ return d;
+}
+
+void node_impl::on_announce(msg const& m, msg& reply)
+{
+ if (!verify_token(m))
+ {
+ reply.message_id = messages::error;
+ reply.error_code = 203;
+ reply.error_msg = "Incorrect write token in announce_peer message";
+ return;
+ }
+
+ // the token was correct. That means this
+ // node is not spoofing its address. So, let
+ // the table get a chance to add it.
+ m_table.node_seen(m.id, m.addr);
+
+ torrent_entry& v = m_map[m.info_hash];
+ peer_entry e;
+ e.addr = tcp::endpoint(m.addr.address(), m.addr.port());
+ e.added = second_clock::universal_time();
+ std::set<peer_entry>::iterator i = v.peers.find(e);
+ if (i != v.peers.end()) v.peers.erase(i++);
+ v.peers.insert(i, e);
+}
+
+namespace
+{
+ tcp::endpoint get_endpoint(peer_entry const& p)
+ {
+ return p.addr;
+ }
+}
+
+bool node_impl::on_find(msg const& m, std::vector<tcp::endpoint>& peers) const
+{
+ table_t::const_iterator i = m_map.find(m.info_hash);
+ if (i == m_map.end()) return false;
+
+ torrent_entry const& v = i->second;
+
+ int num = (std::min)((int)v.peers.size(), m_settings.max_peers_reply);
+ peers.clear();
+ peers.reserve(num);
+ random_sample_n(boost::make_transform_iterator(v.peers.begin(), &get_endpoint)
+ , boost::make_transform_iterator(v.peers.end(), &get_endpoint)
+ , std::back_inserter(peers), num);
+
+#ifdef TORRENT_DHT_VERBOSE_LOGGING
+ for (std::vector<tcp::endpoint>::iterator i = peers.begin()
+ , end(peers.end()); i != end; ++i)
+ {
+ TORRENT_LOG(node) << " " << *i;
+ }
+#endif
+ return true;
+}
+
+void node_impl::incoming_request(msg const& m)
+{
+ msg reply;
+ switch (m.message_id)
+ {
+ case messages::ping:
+ break;
+ case messages::get_peers:
+ {
+ reply.info_hash = m.info_hash;
+ reply.write_token = generate_token(m);
+
+ if (!on_find(m, reply.peers))
+ {
+ // we don't have any peers for this info_hash,
+ // return nodes instead
+ m_table.find_node(m.info_hash, reply.nodes, false);
+#ifdef TORRENT_DHT_VERBOSE_LOGGING
+ for (std::vector<node_entry>::iterator i = reply.nodes.begin()
+ , end(reply.nodes.end()); i != end; ++i)
+ {
+ TORRENT_LOG(node) << " " << i->id << " " << i->addr;
+ }
+#endif
+ }
+ }
+ break;
+ case messages::find_node:
+ {
+ reply.info_hash = m.info_hash;
+
+ m_table.find_node(m.info_hash, reply.nodes, false);
+#ifdef TORRENT_DHT_VERBOSE_LOGGING
+ for (std::vector<node_entry>::iterator i = reply.nodes.begin()
+ , end(reply.nodes.end()); i != end; ++i)
+ {
+ TORRENT_LOG(node) << " " << i->id << " " << i->addr;
+ }
+#endif
+ }
+ break;
+ case messages::announce_peer:
+ {
+ on_announce(m, reply);
+ }
+ break;
+ };
+
+ if (m_table.need_node(m.id))
+ m_rpc.reply_with_ping(reply, m);
+ else
+ m_rpc.reply(reply, m);
+}
+
+
+} } // namespace libtorrent::dht
diff --git a/library/kademlia/node_id.cpp b/library/kademlia/node_id.cpp
new file mode 100644
index 000000000..d435846d2
--- /dev/null
+++ b/library/kademlia/node_id.cpp
@@ -0,0 +1,97 @@
+/*
+
+Copyright (c) 2006, 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.
+
+*/
+
+#include <algorithm>
+#include <iomanip>
+#include <cassert>
+#include <boost/bind.hpp>
+
+#include "libtorrent/kademlia/node_id.hpp"
+
+using boost::bind;
+
+namespace libtorrent { namespace dht
+{
+
+// returns the distance between the two nodes
+// using the kademlia XOR-metric
+node_id distance(node_id const& n1, node_id const& n2)
+{
+ node_id ret;
+ node_id::iterator k = ret.begin();
+ for (node_id::const_iterator i = n1.begin(), j = n2.begin()
+ , end(n1.end()); i != end; ++i, ++j, ++k)
+ {
+ *k = *i ^ *j;
+ }
+ return ret;
+}
+
+// returns true if: distance(n1, ref) < distance(n2, ref)
+bool compare_ref(node_id const& n1, node_id const& n2, node_id const& ref)
+{
+ for (node_id::const_iterator i = n1.begin(), j = n2.begin()
+ , k = ref.begin(), end(n1.end()); i != end; ++i, ++j, ++k)
+ {
+ boost::uint8_t lhs = (*i ^ *k);
+ boost::uint8_t rhs = (*j ^ *k);
+ if (lhs < rhs) return true;
+ if (lhs > rhs) return false;
+ }
+ return false;
+}
+
+// returns n in: 2^n <= distance(n1, n2) < 2^(n+1)
+// useful for finding out which bucket a node belongs to
+int distance_exp(node_id const& n1, node_id const& n2)
+{
+ int byte = node_id::size - 1;
+ for (node_id::const_iterator i = n1.begin(), j = n2.begin()
+ , end(n1.end()); i != end; ++i, ++j, --byte)
+ {
+ assert(byte >= 0);
+ boost::uint8_t t = *i ^ *j;
+ if (t == 0) continue;
+ // we have found the first non-zero byte
+ // return the bit-number of the first bit
+ // that differs
+ int bit = byte * 8;
+ for (int b = 7; b > 0; --b)
+ if (t >= (1 << b)) return bit + b;
+ return bit;
+ }
+
+ return 0;
+}
+
+} } // namespace libtorrent::dht
+
diff --git a/library/kademlia/refresh.cpp b/library/kademlia/refresh.cpp
new file mode 100644
index 000000000..37300d16e
--- /dev/null
+++ b/library/kademlia/refresh.cpp
@@ -0,0 +1,190 @@
+/*
+
+Copyright (c) 2006, Arvid Norberg & Daniel Wallin
+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.
+
+*/
+
+#include <libtorrent/kademlia/refresh.hpp>
+#include <libtorrent/kademlia/routing_table.hpp>
+#include <libtorrent/kademlia/rpc_manager.hpp>
+#include <libtorrent/kademlia/logging.hpp>
+
+#include <libtorrent/io.hpp>
+
+#include <boost/bind.hpp>
+
+using boost::bind;
+
+namespace libtorrent { namespace dht
+{
+
+using asio::ip::udp;
+
+#ifdef TORRENT_DHT_VERBOSE_LOGGING
+TORRENT_DEFINE_LOG(refresh)
+#endif
+
+typedef boost::shared_ptr<observer> observer_ptr;
+
+class refresh_observer : public observer
+{
+public:
+ refresh_observer(
+ boost::intrusive_ptr<refresh> const& algorithm
+ , node_id self
+ , node_id target
+ )
+ : m_target(target)
+ , m_self(self)
+ , m_algorithm(algorithm)
+ {}
+
+ void send(msg& m)
+ {
+ m.info_hash = m_target;
+ }
+
+ void timeout();
+ void reply(msg const& m);
+
+private:
+ node_id const m_target;
+ node_id const m_self;
+ boost::intrusive_ptr<refresh> m_algorithm;
+};
+
+void refresh_observer::reply(msg const& in)
+{
+ if (!in.nodes.empty())
+ {
+ for (msg::nodes_t::const_iterator i = in.nodes.begin()
+ , end(in.nodes.end()); i != end; ++i)
+ {
+ m_algorithm->traverse(i->id, i->addr);
+ }
+ }
+ m_algorithm->finished(m_self);
+}
+
+void refresh_observer::timeout()
+{
+ m_algorithm->failed(m_self);
+}
+
+class ping_observer : public observer
+{
+public:
+ ping_observer(
+ boost::intrusive_ptr<refresh> const& algorithm
+ , node_id self
+ )
+ : m_self(self)
+ , m_algorithm(algorithm)
+ {}
+
+ void send(msg& p) {}
+ void timeout();
+ void reply(msg const& m);
+
+private:
+ node_id const m_self;
+ boost::intrusive_ptr<refresh> m_algorithm;
+};
+
+void ping_observer::reply(msg const& m)
+{
+ m_algorithm->ping_reply(m_self);
+}
+
+void ping_observer::timeout()
+{
+ m_algorithm->ping_timeout(m_self);
+}
+
+void refresh::invoke(node_id const& nid, udp::endpoint addr)
+{
+ observer_ptr p(new refresh_observer(
+ this
+ , nid
+ , m_target
+ ));
+
+ m_rpc.invoke(messages::find_node, addr, p);
+}
+
+void refresh::done()
+{
+ m_leftover_nodes_iterator = (int)m_results.size() > m_max_results ?
+ m_results.begin() + m_max_results : m_results.end();
+
+ invoke_pings_or_finish();
+}
+
+void refresh::ping_reply(node_id nid)
+{
+ m_active_pings--;
+ invoke_pings_or_finish();
+}
+
+void refresh::ping_timeout(node_id nid)
+{
+ m_active_pings--;
+ invoke_pings_or_finish();
+}
+
+void refresh::invoke_pings_or_finish()
+{
+ while (m_active_pings < m_max_active_pings)
+ {
+ if (m_leftover_nodes_iterator == m_results.end()) break;
+
+ result const& node = *m_leftover_nodes_iterator;
+
+ // Skip initial nodes
+ if (node.flags & result::initial)
+ {
+ ++m_leftover_nodes_iterator;
+ continue;
+ }
+
+ observer_ptr p(new ping_observer(this, node.id));
+
+ m_rpc.invoke(messages::ping, node.addr, p);
+ ++m_active_pings;
+ ++m_leftover_nodes_iterator;
+ }
+
+ if (m_active_pings == 0)
+ {
+ m_done_callback();
+ }
+}
+
+} } // namespace libtorrent::dht
+
diff --git a/library/kademlia/routing_table.cpp b/library/kademlia/routing_table.cpp
new file mode 100644
index 000000000..32f7514f2
--- /dev/null
+++ b/library/kademlia/routing_table.cpp
@@ -0,0 +1,434 @@
+/*
+
+Copyright (c) 2006, 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.
+
+*/
+
+#include <vector>
+#include <deque>
+#include <algorithm>
+#include <functional>
+#include <numeric>
+#include <boost/cstdint.hpp>
+#include <boost/bind.hpp>
+#include <boost/date_time/posix_time/posix_time.hpp>
+
+#include "libtorrent/kademlia/routing_table.hpp"
+#include "libtorrent/kademlia/node_id.hpp"
+#include "libtorrent/session_settings.hpp"
+
+using boost::bind;
+using boost::uint8_t;
+
+using boost::posix_time::second_clock;
+using boost::posix_time::minutes;
+using boost::posix_time::seconds;
+using boost::posix_time::hours;
+
+namespace pt = boost::posix_time;
+
+namespace libtorrent { namespace dht
+{
+
+using asio::ip::udp;
+typedef asio::ip::address_v4 address;
+
+routing_table::routing_table(node_id const& id, int bucket_size
+ , dht_settings const& settings)
+ : m_bucket_size(bucket_size)
+ , m_settings(settings)
+ , m_id(id)
+ , m_lowest_active_bucket(160)
+{
+ // distribute the refresh times for the buckets in an
+ // attempt do even out the network load
+ for (int i = 0; i < 160; ++i)
+ m_bucket_activity[i] = second_clock::universal_time() - seconds(15*60 - i*5);
+}
+
+boost::tuple<int, int> routing_table::size() const
+{
+ int nodes = 0;
+ int replacements = 0;
+ for (table_t::const_iterator i = m_buckets.begin()
+ , end(m_buckets.end()); i != end; ++i)
+ {
+ nodes += i->first.size();
+ replacements += i->second.size();
+ }
+ return boost::make_tuple(nodes, replacements);
+}
+
+void routing_table::print_state(std::ostream& os) const
+{
+ os << "kademlia routing table state\n"
+ << "bucket_size: " << m_bucket_size << "\n"
+ << "node_id: " << m_id << "\n\n";
+
+ os << "number of nodes per bucket:\n"
+ "live\n";
+ for (int k = 0; k < 8; ++k)
+ {
+ for (table_t::const_iterator i = m_buckets.begin(), end(m_buckets.end());
+ i != end; ++i)
+ {
+ os << (int(i->first.size()) > (7 - k) ? "|" : " ");
+ }
+ os << "\n";
+ }
+ for (table_t::const_iterator i = m_buckets.begin(), end(m_buckets.end());
+ i != end; ++i)
+ {
+ os << "+";
+ }
+ os << "\n";
+ for (int k = 0; k < 8; ++k)
+ {
+ for (table_t::const_iterator i = m_buckets.begin(), end(m_buckets.end());
+ i != end; ++i)
+ {
+ os << (int(i->second.size()) > k ? "|" : " ");
+ }
+ os << "\n";
+ }
+ os << "cached\n-----------\n";
+
+ os << "nodes:\n";
+ for (table_t::const_iterator i = m_buckets.begin(), end(m_buckets.end());
+ i != end; ++i)
+ {
+ int bucket_index = int(i - m_buckets.begin());
+ os << "bucket " << bucket_index << " "
+ << to_simple_string(m_bucket_activity[bucket_index])
+ << " " << (bucket_index >= m_lowest_active_bucket?"active":"inactive")
+ << "\n";
+ for (bucket_t::const_iterator j = i->first.begin()
+ , end(i->first.end()); j != end; ++j)
+ {
+ os << "ip: " << j->addr << " fails: " << j->fail_count
+ << " id: " << j->id << "\n";
+ }
+ }
+}
+
+void routing_table::touch_bucket(int bucket)
+{
+ m_bucket_activity[bucket] = second_clock::universal_time();
+}
+
+boost::posix_time::ptime routing_table::next_refresh(int bucket)
+{
+ assert(bucket < 160);
+ assert(bucket >= 0);
+ // lower than or equal to since a refresh of bucket 0 will
+ // effectively refresh the lowest active bucket as well
+ if (bucket <= m_lowest_active_bucket && bucket > 0)
+ return second_clock::universal_time() + minutes(15);
+ return m_bucket_activity[bucket] + minutes(15);
+}
+
+void routing_table::replacement_cache(bucket_t& nodes) const
+{
+ for (table_t::const_iterator i = m_buckets.begin()
+ , end(m_buckets.end()); i != end; ++i)
+ {
+ std::copy(i->second.begin(), i->second.end()
+ , std::back_inserter(nodes));
+ }
+}
+
+bool routing_table::need_node(node_id const& id)
+{
+ int bucket_index = distance_exp(m_id, id);
+ assert(bucket_index < (int)m_buckets.size());
+ assert(bucket_index >= 0);
+ bucket_t& b = m_buckets[bucket_index].first;
+ bucket_t& rb = m_buckets[bucket_index].second;
+
+ // if the replacement cache is full, we don't
+ // need another node. The table is fine the
+ // way it is.
+ if ((int)rb.size() >= m_bucket_size) return false;
+
+ // if the node already exists, we don't need it
+ if (std::find_if(b.begin(), b.end(), bind(std::equal_to<node_id>()
+ , bind(&node_entry::id, _1), id)) != b.end()) return false;
+
+ if (std::find_if(rb.begin(), rb.end(), bind(std::equal_to<node_id>()
+ , bind(&node_entry::id, _1), id)) != rb.end()) return false;
+
+ return true;
+}
+
+void routing_table::node_failed(node_id const& id)
+{
+ int bucket_index = distance_exp(m_id, id);
+ assert(bucket_index < (int)m_buckets.size());
+ assert(bucket_index >= 0);
+ bucket_t& b = m_buckets[bucket_index].first;
+ bucket_t& rb = m_buckets[bucket_index].second;
+
+ bucket_t::iterator i = std::find_if(b.begin(), b.end()
+ , bind(std::equal_to<node_id>()
+ , bind(&node_entry::id, _1), id));
+
+ if (i == b.end()) return;
+
+ // if messages to ourself fails, ignore it
+ if (bucket_index == 0) return;
+
+ if (rb.empty())
+ {
+ ++i->fail_count;
+ if (i->fail_count >= m_settings.max_fail_count)
+ {
+ b.erase(i);
+ assert(m_lowest_active_bucket <= bucket_index);
+ while (m_buckets[m_lowest_active_bucket].first.empty()
+ && m_lowest_active_bucket < 160)
+ {
+ ++m_lowest_active_bucket;
+ }
+ }
+ return;
+ }
+
+ b.erase(i);
+ b.push_back(rb.back());
+ rb.erase(rb.end() - 1);
+}
+
+void routing_table::add_router_node(udp::endpoint router)
+{
+ m_router_nodes.insert(router);
+}
+
+// this function is called every time the node sees
+// a sign of a node being alive. This node will either
+// be inserted in the k-buckets or be moved to the top
+// of its bucket.
+// the return value indicates if the table needs a refresh.
+// if true, the node should refresh the table (i.e. do a find_node
+// on its own id)
+bool routing_table::node_seen(node_id const& id, udp::endpoint addr)
+{
+ if (m_router_nodes.find(addr) != m_router_nodes.end()) return false;
+ int bucket_index = distance_exp(m_id, id);
+ assert(bucket_index < (int)m_buckets.size());
+ assert(bucket_index >= 0);
+ bucket_t& b = m_buckets[bucket_index].first;
+
+ bucket_t::iterator i = std::find_if(b.begin(), b.end()
+ , bind(std::equal_to<node_id>()
+ , bind(&node_entry::id, _1), id));
+
+ bool ret = need_bootstrap();
+
+ m_bucket_activity[bucket_index] = second_clock::universal_time();
+
+ if (i != b.end())
+ {
+ // TODO: what do we do if we see a node with
+ // the same id as a node at a different address?
+// assert(i->addr == addr);
+
+ // we already have the node in our bucket
+ // just move it to the back since it was
+ // the last node we had any contact with
+ // in this bucket
+ b.erase(i);
+ b.push_back(node_entry(id, addr));
+// TORRENT_LOG(table) << "replacing node: " << id << " " << addr;
+ return ret;
+ }
+
+ // if the node was not present in our list
+ // we will only insert it if there is room
+ // for it, or if some of our nodes have gone
+ // offline
+ if ((int)b.size() < m_bucket_size)
+ {
+ b.push_back(node_entry(id, addr));
+ // if bucket index is 0, the node is ourselves
+ // don't updated m_lowest_active_bucket
+ if (bucket_index < m_lowest_active_bucket
+ && bucket_index > 0)
+ m_lowest_active_bucket = bucket_index;
+// TORRENT_LOG(table) << "inserting node: " << id << " " << addr;
+ return ret;
+ }
+
+ // if there is no room, we look for nodes marked as stale
+ // in the k-bucket. If we find one, we can replace it.
+ // A node is considered stale if it has failed at least one
+ // time. Here we choose the node that has failed most times.
+ // If we don't find one, place this node in the replacement-
+ // cache and replace any nodes that will fail in the future
+ // with nodes from that cache.
+
+ i = std::max_element(b.begin(), b.end()
+ , bind(std::less<int>()
+ , bind(&node_entry::fail_count, _1)
+ , bind(&node_entry::fail_count, _2)));
+
+ if (i != b.end() && i->fail_count > 0)
+ {
+ // i points to a node that has been marked
+ // as stale. Replace it with this new one
+ b.erase(i);
+ b.push_back(node_entry(id, addr));
+// TORRENT_LOG(table) << "replacing stale node: " << id << " " << addr;
+ return ret;
+ }
+
+ // if we don't have any identified stale nodes in
+ // the bucket, and the bucket is full, we have to
+ // cache this node and wait until some node fails
+ // and then replace it.
+
+ bucket_t& rb = m_buckets[bucket_index].second;
+
+ i = std::find_if(rb.begin(), rb.end()
+ , bind(std::equal_to<node_id>()
+ , bind(&node_entry::id, _1), id));
+
+ // if the node is already in the replacement bucket
+ // just return.
+ if (i != rb.end()) return ret;
+
+ if ((int)rb.size() > m_bucket_size) rb.erase(rb.begin());
+ rb.push_back(node_entry(id, addr));
+// TORRENT_LOG(table) << "inserting node in replacement cache: " << id << " " << addr;
+ return ret;
+}
+
+bool routing_table::need_bootstrap() const
+{
+ for (const_iterator i = begin(); i != end(); ++i)
+ {
+ if (i->fail_count == 0) return false;
+ }
+ return true;
+}
+
+// fills the vector with the k nodes from our buckets that
+// are nearest to the given id.
+void routing_table::find_node(node_id const& target
+ , std::vector<node_entry>& l, bool include_self, int count)
+{
+ l.clear();
+ if (count == 0) count = m_bucket_size;
+ l.reserve(count);
+
+ int bucket_index = distance_exp(m_id, target);
+ bucket_t& b = m_buckets[bucket_index].first;
+
+ // copy all nodes that hasn't failed into the target
+ // vector.
+ std::remove_copy_if(b.begin(), b.end(), std::back_inserter(l)
+ , bind(&node_entry::fail_count, _1));
+ assert((int)l.size() <= count);
+
+ if ((int)l.size() == count)
+ {
+ assert(std::count_if(l.begin(), l.end()
+ , boost::bind(std::not_equal_to<int>()
+ , boost::bind(&node_entry::fail_count, _1), 0)) == 0);
+ return;
+ }
+
+ // if we didn't have enough nodes in that bucket
+ // we have to reply with nodes from buckets closer
+ // to us. i.e. all the buckets in the range
+ // [0, bucket_index) if we are to include ourself
+ // or [1, bucket_index) if not.
+ bucket_t tmpb;
+ for (int i = include_self?0:1; i < count; ++i)
+ {
+ bucket_t& b = m_buckets[i].first;
+ std::remove_copy_if(b.begin(), b.end(), std::back_inserter(tmpb)
+ , bind(&node_entry::fail_count, _1));
+ }
+
+ std::random_shuffle(tmpb.begin(), tmpb.end());
+ size_t to_copy = (std::min)(m_bucket_size - l.size()
+ , tmpb.size());
+ std::copy(tmpb.begin(), tmpb.begin() + to_copy
+ , std::back_inserter(l));
+
+ assert((int)l.size() <= m_bucket_size);
+
+ // return if we have enough nodes or if the bucket index
+ // is the biggest index available (there are no more buckets)
+ // to look in.
+ if ((int)l.size() == count
+ || bucket_index == (int)m_buckets.size() - 1)
+ {
+ assert(std::count_if(l.begin(), l.end()
+ , boost::bind(std::not_equal_to<int>()
+ , boost::bind(&node_entry::fail_count, _1), 0)) == 0);
+ return;
+ }
+
+ for (size_t i = bucket_index + 1; i < m_buckets.size(); ++i)
+ {
+ bucket_t& b = m_buckets[i].first;
+
+ std::remove_copy_if(b.begin(), b.end(), std::back_inserter(l)
+ , bind(&node_entry::fail_count, _1));
+ if ((int)l.size() >= count)
+ {
+ l.erase(l.begin() + count, l.end());
+ assert(std::count_if(l.begin(), l.end()
+ , boost::bind(std::not_equal_to<int>()
+ , boost::bind(&node_entry::fail_count, _1), 0)) == 0);
+ return;
+ }
+ }
+ assert((int)l.size() == count
+ || std::distance(l.begin(), l.end()) < m_bucket_size);
+ assert((int)l.size() <= count);
+
+ assert(std::count_if(l.begin(), l.end()
+ , boost::bind(std::not_equal_to<int>()
+ , boost::bind(&node_entry::fail_count, _1), 0)) == 0);
+}
+
+routing_table::iterator routing_table::begin() const
+{
+ return iterator(m_buckets.begin(), m_buckets.end());
+}
+
+routing_table::iterator routing_table::end() const
+{
+ return iterator(m_buckets.end(), m_buckets.end());
+}
+
+} } // namespace libtorrent::dht
+
diff --git a/library/kademlia/rpc_manager.cpp b/library/kademlia/rpc_manager.cpp
new file mode 100644
index 000000000..770b11bf5
--- /dev/null
+++ b/library/kademlia/rpc_manager.cpp
@@ -0,0 +1,356 @@
+/*
+
+Copyright (c) 2006, 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.
+
+*/
+
+#include <boost/date_time/posix_time/posix_time_types.hpp>
+#include <boost/date_time/posix_time/ptime.hpp>
+#include <boost/bind.hpp>
+
+#include <libtorrent/io.hpp>
+#include <libtorrent/invariant_check.hpp>
+#include <libtorrent/kademlia/rpc_manager.hpp>
+#include <libtorrent/kademlia/logging.hpp>
+#include <libtorrent/kademlia/routing_table.hpp>
+#include <libtorrent/hasher.hpp>
+
+#include <fstream>
+
+using boost::posix_time::ptime;
+using boost::posix_time::time_duration;
+using boost::posix_time::microsec_clock;
+using boost::posix_time::seconds;
+using boost::posix_time::milliseconds;
+using boost::shared_ptr;
+using boost::bind;
+
+namespace libtorrent { namespace dht
+{
+
+namespace io = libtorrent::detail;
+
+#ifdef TORRENT_DHT_VERBOSE_LOGGING
+TORRENT_DEFINE_LOG(rpc)
+#endif
+
+node_id generate_id();
+
+rpc_manager::rpc_manager(fun const& f, node_id const& our_id
+ , routing_table& table, send_fun const& sf)
+ : m_next_transaction_id(rand() % max_transactions)
+ , m_oldest_transaction_id(m_next_transaction_id)
+ , m_incoming(f)
+ , m_send(sf)
+ , m_our_id(our_id)
+ , m_table(table)
+ , m_timer(boost::posix_time::microsec_clock::universal_time())
+ , m_random_number(generate_id())
+{
+ std::srand(time(0));
+}
+
+rpc_manager::~rpc_manager()
+{
+#ifdef TORRENT_DHT_VERBOSE_LOGGING
+ TORRENT_LOG(rpc) << "Destructing";
+#endif
+}
+
+#ifndef NDEBUG
+void rpc_manager::check_invariant() const
+{
+ assert(m_oldest_transaction_id >= 0);
+ assert(m_oldest_transaction_id < max_transactions);
+ assert(m_next_transaction_id >= 0);
+ assert(m_next_transaction_id < max_transactions);
+ assert(!m_transactions[m_next_transaction_id]);
+
+ for (int i = (m_next_transaction_id + 1) % max_transactions;
+ i != m_oldest_transaction_id; i = (i + 1) % max_transactions)
+ {
+ assert(!m_transactions[i]);
+ }
+}
+#endif
+
+bool rpc_manager::incoming(msg const& m)
+{
+ INVARIANT_CHECK;
+
+ if (m.reply)
+ {
+ // if we don't have the transaction id in our
+ // request list, ignore the packet
+
+ if (m.transaction_id.size() != 2)
+ {
+#ifdef TORRENT_DHT_VERBOSE_LOGGING
+ TORRENT_LOG(rpc) << "Reply with invalid transaction id size: "
+ << m.transaction_id.size() << " from " << m.addr;
+#endif
+ return false;
+ }
+
+ std::string::const_iterator i = m.transaction_id.begin();
+ int tid = io::read_uint16(i);
+
+ if (tid >= (int)m_transactions.size()
+ || tid < 0)
+ {
+#ifdef TORRENT_DHT_VERBOSE_LOGGING
+ TORRENT_LOG(rpc) << "Reply with unknown transaction id: "
+ << tid << " from " << m.addr;
+#endif
+ return false;
+ }
+
+ boost::shared_ptr<observer> o = m_transactions[tid];
+
+ if (!o)
+ {
+#ifdef TORRENT_DHT_VERBOSE_LOGGING
+ TORRENT_LOG(rpc) << "Reply with unknown transaction id: "
+ << tid << " from " << m.addr;
+#endif
+ return false;
+ }
+
+ if (m.addr != o->target_addr)
+ {
+#ifdef TORRENT_DHT_VERBOSE_LOGGING
+ TORRENT_LOG(rpc) << "Reply with incorrect address and valid transaction id: "
+ << tid << " from " << m.addr;
+#endif
+ return false;
+ }
+
+#ifdef TORRENT_DHT_VERBOSE_LOGGING
+ std::ofstream reply_stats("libtorrent_logs/round_trip_ms.log", std::ios::app);
+ reply_stats << m.addr << "\t" << (microsec_clock::universal_time()
+ - o->sent).total_milliseconds() << std::endl;
+#endif
+ o->reply(m);
+ m_transactions[tid].reset();
+
+ if (m.piggy_backed_ping)
+ {
+ // there is a ping request piggy
+ // backed in this reply
+ msg ph;
+ ph.message_id = messages::ping;
+ ph.transaction_id = m.ping_transaction_id;
+ ph.id = m_our_id;
+ ph.addr = m.addr;
+
+ msg empty;
+
+ reply(empty, ph);
+ }
+ return m_table.node_seen(m.id, m.addr);
+ }
+ else
+ {
+ // this is an incoming request
+ m_incoming(m);
+ }
+ return false;
+}
+
+time_duration rpc_manager::tick()
+{
+ INVARIANT_CHECK;
+
+ using boost::posix_time::microsec_clock;
+
+ const int timeout_ms = 20 * 1000;
+
+ // look for observers that has timed out
+
+ if (m_next_transaction_id == m_oldest_transaction_id) return milliseconds(timeout_ms);
+
+ for (;m_next_transaction_id != m_oldest_transaction_id;
+ m_oldest_transaction_id = (m_oldest_transaction_id + 1) % max_transactions)
+ {
+ assert(m_oldest_transaction_id >= 0);
+ assert(m_oldest_transaction_id < max_transactions);
+
+ boost::shared_ptr<observer> o = m_transactions[m_oldest_transaction_id];
+ if (!o) continue;
+
+ time_duration diff = o->sent + milliseconds(timeout_ms)
+ - microsec_clock::universal_time();
+ if (diff > seconds(0))
+ {
+ if (diff < seconds(1)) return seconds(1);
+ return diff;
+ }
+
+ m_transactions[m_oldest_transaction_id].reset();
+ o->timeout();
+ }
+ return milliseconds(timeout_ms);
+}
+
+unsigned int rpc_manager::new_transaction_id()
+{
+ INVARIANT_CHECK;
+
+ unsigned int tid = m_next_transaction_id;
+ m_next_transaction_id = (m_next_transaction_id + 1) % max_transactions;
+// boost::shared_ptr<observer> o = m_transactions[m_next_transaction_id];
+ if (m_transactions[m_next_transaction_id])
+ {
+ m_transactions[m_next_transaction_id].reset();
+ assert(m_oldest_transaction_id == m_next_transaction_id);
+ }
+ if (m_oldest_transaction_id == m_next_transaction_id)
+ {
+ m_oldest_transaction_id = (m_oldest_transaction_id + 1) % max_transactions;
+#ifdef TORRENT_DHT_VERBOSE_LOGGING
+ TORRENT_LOG(rpc) << "WARNING: transaction limit reached! Too many concurrent"
+ " messages! limit: " << (int)max_transactions;
+#endif
+ update_oldest_transaction_id();
+ }
+
+#ifndef NDEBUG
+ assert(!m_transactions[m_next_transaction_id]);
+ for (int i = (m_next_transaction_id + 1) % max_transactions;
+ i != m_oldest_transaction_id; i = (i + 1) % max_transactions)
+ {
+ assert(!m_transactions[i]);
+ }
+#endif
+
+// hopefully this wouldn't happen, but unfortunately, the
+// traversal algorithm will simply fail in case its connections
+// are overwritten. If timeout() is called, it will likely spawn
+// another connection, which in turn will close the next one
+// and so on.
+// if (o) o->timeout();
+ return tid;
+}
+
+void rpc_manager::update_oldest_transaction_id()
+{
+ INVARIANT_CHECK;
+
+ assert(m_oldest_transaction_id != m_next_transaction_id);
+ while (!m_transactions[m_oldest_transaction_id])
+ {
+ m_oldest_transaction_id = (m_oldest_transaction_id + 1)
+ % max_transactions;
+ if (m_oldest_transaction_id == m_next_transaction_id)
+ break;
+ }
+}
+
+void rpc_manager::invoke(int message_id, udp::endpoint target_addr
+ , shared_ptr<observer> o)
+{
+ INVARIANT_CHECK;
+
+ msg m;
+ m.message_id = message_id;
+ m.reply = false;
+ m.id = m_our_id;
+ m.addr = target_addr;
+ int tid = new_transaction_id();
+ m.transaction_id.clear();
+ std::back_insert_iterator<std::string> out(m.transaction_id);
+ io::write_uint16(tid, out);
+
+ o->send(m);
+
+ m_transactions[tid] = o;
+ o->sent = boost::posix_time::microsec_clock::universal_time();
+ o->target_addr = target_addr;
+
+#ifdef TORRENT_DHT_VERBOSE_LOGGING
+ TORRENT_LOG(rpc) << "Invoking " << messages::ids[message_id]
+ << " -> " << target_addr;
+#endif
+ m_send(m);
+}
+
+void rpc_manager::reply(msg& m, msg const& reply_to)
+{
+ INVARIANT_CHECK;
+
+ if (m.message_id != messages::error)
+ m.message_id = reply_to.message_id;
+ m.addr = reply_to.addr;
+ m.reply = true;
+ m.piggy_backed_ping = false;
+ m.id = m_our_id;
+ m.transaction_id = reply_to.transaction_id;
+
+ m_send(m);
+}
+
+namespace
+{
+ struct dummy_observer : observer
+ {
+ virtual void reply(msg const&) {}
+ virtual void timeout() {}
+ virtual void send(msg&) {}
+ };
+}
+
+void rpc_manager::reply_with_ping(msg& m, msg const& reply_to)
+{
+ INVARIANT_CHECK;
+
+ if (m.message_id != messages::error)
+ m.message_id = reply_to.message_id;
+ m.addr = reply_to.addr;
+ m.reply = true;
+ m.piggy_backed_ping = true;
+ m.id = m_our_id;
+ m.transaction_id = reply_to.transaction_id;
+
+ int ptid = new_transaction_id();
+ m.ping_transaction_id.clear();
+ std::back_insert_iterator<std::string> out(m.ping_transaction_id);
+ io::write_uint16(ptid, out);
+
+ boost::shared_ptr<observer> o(new dummy_observer);
+ m_transactions[ptid] = o;
+ o->sent = boost::posix_time::microsec_clock::universal_time();
+ o->target_addr = m.addr;
+
+ m_send(m);
+}
+
+
+
+} } // namespace libtorrent::dht
+
diff --git a/library/kademlia/traversal_algorithm.cpp b/library/kademlia/traversal_algorithm.cpp
new file mode 100644
index 000000000..88c62f836
--- /dev/null
+++ b/library/kademlia/traversal_algorithm.cpp
@@ -0,0 +1,162 @@
+/*
+
+Copyright (c) 2006, Arvid Norberg & Daniel Wallin
+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.
+
+*/
+
+#include <libtorrent/kademlia/traversal_algorithm.hpp>
+#include <libtorrent/kademlia/routing_table.hpp>
+#include <libtorrent/kademlia/rpc_manager.hpp>
+
+#include <boost/bind.hpp>
+
+using boost::bind;
+using asio::ip::udp;
+
+namespace libtorrent { namespace dht
+{
+#ifdef TORRENT_DHT_VERBOSE_LOGGING
+TORRENT_DEFINE_LOG(traversal)
+#endif
+
+void traversal_algorithm::add_entry(node_id const& id, udp::endpoint addr, unsigned char flags)
+{
+ if (m_failed.find(addr) != m_failed.end()) return;
+
+ result const entry(id, addr, flags);
+
+ std::vector<result>::iterator i = std::lower_bound(
+ m_results.begin()
+ , m_results.end()
+ , entry
+ , bind(
+ compare_ref
+ , bind(&result::id, _1)
+ , bind(&result::id, _2)
+ , m_target
+ )
+ );
+
+ if (i == m_results.end() || i->id != id)
+ {
+#ifdef TORRENT_DHT_VERBOSE_LOGGING
+ TORRENT_LOG(traversal) << "adding result: " << id << " " << addr;
+#endif
+ m_results.insert(i, entry);
+ }
+}
+
+void traversal_algorithm::traverse(node_id const& id, udp::endpoint addr)
+{
+ add_entry(id, addr, 0);
+}
+
+void traversal_algorithm::finished(node_id const& id)
+{
+ --m_invoke_count;
+ add_requests();
+ if (m_invoke_count == 0) done();
+}
+
+void traversal_algorithm::failed(node_id const& id)
+{
+ m_invoke_count--;
+
+ std::vector<result>::iterator i = std::find_if(
+ m_results.begin()
+ , m_results.end()
+ , bind(
+ std::equal_to<node_id>()
+ , bind(&result::id, _1)
+ , id
+ )
+ );
+
+ assert(i != m_results.end());
+
+ assert(i->flags & result::queried);
+ m_failed.insert(i->addr);
+#ifdef TORRENT_DHT_VERBOSE_LOGGING
+ TORRENT_LOG(traversal) << "failed: " << i->id << " " << i->addr;
+#endif
+ m_results.erase(i);
+ m_table.node_failed(id);
+ add_requests();
+ if (m_invoke_count == 0) done();
+}
+
+void traversal_algorithm::add_request(node_id const& id, udp::endpoint addr)
+{
+ invoke(id, addr);
+ ++m_invoke_count;
+}
+
+namespace
+{
+ bool bitwise_nand(unsigned char lhs, unsigned char rhs)
+ {
+ return (lhs & rhs) == 0;
+ }
+}
+
+void traversal_algorithm::add_requests()
+{
+ while (m_invoke_count < m_branch_factor)
+ {
+ // Find the first node that hasn't already been queried.
+ // TODO: Better heuristic
+ std::vector<result>::iterator i = std::find_if(
+ m_results.begin()
+ , last_iterator()
+ , bind(
+ &bitwise_nand
+ , bind(&result::flags, _1)
+ , (unsigned char)result::queried
+ )
+ );
+#ifdef TORRENT_DHT_VERBOSE_LOGGING
+ TORRENT_LOG(traversal) << "nodes left (" << this << "): " << (last_iterator() - i);
+#endif
+
+ if (i == last_iterator()) break;
+
+ add_request(i->id, i->addr);
+ i->flags |= result::queried;
+ }
+}
+
+std::vector<traversal_algorithm::result>::iterator traversal_algorithm::last_iterator()
+{
+ return (int)m_results.size() >= m_max_results ?
+ m_results.begin() + m_max_results
+ : m_results.end();
+}
+
+} } // namespace libtorrent::dht
+
diff --git a/library/makeit b/library/makeit
new file mode 100755
index 000000000..3d7f60b8d
--- /dev/null
+++ b/library/makeit
@@ -0,0 +1 @@
+python setup.py build
diff --git a/library/peer_connection.cpp b/library/peer_connection.cpp
new file mode 100755
index 000000000..3be09de21
--- /dev/null
+++ b/library/peer_connection.cpp
@@ -0,0 +1,2050 @@
+/*
+
+Copyright (c) 2003, 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.
+
+*/
+
+#include <vector>
+#include <iostream>
+#include <iomanip>
+#include <limits>
+#include <boost/bind.hpp>
+
+#include "libtorrent/peer_connection.hpp"
+#include "libtorrent/identify_client.hpp"
+#include "libtorrent/entry.hpp"
+#include "libtorrent/bencode.hpp"
+#include "libtorrent/alert_types.hpp"
+#include "libtorrent/invariant_check.hpp"
+#include "libtorrent/io.hpp"
+#include "libtorrent/file.hpp"
+#include "libtorrent/version.hpp"
+#include "libtorrent/aux_/session_impl.hpp"
+
+using namespace boost::posix_time;
+using boost::bind;
+using boost::shared_ptr;
+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;
+ }
+
+ peer_connection::peer_connection(
+ session_impl& ses
+ , boost::weak_ptr<torrent> tor
+ , shared_ptr<stream_socket> s
+ , tcp::endpoint const& remote)
+ :
+#ifndef NDEBUG
+ m_last_choke(boost::posix_time::second_clock::universal_time()
+ - hours(1))
+ ,
+#endif
+ m_ses(ses)
+ , m_max_out_request_queue(m_ses.settings().max_out_request_queue)
+ , m_timeout(m_ses.settings().peer_timeout)
+ , m_last_piece(second_clock::universal_time())
+ , m_packet_size(0)
+ , m_recv_pos(0)
+ , m_current_send_buffer(0)
+ , m_write_pos(0)
+ , m_last_receive(second_clock::universal_time())
+ , m_last_sent(second_clock::universal_time())
+ , m_socket(s)
+ , m_remote(remote)
+ , m_torrent(tor)
+ , m_active(true)
+ , m_peer_interested(false)
+ , m_peer_choked(true)
+ , m_interesting(false)
+ , m_choked(true)
+ , m_failed(false)
+ , m_num_pieces(0)
+ , m_desired_queue_size(2)
+ , m_free_upload(0)
+ , m_trust_points(0)
+ , m_assume_fifo(false)
+ , m_num_invalid_requests(0)
+ , m_disconnecting(false)
+ , m_became_uninterested(second_clock::universal_time())
+ , m_became_uninteresting(second_clock::universal_time())
+ , m_connecting(true)
+ , m_queued(true)
+ , m_writing(false)
+ , m_last_write_size(0)
+ , m_reading(false)
+ , m_last_read_size(0)
+ , m_refs(0)
+#ifndef NDEBUG
+ , m_in_constructor(true)
+#endif
+ {
+#ifdef TORRENT_VERBOSE_LOGGING
+ m_logger = m_ses.create_log(m_remote.address().to_string() + "_"
+ + boost::lexical_cast<std::string>(m_remote.port()));
+ (*m_logger) << "*** OUTGOING CONNECTION\n";
+#endif
+
+ boost::shared_ptr<torrent> t = m_torrent.lock();
+ assert(t);
+ // these numbers are used the first second of connection.
+ // then the given upload limits will be applied by running
+ // allocate_resources().
+ m_ul_bandwidth_quota.min = 10;
+ m_ul_bandwidth_quota.max = resource_request::inf;
+
+ if (t->m_ul_bandwidth_quota.given == resource_request::inf)
+ {
+ m_ul_bandwidth_quota.given = resource_request::inf;
+ }
+ else
+ {
+ // just enough to get started with the handshake and bitmask
+ m_ul_bandwidth_quota.given = 400;
+ }
+
+ m_dl_bandwidth_quota.min = 10;
+ m_dl_bandwidth_quota.max = resource_request::inf;
+
+ if (t->m_dl_bandwidth_quota.given == resource_request::inf)
+ {
+ m_dl_bandwidth_quota.given = resource_request::inf;
+ }
+ else
+ {
+ // just enough to get started with the handshake and bitmask
+ m_dl_bandwidth_quota.given = 400;
+ }
+
+ std::fill(m_peer_id.begin(), m_peer_id.end(), 0);
+
+ if (t->ready_for_connections())
+ init();
+ }
+
+ peer_connection::peer_connection(
+ session_impl& ses
+ , boost::shared_ptr<stream_socket> s)
+ :
+#ifndef NDEBUG
+ m_last_choke(boost::posix_time::second_clock::universal_time()
+ - hours(1))
+ ,
+#endif
+ m_ses(ses)
+ , m_max_out_request_queue(m_ses.settings().max_out_request_queue)
+ , m_timeout(m_ses.settings().peer_timeout)
+ , m_last_piece(second_clock::universal_time())
+ , m_packet_size(0)
+ , m_recv_pos(0)
+ , m_current_send_buffer(0)
+ , m_write_pos(0)
+ , m_last_receive(second_clock::universal_time())
+ , m_last_sent(second_clock::universal_time())
+ , m_socket(s)
+ , m_active(false)
+ , m_peer_interested(false)
+ , m_peer_choked(true)
+ , m_interesting(false)
+ , m_choked(true)
+ , m_failed(false)
+ , m_num_pieces(0)
+ , m_desired_queue_size(2)
+ , m_free_upload(0)
+ , m_trust_points(0)
+ , m_assume_fifo(false)
+ , m_num_invalid_requests(0)
+ , m_disconnecting(false)
+ , m_became_uninterested(second_clock::universal_time())
+ , m_became_uninteresting(second_clock::universal_time())
+ , m_connecting(false)
+ , m_queued(false)
+ , m_writing(false)
+ , m_last_write_size(0)
+ , m_reading(false)
+ , m_last_read_size(0)
+ , m_refs(0)
+#ifndef NDEBUG
+ , m_in_constructor(true)
+#endif
+ {
+ m_remote = m_socket->remote_endpoint();
+
+#ifdef TORRENT_VERBOSE_LOGGING
+ assert(m_socket->remote_endpoint() == remote());
+ m_logger = m_ses.create_log(remote().address().to_string() + "_"
+ + boost::lexical_cast<std::string>(remote().port()));
+ (*m_logger) << "*** INCOMING CONNECTION\n";
+#endif
+
+
+ // upload bandwidth will only be given to connections
+ // that are part of a torrent. Since this is an incoming
+ // connection, we have to give it some initial bandwidth
+ // to send the handshake.
+ // after one second, allocate_resources() will be called
+ // and the correct bandwidth limits will be set on all
+ // connections.
+
+ m_ul_bandwidth_quota.min = 10;
+ m_ul_bandwidth_quota.max = resource_request::inf;
+
+ if (m_ses.m_upload_rate == -1)
+ {
+ m_ul_bandwidth_quota.given = resource_request::inf;
+ }
+ else
+ {
+ // just enough to get started with the handshake and bitmask
+ m_ul_bandwidth_quota.given = 400;
+ }
+
+ m_dl_bandwidth_quota.min = 10;
+ m_dl_bandwidth_quota.max = resource_request::inf;
+
+ if (m_ses.m_download_rate == -1)
+ {
+ m_dl_bandwidth_quota.given = resource_request::inf;
+ }
+ else
+ {
+ // just enough to get started with the handshake and bitmask
+ m_dl_bandwidth_quota.given = 400;
+ }
+
+ std::fill(m_peer_id.begin(), m_peer_id.end(), 0);
+ }
+
+ void peer_connection::init()
+ {
+ INVARIANT_CHECK;
+
+ boost::shared_ptr<torrent> t = m_torrent.lock();
+ assert(t);
+ assert(t->valid_metadata());
+ assert(t->ready_for_connections());
+
+ m_have_piece.resize(t->torrent_file().num_pieces(), false);
+
+ // now that we have a piece_picker,
+ // update it with this peers pieces
+
+ // build a vector of all pieces
+ m_num_pieces = 0;
+ std::vector<int> piece_list;
+ for (int i = 0; i < (int)m_have_piece.size(); ++i)
+ {
+ if (m_have_piece[i])
+ {
+ ++m_num_pieces;
+ piece_list.push_back(i);
+ }
+ }
+
+ // let the torrent know which pieces the
+ // peer has, in a shuffled order
+ bool interesting = false;
+ for (std::vector<int>::reverse_iterator i = piece_list.rbegin();
+ i != piece_list.rend(); ++i)
+ {
+ int index = *i;
+ t->peer_has(index);
+ if (!t->have_piece(index)
+ && !t->picker().is_filtered(index))
+ interesting = true;
+ }
+
+ if (piece_list.size() == m_have_piece.size())
+ {
+#ifdef TORRENT_VERBOSE_LOGGING
+ (*m_logger) << " *** THIS IS A SEED ***\n";
+#endif
+ // if we're a seed too, disconnect
+ if (t->is_seed())
+ {
+#ifdef TORRENT_VERBOSE_LOGGING
+ (*m_logger) << " we're also a seed, disconnecting\n";
+#endif
+ throw std::runtime_error("seed to seed connection redundant, disconnecting");
+ }
+ }
+
+ if (interesting)
+ t->get_policy().peer_is_interesting(*this);
+ }
+
+ peer_connection::~peer_connection()
+ {
+// INVARIANT_CHECK;
+ assert(m_disconnecting);
+
+#ifdef TORRENT_VERBOSE_LOGGING
+ using namespace boost::posix_time;
+ if (m_logger)
+ {
+ (*m_logger) << to_simple_string(second_clock::universal_time())
+ << " *** CONNECTION CLOSED\n";
+ }
+#endif
+#ifndef NDEBUG
+ boost::shared_ptr<torrent> t = m_torrent.lock();
+ if (t) assert(t->connection_for(remote()) != this);
+#endif
+ }
+
+ void peer_connection::announce_piece(int index)
+ {
+ // optimization, don't send have messages
+ // to peers that already have the piece
+ if (has_piece(index)) return;
+ write_have(index);
+ }
+
+ bool peer_connection::has_piece(int i) const
+ {
+ INVARIANT_CHECK;
+
+ boost::shared_ptr<torrent> t = m_torrent.lock();
+ assert(t);
+ assert(t->valid_metadata());
+ assert(i >= 0);
+ assert(i < t->torrent_file().num_pieces());
+ return m_have_piece[i];
+ }
+
+ std::deque<piece_block> const& peer_connection::request_queue() const
+ {
+ return m_request_queue;
+ }
+
+ std::deque<piece_block> const& peer_connection::download_queue() const
+ {
+ return m_download_queue;
+ }
+
+ std::deque<peer_request> const& peer_connection::upload_queue() const
+ {
+ return m_requests;
+ }
+
+ void peer_connection::add_stat(size_type downloaded, size_type uploaded)
+ {
+ INVARIANT_CHECK;
+
+ m_statistics.add_stat(downloaded, uploaded);
+ }
+
+ std::vector<bool> const& peer_connection::get_bitfield() const
+ {
+ return m_have_piece;
+ }
+
+ void peer_connection::received_valid_data()
+ {
+ INVARIANT_CHECK;
+
+ m_trust_points++;
+ // TODO: make this limit user settable
+ if (m_trust_points > 20) m_trust_points = 20;
+ }
+
+ void peer_connection::received_invalid_data()
+ {
+ INVARIANT_CHECK;
+
+ // we decrease more than we increase, to keep the
+ // allowed failed/passed ratio low.
+ // TODO: make this limit user settable
+ m_trust_points -= 2;
+ if (m_trust_points < -7) m_trust_points = -7;
+ }
+
+ int peer_connection::trust_points() const
+ {
+ return m_trust_points;
+ }
+
+ size_type peer_connection::total_free_upload() const
+ {
+ return m_free_upload;
+ }
+
+ void peer_connection::add_free_upload(size_type free_upload)
+ {
+ INVARIANT_CHECK;
+
+ m_free_upload += free_upload;
+ }
+
+ void peer_connection::reset_upload_quota()
+ {
+ m_ul_bandwidth_quota.used = 0;
+ m_dl_bandwidth_quota.used = 0;
+ assert(m_ul_bandwidth_quota.left() >= 0);
+ assert(m_dl_bandwidth_quota.left() >= 0);
+ setup_send();
+ setup_receive();
+ }
+
+ // verifies a piece to see if it is valid (is within a valid range)
+ // and if it can correspond to a request generated by libtorrent.
+ bool peer_connection::verify_piece(const peer_request& p) const
+ {
+ INVARIANT_CHECK;
+
+ boost::shared_ptr<torrent> t = m_torrent.lock();
+ assert(t);
+
+ assert(t->valid_metadata());
+
+ return p.piece >= 0
+ && p.piece < t->torrent_file().num_pieces()
+ && p.length > 0
+ && p.start >= 0
+ && (p.length == t->block_size()
+ || (p.length < t->block_size()
+ && p.piece == t->torrent_file().num_pieces()-1
+ && p.start + p.length == t->torrent_file().piece_size(p.piece)))
+ && p.start + p.length <= t->torrent_file().piece_size(p.piece)
+ && p.start % t->block_size() == 0;
+ }
+
+ struct disconnect_torrent
+ {
+ disconnect_torrent(boost::weak_ptr<torrent>& t): m_t(&t) {}
+ ~disconnect_torrent() { if (m_t) m_t->reset(); }
+ void cancel() { m_t = 0; }
+ private:
+ boost::weak_ptr<torrent>* m_t;
+ };
+
+ void peer_connection::attach_to_torrent(sha1_hash const& ih)
+ {
+ INVARIANT_CHECK;
+
+ assert(!m_disconnecting);
+ m_torrent = m_ses.find_torrent(ih);
+
+ boost::shared_ptr<torrent> t = m_torrent.lock();
+
+ if (t && t->is_aborted())
+ {
+ m_torrent.reset();
+ t.reset();
+ }
+
+ if (!t)
+ {
+ // we couldn't find the torrent!
+#ifdef TORRENT_VERBOSE_LOGGING
+ (*m_logger) << " couldn't find a torrent with the given info_hash\n";
+#endif
+ throw std::runtime_error("got info-hash that is not in our session");
+ }
+
+ disconnect_torrent disconnect(m_torrent);
+ if (t->is_paused())
+ {
+ // paused torrents will not accept
+ // incoming connections
+#ifdef TORRENT_VERBOSE_LOGGING
+ (*m_logger) << " rejected connection to paused torrent\n";
+#endif
+ throw std::runtime_error("connection rejected by paused torrent");
+ }
+
+ // check to make sure we don't have another connection with the same
+ // info_hash and peer_id. If we do. close this connection.
+ t->attach_peer(this);
+
+ // if the torrent isn't ready to accept
+ // connections yet, we'll have to wait with
+ // our initialization
+ if (t->ready_for_connections()) init();
+
+ // assume the other end has no pieces
+ // if we don't have valid metadata yet,
+ // leave the vector unallocated
+ assert(m_num_pieces == 0);
+ std::fill(m_have_piece.begin(), m_have_piece.end(), false);
+ disconnect.cancel();
+ }
+
+ // message handlers
+
+ // -----------------------------
+ // --------- KEEPALIVE ---------
+ // -----------------------------
+
+ void peer_connection::incoming_keepalive()
+ {
+ INVARIANT_CHECK;
+
+#ifdef TORRENT_VERBOSE_LOGGING
+ using namespace boost::posix_time;
+ (*m_logger) << to_simple_string(second_clock::universal_time())
+ << " <== KEEPALIVE\n";
+#endif
+ }
+
+ // -----------------------------
+ // ----------- CHOKE -----------
+ // -----------------------------
+
+ void peer_connection::incoming_choke()
+ {
+ INVARIANT_CHECK;
+
+ boost::shared_ptr<torrent> t = m_torrent.lock();
+ assert(t);
+
+#ifdef TORRENT_VERBOSE_LOGGING
+ using namespace boost::posix_time;
+ (*m_logger) << to_simple_string(second_clock::universal_time())
+ << " <== CHOKE\n";
+#endif
+ m_peer_choked = true;
+ t->get_policy().choked(*this);
+
+ // remove all pieces from this peers download queue and
+ // remove the 'downloading' flag from piece_picker.
+ for (std::deque<piece_block>::iterator i = m_download_queue.begin();
+ i != m_download_queue.end(); ++i)
+ {
+ t->picker().abort_download(*i);
+ }
+ for (std::deque<piece_block>::const_iterator i = m_request_queue.begin()
+ , end(m_request_queue.end()); i != end; ++i)
+ {
+ // since this piece was skipped, clear it and allow it to
+ // be requested from other peers
+ t->picker().abort_download(*i);
+ }
+ m_download_queue.clear();
+ m_request_queue.clear();
+
+#ifndef NDEBUG
+// t->picker().integrity_check(m_torrent);
+#endif
+ }
+
+ // -----------------------------
+ // ---------- UNCHOKE ----------
+ // -----------------------------
+
+ void peer_connection::incoming_unchoke()
+ {
+ INVARIANT_CHECK;
+
+ boost::shared_ptr<torrent> t = m_torrent.lock();
+ assert(t);
+
+#ifdef TORRENT_VERBOSE_LOGGING
+ using namespace boost::posix_time;
+ (*m_logger) << to_simple_string(second_clock::universal_time())
+ << " <== UNCHOKE\n";
+#endif
+ m_peer_choked = false;
+ t->get_policy().unchoked(*this);
+ }
+
+ // -----------------------------
+ // -------- INTERESTED ---------
+ // -----------------------------
+
+ void peer_connection::incoming_interested()
+ {
+ INVARIANT_CHECK;
+
+ boost::shared_ptr<torrent> t = m_torrent.lock();
+ assert(t);
+
+#ifdef TORRENT_VERBOSE_LOGGING
+ using namespace boost::posix_time;
+ (*m_logger) << to_simple_string(second_clock::universal_time())
+ << " <== INTERESTED\n";
+#endif
+ m_peer_interested = true;
+ t->get_policy().interested(*this);
+ }
+
+ // -----------------------------
+ // ------ NOT INTERESTED -------
+ // -----------------------------
+
+ void peer_connection::incoming_not_interested()
+ {
+ INVARIANT_CHECK;
+
+ m_became_uninterested = second_clock::universal_time();
+
+ // clear the request queue if the client isn't interested
+ m_requests.clear();
+ setup_send();
+
+#ifdef TORRENT_VERBOSE_LOGGING
+ using namespace boost::posix_time;
+ (*m_logger) << to_simple_string(second_clock::universal_time())
+ << " <== NOT_INTERESTED\n";
+#endif
+
+ boost::shared_ptr<torrent> t = m_torrent.lock();
+ assert(t);
+
+ m_peer_interested = false;
+ t->get_policy().not_interested(*this);
+ }
+
+ // -----------------------------
+ // ----------- HAVE ------------
+ // -----------------------------
+
+ void peer_connection::incoming_have(int index)
+ {
+ INVARIANT_CHECK;
+
+ boost::shared_ptr<torrent> t = m_torrent.lock();
+ assert(t);
+
+#ifdef TORRENT_VERBOSE_LOGGING
+ using namespace boost::posix_time;
+ (*m_logger) << to_simple_string(second_clock::universal_time())
+ << " <== HAVE [ piece: " << index << "]\n";
+#endif
+
+ // if we got an invalid message, abort
+ if (index >= (int)m_have_piece.size() || index < 0)
+ throw protocol_error("got 'have'-message with higher index "
+ "than the number of pieces");
+
+ if (m_have_piece[index])
+ {
+#ifdef TORRENT_VERBOSE_LOGGING
+ (*m_logger) << " got redundant HAVE message for index: " << index << "\n";
+#endif
+ }
+ else
+ {
+ m_have_piece[index] = true;
+
+ // only update the piece_picker if
+ // we have the metadata
+ if (t->valid_metadata())
+ {
+ ++m_num_pieces;
+ t->peer_has(index);
+
+ if (!t->have_piece(index)
+ && !is_interesting()
+ && !t->picker().is_filtered(index))
+ t->get_policy().peer_is_interesting(*this);
+ }
+
+ if (t->is_seed() && is_seed())
+ {
+ throw protocol_error("seed to seed connection redundant, disconnecting");
+ }
+ }
+ }
+
+ // -----------------------------
+ // --------- BITFIELD ----------
+ // -----------------------------
+
+ void peer_connection::incoming_bitfield(std::vector<bool> const& bitfield)
+ {
+ INVARIANT_CHECK;
+
+ boost::shared_ptr<torrent> t = m_torrent.lock();
+ assert(t);
+
+#ifdef TORRENT_VERBOSE_LOGGING
+ using namespace boost::posix_time;
+ (*m_logger) << to_simple_string(second_clock::universal_time())
+ << " <== BITFIELD\n";
+#endif
+
+ // if we don't have the metedata, we cannot
+ // verify the bitfield size
+ if (t->valid_metadata()
+ && (bitfield.size() / 8) != (m_have_piece.size() / 8))
+ throw protocol_error("got bitfield with invalid size");
+
+ // if we don't have metadata yet
+ // just remember the bitmask
+ // don't update the piecepicker
+ // (since it doesn't exist yet)
+ if (!t->valid_metadata())
+ {
+ m_have_piece = bitfield;
+ m_num_pieces = std::count(bitfield.begin(), bitfield.end(), true);
+ return;
+ }
+
+ // build a vector of all pieces
+ std::vector<int> piece_list;
+ for (int i = 0; i < (int)m_have_piece.size(); ++i)
+ {
+ bool have = bitfield[i];
+ if (have && !m_have_piece[i])
+ {
+ m_have_piece[i] = true;
+ ++m_num_pieces;
+ piece_list.push_back(i);
+ }
+ else if (!have && m_have_piece[i])
+ {
+ // this should probably not be allowed
+ m_have_piece[i] = false;
+ --m_num_pieces;
+ t->peer_lost(i);
+ }
+ }
+
+ // let the torrent know which pieces the
+ // peer has, in a shuffled order
+ bool interesting = false;
+ for (std::vector<int>::reverse_iterator i = piece_list.rbegin();
+ i != piece_list.rend(); ++i)
+ {
+ int index = *i;
+ t->peer_has(index);
+ if (!t->have_piece(index)
+ && !t->picker().is_filtered(index))
+ interesting = true;
+ }
+
+ if (piece_list.size() == m_have_piece.size())
+ {
+#ifdef TORRENT_VERBOSE_LOGGING
+ (*m_logger) << " *** THIS IS A SEED ***\n";
+#endif
+ // if we're a seed too, disconnect
+ if (t->is_seed())
+ {
+ throw protocol_error("seed to seed connection redundant, disconnecting");
+ }
+ }
+
+ if (interesting) t->get_policy().peer_is_interesting(*this);
+ }
+
+ // -----------------------------
+ // ---------- REQUEST ----------
+ // -----------------------------
+
+ void peer_connection::incoming_request(peer_request const& r)
+ {
+ INVARIANT_CHECK;
+
+ boost::shared_ptr<torrent> t = m_torrent.lock();
+ assert(t);
+
+ if (!t->valid_metadata())
+ {
+ // if we don't have valid metadata yet,
+ // we shouldn't get a request
+#ifdef TORRENT_VERBOSE_LOGGING
+ using namespace boost::posix_time;
+ (*m_logger) << to_simple_string(second_clock::universal_time())
+ << " <== UNEXPECTED_REQUEST [ "
+ "piece: " << r.piece << " | "
+ "s: " << r.start << " | "
+ "l: " << r.length << " | "
+ "i: " << m_peer_interested << " | "
+ "t: " << (int)t->torrent_file().piece_size(r.piece) << " | "
+ "n: " << t->torrent_file().num_pieces() << " ]\n";
+#endif
+ return;
+ }
+
+ if (int(m_requests.size()) > m_ses.settings().max_allowed_in_request_queue)
+ {
+ // don't allow clients to abuse our
+ // memory consumption.
+ // ignore requests if the client
+ // is making too many of them.
+#ifdef TORRENT_VERBOSE_LOGGING
+ using namespace boost::posix_time;
+ (*m_logger) << to_simple_string(second_clock::universal_time())
+ << " <== TOO MANY REQUESTS [ "
+ "piece: " << r.piece << " | "
+ "s: " << r.start << " | "
+ "l: " << r.length << " | "
+ "i: " << m_peer_interested << " | "
+ "t: " << (int)t->torrent_file().piece_size(r.piece) << " | "
+ "n: " << t->torrent_file().num_pieces() << " ]\n";
+#endif
+ return;
+ }
+
+ // make sure this request
+ // is legal and that the peer
+ // is not choked
+ if (r.piece >= 0
+ && r.piece < t->torrent_file().num_pieces()
+ && t->have_piece(r.piece)
+ && r.start >= 0
+ && r.start < t->torrent_file().piece_size(r.piece)
+ && r.length > 0
+ && r.length + r.start <= t->torrent_file().piece_size(r.piece)
+ && m_peer_interested)
+ {
+ // if we have choked the client
+ // ignore the request
+ if (m_choked)
+ return;
+
+ m_requests.push_back(r);
+ fill_send_buffer();
+#ifdef TORRENT_VERBOSE_LOGGING
+ using namespace boost::posix_time;
+ (*m_logger) << to_simple_string(second_clock::universal_time())
+ << " <== REQUEST [ piece: " << r.piece << " | s: " << r.start << " | l: " << r.length << " ]\n";
+#endif
+ }
+ else
+ {
+#ifdef TORRENT_VERBOSE_LOGGING
+ using namespace boost::posix_time;
+ (*m_logger) << to_simple_string(second_clock::universal_time())
+ << " <== INVALID_REQUEST [ "
+ "piece: " << r.piece << " | "
+ "s: " << r.start << " | "
+ "l: " << r.length << " | "
+ "i: " << m_peer_interested << " | "
+ "t: " << (int)t->torrent_file().piece_size(r.piece) << " | "
+ "n: " << t->torrent_file().num_pieces() << " ]\n";
+#endif
+
+ ++m_num_invalid_requests;
+
+ if (t->alerts().should_post(alert::debug))
+ {
+ t->alerts().post_alert(invalid_request_alert(
+ r
+ , t->get_handle()
+ , m_remote
+ , m_peer_id
+ , "peer sent an illegal piece request, ignoring"));
+ }
+ }
+ }
+
+ void peer_connection::incoming_piece_fragment()
+ {
+ m_last_piece = second_clock::universal_time();
+ }
+
+ // -----------------------------
+ // ----------- PIECE -----------
+ // -----------------------------
+
+ void peer_connection::incoming_piece(peer_request const& p, char const* data)
+ {
+ INVARIANT_CHECK;
+
+ boost::shared_ptr<torrent> t = m_torrent.lock();
+ assert(t);
+
+#ifdef TORRENT_VERBOSE_LOGGING
+ (*m_logger) << to_simple_string(second_clock::universal_time())
+ << " <== PIECE [ piece: " << p.piece << " | "
+ "b: " << p.start / t->block_size() << " | "
+ "s: " << p.start << " | "
+ "l: " << p.length << " | "
+ "ds: " << statistics().download_rate() << " | "
+ "qs: " << m_desired_queue_size << " ]\n";
+#endif
+
+ if (!verify_piece(p))
+ {
+#ifdef TORRENT_VERBOSE_LOGGING
+ using namespace boost::posix_time;
+ (*m_logger) << to_simple_string(second_clock::universal_time())
+ << " <== INVALID_PIECE [ piece: " << p.piece << " | "
+ "start: " << p.start << " | "
+ "length: " << p.length << " ]\n";
+#endif
+ throw protocol_error("got invalid piece packet");
+ }
+
+ using namespace boost::posix_time;
+
+ piece_picker& picker = t->picker();
+
+ piece_block block_finished(p.piece, p.start / t->block_size());
+ std::deque<piece_block>::iterator b
+ = std::find(
+ m_download_queue.begin()
+ , m_download_queue.end()
+ , block_finished);
+
+ std::deque<piece_block>::iterator i;
+
+ if (b != m_download_queue.end())
+ {
+ if (m_assume_fifo)
+ {
+ for (i = m_download_queue.begin();
+ i != b; ++i)
+ {
+#ifdef TORRENT_VERBOSE_LOGGING
+ (*m_logger) << to_simple_string(second_clock::universal_time())
+ << " *** SKIPPED_PIECE [ piece: " << i->piece_index << " | "
+ "b: " << i->block_index << " ] ***\n";
+#endif
+ // since this piece was skipped, clear it and allow it to
+ // be requested from other peers
+ // TODO: send cancel?
+ picker.abort_download(*i);
+ }
+
+ // remove the request that just finished
+ // from the download queue plus the
+ // skipped blocks.
+ m_download_queue.erase(m_download_queue.begin()
+ , boost::next(b));
+ }
+ else
+ {
+ m_download_queue.erase(b);
+ }
+ send_block_requests();
+ }
+ else
+ {
+ // cancel the block from the
+ // peer that has taken over it.
+ boost::optional<tcp::endpoint> peer
+ = t->picker().get_downloader(block_finished);
+ if (peer)
+ {
+ peer_connection* pc = t->connection_for(*peer);
+ if (pc && pc != this)
+ {
+ pc->cancel_request(block_finished);
+ }
+ }
+ else
+ {
+ if (t->alerts().should_post(alert::debug))
+ {
+ t->alerts().post_alert(
+ peer_error_alert(
+ m_remote
+ , m_peer_id
+ , "got a block that was not requested"));
+ }
+#ifdef TORRENT_VERBOSE_LOGGING
+ (*m_logger) << " *** The block we just got was not in the "
+ "request queue ***\n";
+#endif
+ }
+ }
+
+ // if the block we got is already finished, then ignore it
+ if (picker.is_finished(block_finished))
+ {
+ t->received_redundant_data(p.length);
+ return;
+ }
+
+ t->filesystem().write(data, p.piece, p.start, p.length);
+
+ bool was_seed = t->is_seed();
+ bool was_finished = picker.num_filtered() + t->num_pieces()
+ == t->torrent_file().num_pieces();
+
+ picker.mark_as_finished(block_finished, m_remote);
+
+ t->get_policy().block_finished(*this, block_finished);
+
+ // if the piece failed, this connection may be closed, and
+ // detached from the torrent. In that case m_torrent will
+ // be set to 0. So, we need to temporarily save it in this function
+
+ // did we just finish the piece?
+ if (picker.is_piece_finished(p.piece))
+ {
+ bool verified = t->verify_piece(p.piece);
+ if (verified)
+ {
+ t->announce_piece(p.piece);
+ assert(t->valid_metadata());
+ if (!was_finished
+ && 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)
+ t->finished();
+ }
+ }
+ else
+ {
+ t->piece_failed(p.piece);
+ }
+ t->get_policy().piece_finished(p.piece, verified);
+
+ if (!was_seed && t->is_seed())
+ {
+ assert(verified);
+ t->completed();
+ }
+ }
+ }
+
+ // -----------------------------
+ // ---------- CANCEL -----------
+ // -----------------------------
+
+ void peer_connection::incoming_cancel(peer_request const& r)
+ {
+ INVARIANT_CHECK;
+
+#ifdef TORRENT_VERBOSE_LOGGING
+ using namespace boost::posix_time;
+ (*m_logger) << to_simple_string(second_clock::universal_time())
+ << " <== CANCEL [ piece: " << r.piece << " | s: " << r.start << " | l: " << r.length << " ]\n";
+#endif
+
+ std::deque<peer_request>::iterator i
+ = std::find(m_requests.begin(), m_requests.end(), r);
+
+ if (i != m_requests.end())
+ {
+ m_requests.erase(i);
+ }
+ else
+ {
+#ifdef TORRENT_VERBOSE_LOGGING
+ using namespace boost::posix_time;
+ (*m_logger) << to_simple_string(second_clock::universal_time())
+ << " *** GOT CANCEL NOT IN THE QUEUE\n";
+#endif
+ }
+ }
+
+ // -----------------------------
+ // --------- DHT PORT ----------
+ // -----------------------------
+
+ void peer_connection::incoming_dht_port(int listen_port)
+ {
+ INVARIANT_CHECK;
+
+#ifdef TORRENT_VERBOSE_LOGGING
+ using namespace boost::posix_time;
+ (*m_logger) << to_simple_string(second_clock::universal_time())
+ << " <== DHT_PORT [ p: " << listen_port << " ]\n";
+#endif
+#ifndef TORRENT_DISABLE_DHT
+ m_ses.add_dht_node(udp::endpoint(
+ m_remote.address(), listen_port));
+#endif
+ }
+
+ void peer_connection::add_request(piece_block const& block)
+ {
+ INVARIANT_CHECK;
+
+ boost::shared_ptr<torrent> t = m_torrent.lock();
+ assert(t);
+
+ assert(t->valid_metadata());
+ assert(block.piece_index >= 0);
+ 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));
+
+ t->picker().mark_as_downloading(block, m_remote);
+ m_request_queue.push_back(block);
+ }
+
+ void peer_connection::cancel_request(piece_block const& block)
+ {
+ INVARIANT_CHECK;
+
+ boost::shared_ptr<torrent> t = m_torrent.lock();
+ assert(t);
+
+ assert(t->valid_metadata());
+
+ assert(block.piece_index >= 0);
+ 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));
+
+ t->picker().abort_download(block);
+
+ std::deque<piece_block>::iterator it
+ = std::find(m_download_queue.begin(), m_download_queue.end(), block);
+ if (it == m_download_queue.end())
+ {
+ it = std::find(m_request_queue.begin(), m_request_queue.end(), block);
+ assert(it != m_request_queue.end());
+ if (it == m_request_queue.end()) return;
+ m_request_queue.erase(it);
+ // since we found it in the request queue, it means it hasn't been
+ // sent yet, so we don't have to send a cancel.
+ return;
+ }
+ else
+ {
+ m_download_queue.erase(it);
+ }
+
+ send_block_requests();
+
+ int block_offset = block.block_index * t->block_size();
+ int block_size
+ = std::min((int)t->torrent_file().piece_size(block.piece_index)-block_offset,
+ t->block_size());
+ assert(block_size > 0);
+ assert(block_size <= t->block_size());
+
+ peer_request r;
+ r.piece = block.piece_index;
+ r.start = block_offset;
+ r.length = block_size;
+
+ write_cancel(r);
+
+#ifdef TORRENT_VERBOSE_LOGGING
+ using namespace boost::posix_time;
+ (*m_logger) << to_simple_string(second_clock::universal_time())
+ << " ==> CANCEL [ piece: " << block.piece_index << " | s: "
+ << block_offset << " | l: " << block_size << " | " << block.block_index << " ]\n";
+#endif
+ }
+
+ void peer_connection::send_choke()
+ {
+ INVARIANT_CHECK;
+
+ if (m_choked) return;
+ write_choke();
+ m_choked = true;
+
+#ifdef TORRENT_VERBOSE_LOGGING
+ using namespace boost::posix_time;
+ (*m_logger) << to_simple_string(second_clock::universal_time())
+ << " ==> CHOKE\n";
+#endif
+#ifndef NDEBUG
+ using namespace boost::posix_time;
+ m_last_choke = second_clock::universal_time();
+#endif
+ m_num_invalid_requests = 0;
+ m_requests.clear();
+ }
+
+ void peer_connection::send_unchoke()
+ {
+ INVARIANT_CHECK;
+
+#ifndef NDEBUG
+ // TODO: once the policy lowers the interval for optimistic
+ // unchoke, increase this value that interval
+ // this condition cannot be guaranteed since if peers disconnect
+ // a new one will be unchoked ignoring when it was last choked
+ using namespace boost::posix_time;
+ //assert(second_clock::universal_time() - m_last_choke > seconds(9));
+#endif
+
+ if (!m_choked) return;
+ write_unchoke();
+ m_choked = false;
+
+#ifdef TORRENT_VERBOSE_LOGGING
+ using namespace boost::posix_time;
+ (*m_logger) << to_simple_string(second_clock::universal_time())
+ << " ==> UNCHOKE\n";
+#endif
+ }
+
+ void peer_connection::send_interested()
+ {
+ INVARIANT_CHECK;
+
+ if (m_interesting) return;
+ write_interested();
+ m_interesting = true;
+
+#ifdef TORRENT_VERBOSE_LOGGING
+ using namespace boost::posix_time;
+ (*m_logger) << to_simple_string(second_clock::universal_time())
+ << " ==> INTERESTED\n";
+#endif
+ }
+
+ void peer_connection::send_not_interested()
+ {
+ INVARIANT_CHECK;
+
+ if (!m_interesting) return;
+ write_not_interested();
+ m_interesting = false;
+
+ m_became_uninteresting = second_clock::universal_time();
+
+#ifdef TORRENT_VERBOSE_LOGGING
+ using namespace boost::posix_time;
+ (*m_logger) << to_simple_string(second_clock::universal_time())
+ << " ==> NOT_INTERESTED\n";
+#endif
+ }
+
+ void peer_connection::send_block_requests()
+ {
+ INVARIANT_CHECK;
+
+ boost::shared_ptr<torrent> t = m_torrent.lock();
+ assert(t);
+
+ assert(!has_peer_choked());
+
+ if ((int)m_download_queue.size() >= m_desired_queue_size) return;
+
+ while (!m_request_queue.empty()
+ && (int)m_download_queue.size() < m_desired_queue_size)
+ {
+ piece_block block = m_request_queue.front();
+ m_request_queue.pop_front();
+ m_download_queue.push_back(block);
+
+ int block_offset = block.block_index * t->block_size();
+ int block_size = std::min((int)t->torrent_file().piece_size(
+ block.piece_index) - block_offset, t->block_size());
+ assert(block_size > 0);
+ assert(block_size <= t->block_size());
+
+ peer_request r;
+ r.piece = block.piece_index;
+ r.start = block_offset;
+ r.length = block_size;
+
+ assert(verify_piece(r));
+ write_request(r);
+
+ using namespace boost::posix_time;
+
+#ifdef TORRENT_VERBOSE_LOGGING
+ (*m_logger) << to_simple_string(second_clock::universal_time())
+ << " ==> REQUEST [ "
+ "piece: " << block.piece_index << " | "
+ "b: " << block.block_index << " | "
+ "s: " << block_offset << " | "
+ "l: " << block_size << " | "
+ "ds: " << statistics().download_rate() << " | "
+ "qs: " << m_desired_queue_size << " ]\n";
+#endif
+ }
+ m_last_piece = second_clock::universal_time();
+ }
+
+
+ void close_socket_ignore_error(boost::shared_ptr<stream_socket> s)
+ {
+ s->close(asio::ignore_error());
+ }
+
+ void peer_connection::disconnect()
+ {
+ boost::intrusive_ptr<peer_connection> me(this);
+
+ INVARIANT_CHECK;
+
+ if (m_disconnecting) return;
+ m_disconnecting = true;
+ m_ses.m_selector.post(boost::bind(&close_socket_ignore_error, m_socket));
+
+ boost::shared_ptr<torrent> t = m_torrent.lock();
+
+ if (t)
+ {
+ if (t->valid_metadata())
+ {
+ piece_picker& picker = t->picker();
+
+ while (!m_download_queue.empty())
+ {
+ picker.abort_download(m_download_queue.back());
+ m_download_queue.pop_back();
+ }
+ while (!m_request_queue.empty())
+ {
+ picker.abort_download(m_request_queue.back());
+ m_request_queue.pop_back();
+ }
+ }
+#ifndef NDEBUG
+ else
+ {
+ assert(m_download_queue.empty());
+ assert(m_request_queue.empty());
+ }
+#endif
+ t->remove_peer(this);
+ m_torrent.reset();
+ }
+
+ m_ses.close_connection(me);
+ }
+
+ void peer_connection::set_upload_limit(int limit)
+ {
+ assert(limit >= -1);
+ if (limit == -1) limit = std::numeric_limits<int>::max();
+ if (limit < 10) limit = 10;
+ m_ul_bandwidth_quota.max = limit;
+ assert(m_ul_bandwidth_quota.max >= m_ul_bandwidth_quota.min);
+ }
+
+ void peer_connection::set_download_limit(int limit)
+ {
+ assert(limit >= -1);
+ if (limit == -1) limit = std::numeric_limits<int>::max();
+ if (limit < 10) limit = 10;
+ m_dl_bandwidth_quota.max = limit;
+ assert(m_dl_bandwidth_quota.max >= m_dl_bandwidth_quota.min);
+ }
+
+ size_type peer_connection::share_diff() const
+ {
+ INVARIANT_CHECK;
+
+ boost::shared_ptr<torrent> t = m_torrent.lock();
+ assert(t);
+
+ float ratio = t->ratio();
+
+ // if we have an infinite ratio, just say we have downloaded
+ // much more than we have uploaded. And we'll keep uploading.
+ if (ratio == 0.f)
+ return std::numeric_limits<size_type>::max();
+
+ return m_free_upload
+ + static_cast<size_type>(m_statistics.total_payload_download() * ratio)
+ - m_statistics.total_payload_upload();
+ }
+
+ void peer_connection::cut_receive_buffer(int size, int packet_size)
+ {
+ INVARIANT_CHECK;
+
+ assert((int)m_recv_buffer.size() >= size);
+
+ std::copy(m_recv_buffer.begin() + size, m_recv_buffer.begin() + m_recv_pos, m_recv_buffer.begin());
+
+ assert(m_recv_pos >= size);
+ m_recv_pos -= size;
+
+#ifndef NDEBUG
+ std::fill(m_recv_buffer.begin() + m_recv_pos, m_recv_buffer.end(), 0);
+#endif
+
+ m_packet_size = packet_size;
+ m_recv_buffer.resize(m_packet_size);
+ }
+
+ void peer_connection::second_tick(float tick_interval)
+ {
+ INVARIANT_CHECK;
+
+ ptime now(second_clock::universal_time());
+
+ boost::shared_ptr<torrent> t = m_torrent.lock();
+ assert(t);
+
+ on_tick();
+
+ if (!t->valid_metadata()) return;
+
+ // calculate the desired download queue size
+ const float queue_time = m_ses.settings().request_queue_time;
+ // (if the latency is more than this, the download will stall)
+ // so, the queue size is queue_time * down_rate / 16 kiB
+ // (16 kB is the size of each request)
+ // the minimum number of requests is 2 and the maximum is 48
+ // the block size doesn't have to be 16. So we first query the
+ // torrent for it
+ const int block_size = t->block_size();
+ assert(block_size > 0);
+
+ m_desired_queue_size = static_cast<int>(queue_time
+ * statistics().download_rate() / block_size);
+ if (m_desired_queue_size > m_max_out_request_queue)
+ m_desired_queue_size = m_max_out_request_queue;
+ if (m_desired_queue_size < min_request_queue)
+ m_desired_queue_size = min_request_queue;
+
+ if (!m_download_queue.empty()
+ && now - m_last_piece > seconds(m_ses.settings().piece_timeout))
+ {
+ // this peer isn't sending the pieces we've
+ // requested (this has been observed by BitComet)
+ // in this case we'll clear our download queue and
+ // re-request the blocks.
+#ifdef TORRENT_VERBOSE_LOGGING
+ (*m_logger) << to_simple_string(now)
+ << " *** PIECE_REQUESTS TIMED OUT [ " << (int)m_download_queue.size()
+ << " " << to_simple_string(now - m_last_piece) << "] ***\n";
+#endif
+
+ piece_picker& picker = t->picker();
+ for (std::deque<piece_block>::const_iterator i = m_download_queue.begin()
+ , end(m_download_queue.end()); i != end; ++i)
+ {
+ // since this piece was skipped, clear it and allow it to
+ // be requested from other peers
+ picker.abort_download(*i);
+ }
+ for (std::deque<piece_block>::const_iterator i = m_request_queue.begin()
+ , end(m_request_queue.end()); i != end; ++i)
+ {
+ // since this piece was skipped, clear it and allow it to
+ // be requested from other peers
+ picker.abort_download(*i);
+ }
+
+ m_download_queue.clear();
+ m_request_queue.clear();
+
+ // TODO: If we have a limited number of upload
+ // slots, choke this peer
+
+ m_assume_fifo = true;
+
+ // this will trigger new picking of pieces
+ t->get_policy().unchoked(*this);
+ }
+
+ m_statistics.second_tick(tick_interval);
+ m_ul_bandwidth_quota.used = std::min(
+ (int)ceil(statistics().upload_rate())
+ , m_ul_bandwidth_quota.given);
+
+ // If the client sends more data
+ // we send it data faster, otherwise, slower.
+ // It will also depend on how much data the
+ // client has sent us. This is the mean to
+ // maintain the share ratio given by m_ratio
+ // with all peers.
+
+ if (t->is_seed() || is_choked() || t->ratio() == 0.0f)
+ {
+ // if we have downloaded more than one piece more
+ // than we have uploaded OR if we are a seed
+ // have an unlimited upload rate
+ if(send_buffer_size() > 0
+ || (!m_requests.empty() && !is_choked()))
+ m_ul_bandwidth_quota.max = resource_request::inf;
+ else
+ m_ul_bandwidth_quota.max = m_ul_bandwidth_quota.min;
+ }
+ else
+ {
+ size_type bias = 0x10000+2*t->block_size() + m_free_upload;
+
+ double break_even_time = 15; // seconds.
+ size_type have_uploaded = m_statistics.total_payload_upload();
+ size_type have_downloaded = m_statistics.total_payload_download();
+ double download_speed = m_statistics.download_rate();
+
+ size_type soon_downloaded =
+ have_downloaded + (size_type)(download_speed * break_even_time*1.5);
+
+ if(t->ratio() != 1.f)
+ soon_downloaded = (size_type)(soon_downloaded*(double)t->ratio());
+
+ double upload_speed_limit = (soon_downloaded - have_uploaded
+ + bias) / break_even_time;
+
+ upload_speed_limit = std::min(upload_speed_limit,
+ (double)std::numeric_limits<int>::max());
+
+ m_ul_bandwidth_quota.max
+ = std::max((int)upload_speed_limit, m_ul_bandwidth_quota.min);
+ }
+ if (m_ul_bandwidth_quota.given > m_ul_bandwidth_quota.max)
+ m_ul_bandwidth_quota.given = m_ul_bandwidth_quota.max;
+
+ if (m_ul_bandwidth_quota.used > m_ul_bandwidth_quota.given)
+ m_ul_bandwidth_quota.used = m_ul_bandwidth_quota.given;
+
+ fill_send_buffer();
+/*
+ size_type diff = share_diff();
+
+ enum { block_limit = 2 }; // how many blocks difference is considered unfair
+
+ // if the peer has been choked, send the current piece
+ // as fast as possible
+ if (diff > block_limit*m_torrent->block_size() || m_torrent->is_seed() || is_choked())
+ {
+ // if we have downloaded more than one piece more
+ // than we have uploaded OR if we are a seed
+ // have an unlimited upload rate
+ m_ul_bandwidth_quota.wanted = std::numeric_limits<int>::max();
+ }
+ else
+ {
+ float ratio = m_torrent->ratio();
+ // if we have downloaded too much, response with an
+ // upload rate of 10 kB/s more than we dowlload
+ // if we have uploaded too much, send with a rate of
+ // 10 kB/s less than we receive
+ int bias = 0;
+ if (diff > -block_limit*m_torrent->block_size())
+ {
+ bias = static_cast<int>(m_statistics.download_rate() * ratio) / 2;
+ if (bias < 10*1024) bias = 10*1024;
+ }
+ else
+ {
+ bias = -static_cast<int>(m_statistics.download_rate() * ratio) / 2;
+ }
+ m_ul_bandwidth_quota.wanted = static_cast<int>(m_statistics.download_rate()) + bias;
+
+ // the maximum send_quota given our download rate from this peer
+ if (m_ul_bandwidth_quota.wanted < 256) m_ul_bandwidth_quota.wanted = 256;
+ }
+*/
+ }
+
+ void peer_connection::fill_send_buffer()
+ {
+ INVARIANT_CHECK;
+
+ boost::shared_ptr<torrent> t = m_torrent.lock();
+ if (!t) return;
+
+ // only add new piece-chunks if the send buffer is small enough
+ // otherwise there will be no end to how large it will be!
+ // TODO: the buffer size should probably be dependent on the transfer speed
+ while (!m_requests.empty()
+ && (send_buffer_size() < t->block_size() * 6)
+ && !m_choked)
+ {
+ assert(t->valid_metadata());
+ peer_request& r = m_requests.front();
+
+ assert(r.piece >= 0);
+ assert(r.piece < (int)m_have_piece.size());
+ assert(t->have_piece(r.piece));
+ 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
+ using namespace boost::posix_time;
+ (*m_logger) << to_simple_string(second_clock::universal_time())
+ << " ==> PIECE [ piece: " << r.piece << " | s: " << r.start
+ << " | l: " << r.length << " ]\n";
+#endif
+
+ m_requests.erase(m_requests.begin());
+
+ if (m_requests.empty()
+ && m_num_invalid_requests > 0
+ && is_peer_interested()
+ && !is_seed())
+ {
+ // this will make the peer clear
+ // its download queue and re-request
+ // pieces. Hopefully it will not
+ // send invalid requests then
+ send_choke();
+ send_unchoke();
+ }
+ }
+ }
+
+ void peer_connection::setup_send()
+ {
+ session_impl::mutex_t::scoped_lock l(m_ses.m_mutex);
+
+ INVARIANT_CHECK;
+
+ if (m_writing) return;
+ if (!can_write()) return;
+
+ assert(!m_writing);
+
+ int sending_buffer = (m_current_send_buffer + 1) & 1;
+ if (m_send_buffer[sending_buffer].empty())
+ {
+ // thise means we have to swap buffer, because there's no
+ // previous buffer we're still waiting for.
+ std::swap(m_current_send_buffer, sending_buffer);
+ m_write_pos = 0;
+ }
+
+ // send the actual buffer
+ if (!m_send_buffer[sending_buffer].empty())
+ {
+ int amount_to_send
+ = std::min(m_ul_bandwidth_quota.left()
+ , (int)m_send_buffer[sending_buffer].size() - m_write_pos);
+
+ assert(amount_to_send > 0);
+
+ assert(m_write_pos < (int)m_send_buffer[sending_buffer].size());
+ m_socket->async_write_some(asio::buffer(
+ &m_send_buffer[sending_buffer][m_write_pos], amount_to_send)
+ , bind(&peer_connection::on_send_data, self(), _1, _2));
+
+ m_writing = true;
+ m_last_write_size = amount_to_send;
+ m_ul_bandwidth_quota.used += m_last_write_size;
+ }
+ }
+
+ void peer_connection::setup_receive()
+ {
+ session_impl::mutex_t::scoped_lock l(m_ses.m_mutex);
+
+ INVARIANT_CHECK;
+
+ if (m_reading) return;
+ if (!can_read()) return;
+
+ assert(m_packet_size > 0);
+ int max_receive = std::min(
+ m_dl_bandwidth_quota.left()
+ , m_packet_size - m_recv_pos);
+
+ assert(m_recv_pos >= 0);
+ assert(m_packet_size > 0);
+ assert(m_dl_bandwidth_quota.left() > 0);
+ assert(max_receive > 0);
+
+ assert(can_read());
+ m_socket->async_read_some(asio::buffer(&m_recv_buffer[m_recv_pos]
+ , max_receive), bind(&peer_connection::on_receive_data, self(), _1, _2));
+ m_reading = true;
+ m_last_read_size = max_receive;
+ m_dl_bandwidth_quota.used += max_receive;
+ assert(m_dl_bandwidth_quota.used <= m_dl_bandwidth_quota.given);
+ }
+
+ void peer_connection::reset_recv_buffer(int packet_size)
+ {
+ assert(packet_size > 0);
+ m_recv_pos = 0;
+ m_packet_size = packet_size;
+ if (int(m_recv_buffer.size()) < m_packet_size)
+ m_recv_buffer.resize(m_packet_size);
+ }
+
+ void peer_connection::send_buffer(char const* begin, char const* end)
+ {
+ std::vector<char>& buf = m_send_buffer[m_current_send_buffer];
+ buf.insert(buf.end(), begin, end);
+ setup_send();
+ }
+
+// TODO: change this interface to automatically call setup_send() when the
+// return value is destructed
+ buffer::interval peer_connection::allocate_send_buffer(int size)
+ {
+ std::vector<char>& buf = m_send_buffer[m_current_send_buffer];
+ buf.resize(buf.size() + size);
+ buffer::interval ret(&buf[0] + buf.size() - size, &buf[0] + buf.size());
+ return ret;
+ }
+
+ template<class T>
+ struct set_to_zero
+ {
+ set_to_zero(T& v, bool cond): m_val(v), m_cond(cond) {}
+ void fire() { if (!m_cond) return; m_cond = false; m_val = 0; }
+ ~set_to_zero() { if (m_cond) m_val = 0; }
+ private:
+ T& m_val;
+ bool m_cond;
+ };
+
+
+ // --------------------------
+ // RECEIVE DATA
+ // --------------------------
+
+ // throws exception when the client should be disconnected
+ void peer_connection::on_receive_data(const asio::error& error
+ , std::size_t bytes_transferred) try
+ {
+ session_impl::mutex_t::scoped_lock l(m_ses.m_mutex);
+
+ INVARIANT_CHECK;
+
+ assert(m_reading);
+ assert(m_last_read_size > 0);
+ assert(m_last_read_size >= int(bytes_transferred));
+ m_reading = false;
+ // correct the dl quota usage, if not all of the buffer was actually read
+ m_dl_bandwidth_quota.used -= m_last_read_size - bytes_transferred;
+ m_last_read_size = 0;
+
+ if (error)
+ {
+#ifdef TORRENT_VERBOSE_LOGGING
+ (*m_logger) << "**ERROR**: " << error.what() << "\n";
+#endif
+ on_receive(error, bytes_transferred);
+ throw std::runtime_error(error.what());
+ }
+
+ if (m_disconnecting) return;
+
+ assert(m_packet_size > 0);
+ assert(bytes_transferred > 0);
+
+ m_last_receive = second_clock::universal_time();
+ m_recv_pos += bytes_transferred;
+
+ // this will reset the m_recv_pos to 0 if the
+ // entire packet was received
+ // it is important that this is done before
+ // setup_receive() is called. Therefore, fire() is
+ // called before setup_receive().
+ assert(m_recv_pos <= m_packet_size);
+ set_to_zero<int> reset(m_recv_pos, m_recv_pos == m_packet_size);
+
+ {
+ INVARIANT_CHECK;
+ on_receive(error, bytes_transferred);
+ }
+
+ assert(m_packet_size > 0);
+
+ // do the reset immediately
+ reset.fire();
+
+ setup_receive();
+ }
+ catch (file_error& e)
+ {
+ session_impl::mutex_t::scoped_lock l(m_ses.m_mutex);
+
+ boost::shared_ptr<torrent> t = m_torrent.lock();
+ if (!t)
+ {
+ m_ses.connection_failed(m_socket, remote(), e.what());
+ return;
+ }
+
+ if (t->alerts().should_post(alert::fatal))
+ {
+ t->alerts().post_alert(
+ file_error_alert(t->get_handle()
+ , std::string("torrent paused: ") + e.what()));
+ }
+ t->pause();
+ }
+ catch (std::exception& e)
+ {
+ session_impl::mutex_t::scoped_lock l(m_ses.m_mutex);
+ m_ses.connection_failed(m_socket, remote(), e.what());
+ }
+ catch (...)
+ {
+ // all exceptions should derive from std::exception
+ assert(false);
+ session_impl::mutex_t::scoped_lock l(m_ses.m_mutex);
+ m_ses.connection_failed(m_socket, remote(), "connection failed for unkown reason");
+ }
+
+ bool peer_connection::can_write() const
+ {
+ INVARIANT_CHECK;
+
+ // if we have requests or pending data to be sent or announcements to be made
+ // we want to send data
+ return (!m_send_buffer[m_current_send_buffer].empty()
+ || !m_send_buffer[(m_current_send_buffer + 1) & 1].empty())
+ && m_ul_bandwidth_quota.left() > 0
+ && !m_connecting;
+ }
+
+ bool peer_connection::can_read() const
+ {
+ INVARIANT_CHECK;
+
+ return m_dl_bandwidth_quota.left() > 0 && !m_connecting;
+ }
+
+ void peer_connection::connect()
+ {
+ INVARIANT_CHECK;
+
+#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
+ (*m_ses.m_logger) << "CONNECTING: " << m_remote.address().to_string() << "\n";
+#endif
+
+ boost::shared_ptr<torrent> t = m_torrent.lock();
+ assert(t);
+
+ m_queued = false;
+ assert(m_connecting);
+ m_socket->open(asio::ip::tcp::v4());
+ m_socket->bind(t->get_interface());
+ m_socket->async_connect(m_remote
+ , bind(&peer_connection::on_connection_complete, self(), _1));
+
+ if (t->alerts().should_post(alert::debug))
+ {
+ t->alerts().post_alert(peer_error_alert(
+ m_remote, m_peer_id, "connecting to peer"));
+ }
+ }
+
+ void peer_connection::on_connection_complete(asio::error const& e) try
+ {
+ session_impl::mutex_t::scoped_lock l(m_ses.m_mutex);
+
+ INVARIANT_CHECK;
+
+ if (e)
+ {
+#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
+ (*m_ses.m_logger) << "CONNECTION FAILED: " << m_remote.address().to_string() << "\n";
+#endif
+ m_ses.connection_failed(m_socket, m_remote, e.what());
+ return;
+ }
+
+ if (m_disconnecting) return;
+ m_last_receive = second_clock::universal_time();
+
+ // this means the connection just succeeded
+
+#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
+ (*m_ses.m_logger) << "COMPLETED: " << m_remote.address().to_string() << "\n";
+#endif
+
+ m_connecting = false;
+ m_ses.connection_completed(self());
+ on_connected();
+ setup_send();
+ }
+ catch (std::exception& ex)
+ {
+ session_impl::mutex_t::scoped_lock l(m_ses.m_mutex);
+ m_ses.connection_failed(m_socket, remote(), ex.what());
+ }
+ catch (...)
+ {
+ // all exceptions should derive from std::exception
+ assert(false);
+ session_impl::mutex_t::scoped_lock l(m_ses.m_mutex);
+ m_ses.connection_failed(m_socket, remote(), "connection failed for unkown reason");
+ }
+
+ // --------------------------
+ // SEND DATA
+ // --------------------------
+
+ // throws exception when the client should be disconnected
+ void peer_connection::on_send_data(asio::error const& error
+ , std::size_t bytes_transferred) try
+ {
+ session_impl::mutex_t::scoped_lock l(m_ses.m_mutex);
+
+ INVARIANT_CHECK;
+
+ assert(m_writing);
+ assert(m_last_write_size > 0);
+ m_writing = false;
+ // correct the ul quota usage, if not all of the buffer was sent
+ m_ul_bandwidth_quota.used -= m_last_write_size - bytes_transferred;
+ m_last_write_size = 0;
+ m_write_pos += bytes_transferred;
+
+ if (error)
+ {
+#ifdef TORRENT_VERBOSE_LOGGING
+ (*m_logger) << "**ERROR**: " << error.what() << "\n";
+#endif
+ throw std::runtime_error(error.what());
+ }
+ if (m_disconnecting) return;
+
+ assert(!m_connecting);
+ assert(bytes_transferred > 0);
+
+ int sending_buffer = (m_current_send_buffer + 1) & 1;
+
+ assert(int(m_send_buffer[sending_buffer].size()) >= m_write_pos);
+ if (int(m_send_buffer[sending_buffer].size()) == m_write_pos)
+ {
+ m_send_buffer[sending_buffer].clear();
+ m_write_pos = 0;
+ }
+
+ m_last_sent = second_clock::universal_time();
+
+ on_sent(error, bytes_transferred);
+ fill_send_buffer();
+ setup_send();
+ }
+ catch (std::exception& e)
+ {
+ session_impl::mutex_t::scoped_lock l(m_ses.m_mutex);
+ m_ses.connection_failed(m_socket, remote(), e.what());
+ }
+ catch (...)
+ {
+ // all exceptions should derive from std::exception
+ assert(false);
+ session_impl::mutex_t::scoped_lock l(m_ses.m_mutex);
+ m_ses.connection_failed(m_socket, remote(), "connection failed for unkown reason");
+ }
+
+
+#ifndef NDEBUG
+ void peer_connection::check_invariant() const
+ {
+ boost::shared_ptr<torrent> t = m_torrent.lock();
+ if (!t)
+ {
+ typedef session_impl::torrent_map torrent_map;
+ torrent_map& m = m_ses.m_torrents;
+ for (torrent_map::iterator i = m.begin(), end(m.end()); i != end; ++i)
+ {
+ torrent& t = *i->second;
+ assert(t.connection_for(m_remote) != this);
+ }
+ return;
+ }
+
+ if (!m_in_constructor && t->connection_for(remote()) != this)
+ {
+ assert(false);
+ }
+
+ if (t->valid_metadata())
+ {
+ int piece_count = std::count(m_have_piece.begin()
+ , m_have_piece.end(), true);
+ if (m_num_pieces != piece_count)
+ {
+ assert(false);
+ }
+ }
+
+ assert(m_write_pos <= int(m_send_buffer[
+ (m_current_send_buffer + 1) & 1].size()));
+ }
+#endif
+
+ bool peer_connection::has_timed_out() const
+ {
+ // TODO: the timeout should be called by an event
+ INVARIANT_CHECK;
+
+ using namespace boost::posix_time;
+
+ ptime now(second_clock::universal_time());
+
+ // if the socket is still connecting, don't
+ // consider it timed out. Because Windows XP SP2
+ // may delay connection attempts.
+ if (m_connecting) return false;
+
+ // if the peer hasn't said a thing for a certain
+ // time, it is considered to have timed out
+ time_duration d;
+ d = second_clock::universal_time() - m_last_receive;
+ if (d > seconds(m_timeout)) return true;
+
+ // if the peer hasn't become interested and we haven't
+ // become interested in the peer for 10 minutes, it
+ // has also timed out.
+ time_duration d1;
+ time_duration d2;
+ d1 = now - m_became_uninterested;
+ d2 = now - m_became_uninteresting;
+ // TODO: these timeouts should be user settable
+ if (!m_interesting
+ && !m_peer_interested
+ && d1 > minutes(10)
+ && d2 > minutes(10))
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+
+ void peer_connection::keep_alive()
+ {
+ INVARIANT_CHECK;
+
+ boost::posix_time::time_duration d;
+ d = second_clock::universal_time() - m_last_sent;
+ if (d.total_seconds() < m_timeout / 2) return;
+
+ if (m_connecting) return;
+
+ write_keepalive();
+ }
+
+ bool peer_connection::is_seed() const
+ {
+ INVARIANT_CHECK;
+ // if m_num_pieces == 0, we probably doesn't have the
+ // metadata yet.
+ return m_num_pieces == (int)m_have_piece.size() && m_num_pieces > 0;
+ }
+}
+
diff --git a/library/piece_picker.cpp b/library/piece_picker.cpp
new file mode 100755
index 000000000..61e7df66f
--- /dev/null
+++ b/library/piece_picker.cpp
@@ -0,0 +1,1198 @@
+/*
+
+Copyright (c) 2003, 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.
+
+*/
+
+#include <vector>
+#include <cmath>
+#include <algorithm>
+#include <numeric>
+
+// non-standard header, is_sorted()
+//#include <algo.h>
+
+#include "libtorrent/piece_picker.hpp"
+#include "libtorrent/aux_/session_impl.hpp"
+
+#ifndef NDEBUG
+#include "libtorrent/peer_connection.hpp"
+#include "libtorrent/torrent.hpp"
+#endif
+
+//#define TORRENT_PIECE_PICKER_INVARIANT_CHECK INVARIANT_CHECK
+#define TORRENT_PIECE_PICKER_INVARIANT_CHECK
+
+namespace libtorrent
+{
+
+ piece_picker::piece_picker(int blocks_per_piece, int total_num_blocks)
+ : m_piece_info(2)
+ , m_downloading_piece_info(2)
+ , m_piece_map((total_num_blocks + blocks_per_piece-1) / blocks_per_piece)
+ , m_num_filtered(0)
+ , m_num_have_filtered(0)
+ , m_sequenced_download_threshold(100)
+ {
+ assert(blocks_per_piece > 0);
+ assert(total_num_blocks >= 0);
+
+ // the piece index is stored in 20 bits, which limits the allowed
+ // number of pieces somewhat
+ if (m_piece_map.size() >= piece_pos::we_have_index)
+ throw std::runtime_error("too many pieces in torrent");
+
+ m_blocks_per_piece = blocks_per_piece;
+ m_blocks_in_last_piece = total_num_blocks % blocks_per_piece;
+ if (m_blocks_in_last_piece == 0) m_blocks_in_last_piece = blocks_per_piece;
+
+ assert(m_blocks_per_piece <= max_blocks_per_piece);
+ assert(m_blocks_in_last_piece <= m_blocks_per_piece);
+ assert(m_blocks_in_last_piece <= max_blocks_per_piece);
+
+ // allocate the piece_map to cover all pieces
+ // and make them invalid (as if though we already had every piece)
+ std::fill(m_piece_map.begin(), m_piece_map.end()
+ , piece_pos(0, piece_pos::we_have_index));
+ }
+
+ // 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)
+ {
+ // build a vector of all the pieces we don't have
+ std::vector<int> piece_list;
+ piece_list.reserve(std::count(pieces.begin(), pieces.end(), false));
+
+ for (std::vector<bool>::const_iterator i = pieces.begin();
+ i != pieces.end(); ++i)
+ {
+ if (*i) continue;
+ int index = static_cast<int>(i - pieces.begin());
+ if (m_piece_map[index].filtered)
+ {
+ ++m_num_filtered;
+ --m_num_have_filtered;
+ m_piece_map[index].index = 0;
+ }
+ else
+ {
+ piece_list.push_back(index);
+ }
+ }
+
+ // add the pieces to the piece_picker
+ for (std::vector<int>::reverse_iterator i = piece_list.rbegin();
+ i != piece_list.rend(); ++i)
+ {
+ int index = *i;
+ assert(index >= 0);
+ assert(index < (int)m_piece_map.size());
+ assert(m_piece_map[index].index == piece_pos::we_have_index);
+ assert(m_piece_map[index].peer_count == 0);
+ assert(m_piece_info.size() == 2);
+
+ add(index);
+ assert(m_piece_map[index].index != piece_pos::we_have_index);
+ }
+
+ // if we have fast resume info
+ // use it
+ if (!unfinished.empty())
+ {
+ for (std::vector<downloading_piece>::const_iterator i
+ = unfinished.begin(); i != unfinished.end(); ++i)
+ {
+ tcp::endpoint peer;
+ for (int j = 0; j < m_blocks_per_piece; ++j)
+ {
+ if (i->finished_blocks[j])
+ mark_as_finished(piece_block(i->index, j), peer);
+ }
+ }
+ }
+ }
+
+ void piece_picker::set_sequenced_download_threshold(
+ int sequenced_download_threshold)
+ {
+ TORRENT_PIECE_PICKER_INVARIANT_CHECK;
+
+ if (sequenced_download_threshold == m_sequenced_download_threshold)
+ return;
+
+ int old_limit = m_sequenced_download_threshold;
+ m_sequenced_download_threshold = sequenced_download_threshold;
+
+ for (std::vector<piece_pos>::iterator i = m_piece_map.begin()
+ , end(m_piece_map.end()); i != end; ++i)
+ {
+ if (i->priority(old_limit) != i->priority(m_sequenced_download_threshold))
+ {
+ piece_pos& p = *i;
+ if (p.index == piece_pos::we_have_index) continue;
+ int prev_priority = p.priority(old_limit);
+ move(p.downloading, p.filtered, prev_priority, p.index);
+ }
+ }
+
+ typedef std::vector<int> info_t;
+
+ if (old_limit < sequenced_download_threshold)
+ {
+ // the threshold was incremented, in case
+ // the previous max availability was reached
+ // we need to shuffle that bucket, if not, we
+ // don't have to do anything
+ if (int(m_piece_info.size()) > old_limit)
+ {
+ info_t& in = m_piece_info[old_limit];
+ std::random_shuffle(in.begin(), in.end());
+ int c = 0;
+ for (info_t::iterator i = in.begin()
+ , end(in.end()); i != end; ++i)
+ {
+ m_piece_map[*i].index = c++;
+ assert(m_piece_map[*i].priority(old_limit) == old_limit);
+ }
+ }
+ }
+ else if (int(m_piece_info.size()) > sequenced_download_threshold)
+ {
+ info_t& in = m_piece_info[sequenced_download_threshold];
+ std::sort(in.begin(), in.end());
+ int c = 0;
+ for (info_t::iterator i = in.begin()
+ , end(in.end()); i != end; ++i)
+ {
+ m_piece_map[*i].index = c++;
+ assert(m_piece_map[*i].priority(
+ sequenced_download_threshold) == sequenced_download_threshold);
+ }
+ }
+ }
+
+#ifndef NDEBUG
+
+ void piece_picker::check_invariant(const torrent* t) const
+ {
+ assert(sizeof(piece_pos) == 4);
+
+ if (t != 0)
+ assert((int)m_piece_map.size() == t->torrent_file().num_pieces());
+
+ int num_filtered = 0;
+ int num_have_filtered = 0;
+ for (std::vector<piece_pos>::const_iterator i = m_piece_map.begin();
+ i != m_piece_map.end(); ++i)
+ {
+ int index = static_cast<int>(i - m_piece_map.begin());
+ if (i->filtered)
+ {
+ if (i->index != piece_pos::we_have_index)
+ ++num_filtered;
+ else
+ ++num_have_filtered;
+ }
+ if (t != 0)
+ {
+ int actual_peer_count = 0;
+ for (torrent::const_peer_iterator peer = t->begin();
+ peer != t->end(); ++peer)
+ {
+ if (peer->second->has_piece(index)) actual_peer_count++;
+ }
+
+ assert((int)i->peer_count == actual_peer_count);
+/*
+ int num_downloaders = 0;
+ for (std::vector<peer_connection*>::const_iterator peer = t->begin();
+ peer != t->end();
+ ++peer)
+ {
+ const std::vector<piece_block>& queue = (*peer)->download_queue();
+ if (std::find_if(queue.begin(), queue.end(), has_index(index)) == queue.end()) continue;
+
+ ++num_downloaders;
+ }
+
+ if (i->downloading)
+ {
+ assert(num_downloaders == 1);
+ }
+ else
+ {
+ assert(num_downloaders == 0);
+ }
+*/
+ }
+
+ if (i->index == piece_pos::we_have_index)
+ {
+ assert(t == 0 || t->have_piece(index));
+ assert(i->downloading == 0);
+
+ // make sure there's no entry
+ // with this index. (there shouldn't
+ // be since the piece_map is piece_pos::we_have_index)
+ for (std::vector<std::vector<int> >::const_iterator i = m_piece_info.begin();
+ i != m_piece_info.end(); ++i)
+ {
+ for (std::vector<int>::const_iterator j= i->begin();
+ j != i->end(); ++j)
+ {
+ assert(*j != index);
+ }
+ }
+
+ for (std::vector<std::vector<int> >::const_iterator i = m_downloading_piece_info.begin();
+ i != m_downloading_piece_info.end(); ++i)
+ {
+ for (std::vector<int>::const_iterator j = i->begin();
+ j != i->end(); ++j)
+ {
+ assert(*j != index);
+ }
+ }
+
+ }
+ else if (!i->filtered)
+ {
+ if (t != 0)
+ assert(!t->have_piece(index));
+
+ const std::vector<std::vector<int> >& c_vec = pick_piece_info_vector(i->downloading, i->filtered);
+ assert(i->priority(m_sequenced_download_threshold) < (int)c_vec.size());
+ const std::vector<int>& vec = c_vec[i->priority(m_sequenced_download_threshold)];
+ if (i->index >= vec.size())
+ {
+ assert(false);
+ }
+ assert(vec[i->index] == index);
+ }
+
+ std::vector<downloading_piece>::const_iterator down
+ = std::find_if(m_downloads.begin(),
+ m_downloads.end(),
+ has_index(index));
+ if (i->downloading == 1)
+ {
+ assert(down != m_downloads.end());
+ }
+ else
+ {
+ assert(down == m_downloads.end());
+ }
+ }
+ assert(num_filtered == m_num_filtered);
+ assert(num_have_filtered == m_num_have_filtered);
+ }
+#endif
+
+ float piece_picker::distributed_copies() const
+ {
+ const float num_pieces = static_cast<float>(m_piece_map.size());
+
+ for (int i = 0; i < (int)m_piece_info.size(); ++i)
+ {
+ int p = (int)m_piece_info[i].size();
+ assert(num_pieces == 0 || float(p) / num_pieces <= 1.f);
+ if (p > 0)
+ {
+ float fraction_above_count =
+ 1.f - float(p) / num_pieces;
+ return i + fraction_above_count;
+ }
+ }
+ return 1.f;
+ }
+
+ std::vector<std::vector<int> >& piece_picker::pick_piece_info_vector(
+ bool downloading, bool filtered)
+ {
+ assert(!filtered);
+ return downloading?m_downloading_piece_info:m_piece_info;
+ }
+
+ std::vector<std::vector<int> > const& piece_picker::pick_piece_info_vector(
+ bool downloading, bool filtered) const
+ {
+ assert(!filtered);
+ return downloading?m_downloading_piece_info:m_piece_info;
+ }
+
+ void piece_picker::add(int index)
+ {
+ assert(index >= 0);
+ assert(index < (int)m_piece_map.size());
+ piece_pos& p = m_piece_map[index];
+ assert(!p.filtered);
+
+ std::vector<std::vector<int> >& dst_vec = pick_piece_info_vector(
+ p.downloading, p.filtered);
+
+ int priority = p.priority(m_sequenced_download_threshold);
+ if ((int)dst_vec.size() <= priority)
+ dst_vec.resize(priority + 1);
+
+ assert((int)dst_vec.size() > priority);
+
+ if (p.ordered(m_sequenced_download_threshold))
+ {
+ // the piece should be inserted ordered, not randomly
+ std::vector<int>& v = dst_vec[priority];
+// assert(is_sorted(v.begin(), v.end()/*, std::greater<int>()*/));
+ std::vector<int>::iterator i = std::lower_bound(v.begin(), v.end()
+ , index/*, std::greater<int>()*/);
+ p.index = i - v.begin();
+ v.insert(i, index);
+ i = v.begin() + p.index + 1;
+ for (;i != v.end(); ++i)
+ {
+ ++m_piece_map[*i].index;
+ assert(v[m_piece_map[*i].index] == *i);
+ }
+// assert(is_sorted(v.begin(), v.end()/*, std::greater<int>()*/));
+ }
+ else if (dst_vec[priority].size() < 2)
+ {
+ p.index = dst_vec[priority].size();
+ dst_vec[priority].push_back(index);
+ }
+ else
+ {
+ // find a random position in the destination vector where we will place
+ // this entry.
+ int dst_index = rand() % dst_vec[priority].size();
+
+ // copy the entry at that position to the back
+ m_piece_map[dst_vec[priority][dst_index]].index
+ = dst_vec[priority].size();
+ dst_vec[priority].push_back(dst_vec[priority][dst_index]);
+
+ // and then replace the one at dst_index with the one we're moving.
+ // this procedure is to make sure there's no ordering when pieces
+ // are moved in sequenced order.
+ p.index = dst_index;
+ dst_vec[priority][p.index] = index;
+ }
+ }
+
+ // will update the piece with the given properties (downloading, filtered,
+ // priority, elem_index) to place it at the correct position in the
+ // vectors.
+ void piece_picker::move(bool downloading, bool filtered, int priority
+ , int elem_index)
+ {
+ assert(!filtered);
+ assert(priority >= 0);
+ assert(elem_index >= 0);
+ assert(elem_index != piece_pos::we_have_index);
+ std::vector<std::vector<int> >& src_vec(pick_piece_info_vector(
+ downloading, filtered));
+
+ assert((int)src_vec.size() > priority);
+ assert((int)src_vec[priority].size() > elem_index);
+
+ int index = src_vec[priority][elem_index];
+ // update the piece_map
+ piece_pos& p = m_piece_map[index];
+ int new_priority = p.priority(m_sequenced_download_threshold);
+
+ if (p.downloading == downloading
+ && p.filtered == filtered
+ && new_priority == priority)
+ {
+ assert(p.ordered(m_sequenced_download_threshold));
+ return;
+ }
+
+ std::vector<std::vector<int> >& dst_vec(pick_piece_info_vector(
+ p.downloading, p.filtered));
+
+ assert(&dst_vec != &src_vec || new_priority != priority);
+
+ if ((int)dst_vec.size() <= new_priority)
+ {
+ dst_vec.resize(new_priority + 1);
+ assert((int)dst_vec.size() > new_priority);
+ }
+
+ if (p.ordered(m_sequenced_download_threshold))
+ {
+ // the piece should be inserted ordered, not randomly
+ std::vector<int>& v = dst_vec[new_priority];
+// assert(is_sorted(v.begin(), v.end()/*, std::greater<int>()*/));
+ std::vector<int>::iterator i = std::lower_bound(v.begin(), v.end()
+ , index/*, std::greater<int>()*/);
+ p.index = i - v.begin();
+ v.insert(i, index);
+ i = v.begin() + p.index + 1;
+ for (;i != v.end(); ++i)
+ {
+ ++m_piece_map[*i].index;
+ assert(v[m_piece_map[*i].index] == *i);
+ }
+// assert(is_sorted(v.begin(), v.end()/*, std::greater<int>()*/));
+ }
+ else if (dst_vec[new_priority].size() < 2)
+ {
+ p.index = dst_vec[new_priority].size();
+ dst_vec[new_priority].push_back(index);
+ }
+ else
+ {
+ // find a random position in the destination vector where we will place
+ // this entry.
+ int dst_index = rand() % dst_vec[new_priority].size();
+
+ // copy the entry at that position to the back
+ m_piece_map[dst_vec[new_priority][dst_index]].index
+ = dst_vec[new_priority].size();
+ dst_vec[new_priority].push_back(dst_vec[new_priority][dst_index]);
+
+ // and then replace the one at dst_index with the one we're moving.
+ // this procedure is to make sure there's no ordering when pieces
+ // are moved in sequenced order.
+ p.index = dst_index;
+ dst_vec[new_priority][p.index] = index;
+ }
+ assert(p.index < dst_vec[p.priority(m_sequenced_download_threshold)].size());
+ assert(dst_vec[p.priority(m_sequenced_download_threshold)][p.index] == index);
+
+ if (priority >= m_sequenced_download_threshold)
+ {
+ // remove the element from the source vector and preserve the order
+ std::vector<int>& v = src_vec[priority];
+ v.erase(v.begin() + elem_index);
+ for (std::vector<int>::iterator i = v.begin() + elem_index;
+ i != v.end(); ++i)
+ {
+ --m_piece_map[*i].index;
+ assert(v[m_piece_map[*i].index] == *i);
+ }
+ }
+ else
+ {
+ // this will remove elem from the source vector without
+ // preserving order, but the order is random anyway
+ int replace_index = src_vec[priority][elem_index] = src_vec[priority].back();
+ if (index != replace_index)
+ {
+ // update the entry we moved from the back
+ m_piece_map[replace_index].index = elem_index;
+
+ assert((int)src_vec[priority].size() > elem_index);
+ // this may not necessarily be the case. If we've just updated the threshold and are updating
+ // the piece map
+// assert((int)m_piece_map[replace_index].priority(m_sequenced_download_threshold) == priority);
+ assert((int)m_piece_map[replace_index].index == elem_index);
+ assert(src_vec[priority][elem_index] == replace_index);
+ }
+ else
+ {
+ assert((int)src_vec[priority].size() == elem_index+1);
+ }
+
+ src_vec[priority].pop_back();
+ }
+ }
+
+ void piece_picker::remove(bool downloading, bool filtered, int priority
+ , int elem_index)
+ {
+ assert(!filtered);
+ assert(priority >= 0);
+ assert(elem_index >= 0);
+
+ std::vector<std::vector<int> >& src_vec(pick_piece_info_vector(downloading, filtered));
+
+ assert((int)src_vec.size() > priority);
+ assert((int)src_vec[priority].size() > elem_index);
+
+ int index = src_vec[priority][elem_index];
+
+ if (downloading)
+ {
+ std::vector<downloading_piece>::iterator i
+ = std::find_if(m_downloads.begin(),
+ m_downloads.end(),
+ has_index(index));
+ assert(i != m_downloads.end());
+ m_downloads.erase(i);
+ }
+ piece_pos& p = m_piece_map[index];
+ p.downloading = 0;
+ if (p.ordered(m_sequenced_download_threshold))
+ {
+ std::vector<int>& v = src_vec[priority];
+// assert(is_sorted(v.begin(), v.end()/*, std::greater<int>()*/));
+ std::vector<int>::iterator i = v.begin() + elem_index;
+ v.erase(i);
+ i = v.begin() + elem_index;
+ for (; i != v.end(); ++i)
+ {
+ --m_piece_map[*i].index;
+ assert(v[m_piece_map[*i].index] == *i);
+ }
+// assert(is_sorted(v.begin(), v.end()/*, std::greater<int>()*/));
+ }
+ else
+ {
+ // this will remove elem from the vector without
+ // preserving order
+ index = src_vec[priority][elem_index] = src_vec[priority].back();
+ // update the entry we moved from the back
+ if ((int)src_vec[priority].size() > elem_index+1)
+ m_piece_map[index].index = elem_index;
+ src_vec[priority].pop_back();
+ }
+ }
+
+ void piece_picker::restore_piece(int index)
+ {
+ TORRENT_PIECE_PICKER_INVARIANT_CHECK;
+
+ assert(index >= 0);
+ assert(index < (int)m_piece_map.size());
+
+ assert(m_piece_map[index].downloading == 1);
+
+ std::vector<downloading_piece>::iterator i
+ = std::find_if(m_downloads.begin(),
+ m_downloads.end(),
+ has_index(index));
+ assert(i != m_downloads.end());
+ m_downloads.erase(i);
+
+ m_piece_map[index].downloading = 0;
+ piece_pos& p = m_piece_map[index];
+ if (p.filtered) return;
+ move(true, p.filtered, p.priority(m_sequenced_download_threshold), p.index);
+ }
+
+ void piece_picker::inc_refcount(int i)
+ {
+ TORRENT_PIECE_PICKER_INVARIANT_CHECK;
+ assert(i >= 0);
+ assert(i < (int)m_piece_map.size());
+
+ int index = m_piece_map[i].index;
+ int prev_priority = m_piece_map[i].priority(m_sequenced_download_threshold);
+
+ assert(m_piece_map[i].peer_count < 2048);
+ m_piece_map[i].peer_count++;
+ assert(m_piece_map[i].peer_count != 0);
+
+ piece_pos& p = m_piece_map[i];
+
+ // if we have the piece or if it's filtered
+ // we don't have to move any entries in the piece_info vector
+ if (index == piece_pos::we_have_index || p.filtered
+ || p.priority(m_sequenced_download_threshold) == prev_priority) return;
+
+ move(p.downloading, p.filtered, prev_priority, index);
+
+#ifndef NDEBUG
+// integrity_check();
+#endif
+ return;
+ }
+
+ void piece_picker::dec_refcount(int i)
+ {
+ TORRENT_PIECE_PICKER_INVARIANT_CHECK;
+
+ assert(i >= 0);
+ assert(i < (int)m_piece_map.size());
+
+ int prev_priority = m_piece_map[i].priority(m_sequenced_download_threshold);
+ int index = m_piece_map[i].index;
+ assert(m_piece_map[i].peer_count > 0);
+
+ if (m_piece_map[i].peer_count > 0)
+ m_piece_map[i].peer_count--;
+
+ piece_pos& p = m_piece_map[i];
+
+ if (index == piece_pos::we_have_index || p.filtered
+ || p.priority(m_sequenced_download_threshold) == prev_priority) return;
+
+ move(p.downloading, p.filtered, prev_priority, index);
+ }
+
+ // this is used to indicate that we succesfully have
+ // downloaded a piece, and that no further attempts
+ // to pick that piece should be made. The piece will
+ // be removed from the available piece list.
+ void piece_picker::we_have(int index)
+ {
+ TORRENT_PIECE_PICKER_INVARIANT_CHECK;
+ assert(index >= 0);
+ assert(index < (int)m_piece_map.size());
+
+ int info_index = m_piece_map[index].index;
+ int priority = m_piece_map[index].priority(m_sequenced_download_threshold);
+
+ assert(m_piece_map[index].downloading == 1);
+
+ assert(info_index != piece_pos::we_have_index);
+ piece_pos& p = m_piece_map[index];
+ if (p.filtered)
+ {
+ --m_num_filtered;
+ ++m_num_have_filtered;
+ return;
+ }
+ if (info_index == piece_pos::we_have_index) return;
+ remove(p.downloading, p.filtered, priority, info_index);
+ p.index = piece_pos::we_have_index;
+ }
+
+
+ void piece_picker::mark_as_filtered(int index)
+ {
+ TORRENT_PIECE_PICKER_INVARIANT_CHECK;
+ assert(index >= 0);
+ assert(index < (int)m_piece_map.size());
+
+ piece_pos& p = m_piece_map[index];
+ if (p.filtered == 1) return;
+ p.filtered = 1;
+ if (p.index != piece_pos::we_have_index)
+ {
+ ++m_num_filtered;
+ remove(p.downloading, false, p.priority(m_sequenced_download_threshold), p.index);
+ assert(p.filtered == 1);
+ }
+ else
+ {
+ ++m_num_have_filtered;
+ }
+ }
+
+ // this function can be used for pieces that we don't
+ // have, but have marked as filtered (so we didn't
+ // want to download them) but later want to enable for
+ // downloading, then we call this function and it will
+ // be inserted in the available piece list again
+ void piece_picker::mark_as_unfiltered(int index)
+ {
+ TORRENT_PIECE_PICKER_INVARIANT_CHECK;
+ assert(index >= 0);
+ assert(index < (int)m_piece_map.size());
+
+ piece_pos& p = m_piece_map[index];
+ if (p.filtered == 0) return;
+ p.filtered = 0;
+ if (p.index != piece_pos::we_have_index)
+ {
+ --m_num_filtered;
+ assert(m_num_filtered >= 0);
+ add(index);
+ }
+ else
+ {
+ --m_num_have_filtered;
+ assert(m_num_have_filtered >= 0);
+ }
+ }
+
+ bool piece_picker::is_filtered(int index) const
+ {
+ assert(index >= 0);
+ assert(index < (int)m_piece_map.size());
+
+ return m_piece_map[index].filtered == 1;
+ }
+
+ void piece_picker::filtered_pieces(std::vector<bool>& mask) const
+ {
+ mask.resize(m_piece_map.size());
+ std::vector<bool>::iterator j = mask.begin();
+ for (std::vector<piece_pos>::const_iterator i = m_piece_map.begin(),
+ end(m_piece_map.end()); i != end; ++i, ++j)
+ {
+ *j = i->filtered == 1;
+ }
+ }
+
+ void piece_picker::pick_pieces(const std::vector<bool>& pieces
+ , std::vector<piece_block>& interesting_blocks
+ , int num_blocks, bool prefer_whole_pieces
+ , tcp::endpoint peer) const
+ {
+ TORRENT_PIECE_PICKER_INVARIANT_CHECK;
+ assert(num_blocks > 0);
+ assert(pieces.size() == m_piece_map.size());
+
+ // free refers to pieces that are free to download, no one else
+ // is downloading them.
+ // partial is pieces that are partially being downloaded, and
+ // parts of them may be free for download as well, the
+ // partially downloaded pieces will be prioritized
+ assert(m_piece_info.begin() != m_piece_info.end());
+ // +1 is to ignore pieces that no peer has. The bucket with index 0 contains
+ // pieces that 0 other peers has.
+ std::vector<std::vector<int> >::const_iterator free
+ = m_piece_info.begin() + 1;
+ assert(m_downloading_piece_info.begin()
+ != m_downloading_piece_info.end());
+
+ std::vector<std::vector<int> >::const_iterator partial
+ = m_downloading_piece_info.begin() + 1;
+
+ std::vector<piece_block> backup_blocks;
+
+ // this loop will loop from pieces with 1 peer and up
+ // until we either reach the end of the piece list or
+ // has filled the interesting_blocks with num_blocks
+ // blocks.
+
+ // it iterates over two ranges simultaneously. The pieces that are
+ // partially downloaded or partially requested, and the pieces that
+ // hasn't been requested at all. The default is to prioritize pieces
+ // that are partially requested/downloaded, so the loop will first
+ // look for blocks among those pieces. And it will also take two steps
+ // in that range when iterating. This has the effect that partial pieces
+ // doesn't have to be as rare as non-requested pieces in order to be
+ // prefered.
+
+ // When prefer_whole_pieces is set (usually set when downloading from
+ // fast peers) the partial pieces will not be prioritized, but actually
+ // ignored as long as possible.
+
+ while((free != m_piece_info.end())
+ || (partial != m_downloading_piece_info.end()))
+ {
+ if (partial != m_downloading_piece_info.end())
+ {
+ for (int i = 0; i < 2; ++i)
+ {
+ num_blocks = add_interesting_blocks_partial(*partial, pieces
+ , interesting_blocks, backup_blocks, num_blocks
+ , prefer_whole_pieces, peer);
+ assert(num_blocks >= 0);
+ if (num_blocks == 0) return;
+ ++partial;
+ if (partial == m_downloading_piece_info.end()) break;
+ }
+ }
+
+ if (free != m_piece_info.end())
+ {
+ num_blocks = add_interesting_blocks_free(*free, pieces
+ , interesting_blocks, num_blocks, prefer_whole_pieces);
+ assert(num_blocks >= 0);
+ if (num_blocks == 0) return;
+ ++free;
+ }
+ }
+
+ if (!prefer_whole_pieces) return;
+ assert(num_blocks > 0);
+
+#ifdef TORRENT_VERBOSE_LOGGING
+// std::ofstream f("piece_picker.log", std::ios_base::app);
+// f << "backup_blocks: " << backup_blocks.size() << "\n"
+// << "used: " << std::min(num_blocks, (int)backup_blocks.size()) << "\n----\n";
+#endif
+
+ interesting_blocks.insert(interesting_blocks.end()
+ , backup_blocks.begin(), backup_blocks.begin()
+ + (std::min)(num_blocks, (int)backup_blocks.size()));
+ }
+
+ namespace
+ {
+ bool exclusively_requested_from(piece_picker::downloading_piece const& p
+ , int num_blocks_in_piece, tcp::endpoint peer)
+ {
+ for (int j = 0; j < num_blocks_in_piece; ++j)
+ {
+ if ((p.finished_blocks[j] == 1
+ || p.requested_blocks[j] == 1)
+ && p.info[j].peer != peer
+ && p.info[j].peer != tcp::endpoint())
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+ }
+
+ int piece_picker::add_interesting_blocks_free(std::vector<int> const& piece_list
+ , std::vector<bool> const& pieces
+ , std::vector<piece_block>& interesting_blocks
+ , int num_blocks, bool prefer_whole_pieces) const
+ {
+ for (std::vector<int>::const_iterator i = piece_list.begin();
+ i != piece_list.end(); ++i)
+ {
+ assert(*i >= 0);
+ assert(*i < (int)m_piece_map.size());
+ assert(m_piece_map[*i].downloading == 0);
+
+ // if the peer doesn't have the piece
+ // skip it
+ if (!pieces[*i]) continue;
+
+ int piece_blocks = blocks_in_piece(*i);
+ if (!prefer_whole_pieces && piece_blocks > num_blocks)
+ piece_blocks = num_blocks;
+ for (int j = 0; j < piece_blocks; ++j)
+ {
+ interesting_blocks.push_back(piece_block(*i, j));
+ }
+ num_blocks -= (std::min)(piece_blocks, num_blocks);
+ assert(num_blocks >= 0);
+ if (num_blocks == 0) return num_blocks;
+ }
+ return num_blocks;
+ }
+
+ int piece_picker::add_interesting_blocks_partial(std::vector<int> const& piece_list
+ , const std::vector<bool>& pieces
+ , std::vector<piece_block>& interesting_blocks
+ , std::vector<piece_block>& backup_blocks
+ , int num_blocks, bool prefer_whole_pieces
+ , tcp::endpoint peer) const
+ {
+ assert(num_blocks > 0);
+
+ for (std::vector<int>::const_iterator i = piece_list.begin();
+ i != piece_list.end(); ++i)
+ {
+ assert(*i >= 0);
+ assert(*i < (int)m_piece_map.size());
+ // if the peer doesn't have the piece
+ // skip it
+ if (!pieces[*i]) continue;
+
+ assert(m_piece_map[*i].downloading == 1);
+
+ // calculate the number of blocks in this
+ // piece. It's always m_blocks_per_piece, except
+ // in the last piece.
+ int num_blocks_in_piece = blocks_in_piece(*i);
+
+ std::vector<downloading_piece>::const_iterator p
+ = std::find_if(m_downloads.begin(), m_downloads.end(), has_index(*i));
+ assert(p != m_downloads.end());
+
+ // this means that this partial piece has
+ // been downloaded/requested partially from
+ // another peer that isn't us. And since
+ // we prefer whole pieces, add this piece's
+ // blocks to the backup list. If the prioritized
+ // blocks aren't enough, blocks from this list
+ // will be picked.
+ if (prefer_whole_pieces
+ && !exclusively_requested_from(*p, num_blocks_in_piece, peer))
+ {
+ if ((int)backup_blocks.size() >= num_blocks) continue;
+ for (int j = 0; j < num_blocks_in_piece; ++j)
+ {
+ if (p->finished_blocks[j] == 1) continue;
+ if (p->requested_blocks[j] == 1
+ && p->info[j].peer == peer) continue;
+ backup_blocks.push_back(piece_block(*i, j));
+ }
+ continue;
+ }
+
+ for (int j = 0; j < num_blocks_in_piece; ++j)
+ {
+ if (p->finished_blocks[j] == 1) continue;
+ if (p->requested_blocks[j] == 1
+ && p->info[j].peer == peer) continue;
+ // this block is interesting (we don't have it
+ // yet). But it may already have been requested
+ // from another peer. We have to add it anyway
+ // to allow the requester to determine if the
+ // block should be requested from more than one
+ // peer. If it is being downloaded, we continue
+ // to look for blocks until we have num_blocks
+ // blocks that have not been requested from any
+ // other peer.
+ interesting_blocks.push_back(piece_block(*i, j));
+ if (p->requested_blocks[j] == 0)
+ {
+ // we have found a block that's free to download
+ num_blocks--;
+ if (prefer_whole_pieces) continue;
+ assert(num_blocks >= 0);
+ if (num_blocks == 0) return num_blocks;
+ }
+ }
+ assert(num_blocks >= 0 || prefer_whole_pieces);
+ if (num_blocks < 0) num_blocks = 0;
+ if (num_blocks == 0) return num_blocks;
+ }
+ return num_blocks;
+ }
+
+ bool piece_picker::is_piece_finished(int index) const
+ {
+ assert(index < (int)m_piece_map.size());
+ assert(index >= 0);
+
+ if (m_piece_map[index].downloading == 0) return false;
+ std::vector<downloading_piece>::const_iterator i
+ = std::find_if(m_downloads.begin(), m_downloads.end(), has_index(index));
+ assert(i != m_downloads.end());
+ assert((int)i->finished_blocks.count() <= m_blocks_per_piece);
+ int max_blocks = blocks_in_piece(index);
+ if ((int)i->finished_blocks.count() != max_blocks) return false;
+
+ assert((int)i->requested_blocks.count() == max_blocks);
+ return true;
+ }
+
+ bool piece_picker::is_downloading(piece_block block) const
+ {
+ assert(block.piece_index >= 0);
+ assert(block.block_index >= 0);
+ assert(block.piece_index < (int)m_piece_map.size());
+ assert(block.block_index < (int)max_blocks_per_piece);
+
+ 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->requested_blocks[block.block_index];
+ }
+
+ bool piece_picker::is_finished(piece_block block) const
+ {
+ assert(block.piece_index >= 0);
+ assert(block.block_index >= 0);
+ assert(block.piece_index < (int)m_piece_map.size());
+ assert(block.block_index < (int)max_blocks_per_piece);
+
+ 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->finished_blocks[block.block_index];
+ }
+
+
+ void piece_picker::mark_as_downloading(piece_block block, const tcp::endpoint& peer)
+ {
+ TORRENT_PIECE_PICKER_INVARIANT_CHECK;
+
+ 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)
+ {
+ p.downloading = 1;
+ move(false, p.filtered, p.priority(m_sequenced_download_threshold), p.index);
+
+ downloading_piece dp;
+ dp.index = block.piece_index;
+ dp.requested_blocks[block.block_index] = 1;
+ dp.info[block.block_index].peer = peer;
+ m_downloads.push_back(dp);
+ }
+ 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());
+ assert(i->requested_blocks[block.block_index] == 0);
+ i->info[block.block_index].peer = peer;
+ i->requested_blocks[block.block_index] = 1;
+ }
+ }
+
+ void piece_picker::mark_as_finished(piece_block block, const tcp::endpoint& peer)
+ {
+ TORRENT_PIECE_PICKER_INVARIANT_CHECK;
+
+ 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.index == piece_pos::we_have_index || p.filtered) return;
+
+ if (p.downloading == 0)
+ {
+ p.downloading = 1;
+ move(false, p.filtered, p.priority(m_sequenced_download_threshold), p.index);
+
+ downloading_piece dp;
+ dp.index = block.piece_index;
+ dp.requested_blocks[block.block_index] = 1;
+ dp.finished_blocks[block.block_index] = 1;
+ dp.info[block.block_index].peer = peer;
+ m_downloads.push_back(dp);
+ }
+ 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());
+ i->info[block.block_index].peer = peer;
+ i->requested_blocks[block.block_index] = 1;
+ i->finished_blocks[block.block_index] = 1;
+ }
+ }
+/*
+ void piece_picker::mark_as_finished(piece_block block, const peer_id& peer)
+ {
+#ifndef NDEBUG
+ integrity_check();
+#endif
+ assert(block.piece_index >= 0);
+ assert(block.block_index >= 0);
+ assert(block.piece_index < m_piece_map.size());
+ assert(block.block_index < blocks_in_piece(block.piece_index));
+
+ assert(m_piece_map[block.piece_index].downloading == 1);
+
+ 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());
+ i->finished_blocks[block.block_index] = 1;
+ // the block may have been requested, then cancled
+ // and requested by a peer that disconnects
+ // that way we can actually receive the piece
+ // without the requested bit is set.
+ i->requested_blocks[block.block_index] = 1;
+ i->info[block.block_index].num_downloads++;
+ i->info[block.block_index].peer = peer;
+#ifndef NDEBUG
+ integrity_check();
+#endif
+ }
+*/
+ void piece_picker::get_downloaders(std::vector<tcp::endpoint>& d, int index) const
+ {
+ assert(index >= 0 && index <= (int)m_piece_map.size());
+ std::vector<downloading_piece>::const_iterator i
+ = std::find_if(m_downloads.begin(), m_downloads.end(), has_index(index));
+ assert(i != m_downloads.end());
+
+ d.clear();
+ for (int j = 0; j < blocks_in_piece(index); ++j)
+ {
+ d.push_back(i->info[j].peer);
+ }
+ }
+
+ boost::optional<tcp::endpoint> piece_picker::get_downloader(piece_block block) const
+ {
+ std::vector<downloading_piece>::const_iterator i = std::find_if(
+ m_downloads.begin()
+ , m_downloads.end()
+ , has_index(block.piece_index));
+
+ if (i == m_downloads.end())
+ return boost::optional<tcp::endpoint>();
+
+ assert(block.block_index < max_blocks_per_piece);
+ assert(block.block_index >= 0);
+
+ if (i->requested_blocks[block.block_index] == false
+ || i->finished_blocks[block.block_index] == true)
+ return boost::optional<tcp::endpoint>();
+
+ return boost::optional<tcp::endpoint>(i->info[block.block_index].peer);
+ }
+
+ void piece_picker::abort_download(piece_block block)
+ {
+ TORRENT_PIECE_PICKER_INVARIANT_CHECK;
+
+ 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));
+
+ if (m_piece_map[block.piece_index].downloading == 0)
+ {
+ assert(std::find_if(m_downloads.begin(), m_downloads.end(), has_index(block.piece_index)) == m_downloads.end());
+ return;
+ }
+
+ 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());
+
+ if (i->finished_blocks[block.block_index]) return;
+
+ assert(block.block_index < blocks_in_piece(block.piece_index));
+#ifndef NDEBUG
+ if (i->requested_blocks[block.block_index] != 1)
+ {
+ assert(false);
+ }
+#endif
+
+ // clear this block as being downloaded
+ i->requested_blocks[block.block_index] = 0;
+
+ // if there are no other blocks in this pieces
+ // that's being downloaded, remove it from the list
+ if (i->requested_blocks.count() == 0)
+ {
+ m_downloads.erase(i);
+ m_piece_map[block.piece_index].downloading = 0;
+ piece_pos& p = m_piece_map[block.piece_index];
+ move(true, p.filtered, p.priority(m_sequenced_download_threshold), p.index);
+ }
+ }
+
+ int piece_picker::unverified_blocks() const
+ {
+ int counter = 0;
+ for (std::vector<downloading_piece>::const_iterator i = m_downloads.begin();
+ i != m_downloads.end(); ++i)
+ {
+ counter += (int)i->finished_blocks.count();
+ }
+ return counter;
+ }
+
+}
+
diff --git a/library/policy.cpp b/library/policy.cpp
new file mode 100755
index 000000000..bdd90ac77
--- /dev/null
+++ b/library/policy.cpp
@@ -0,0 +1,1419 @@
+/*
+
+Copyright (c) 2003, 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.
+
+*/
+
+#include <iostream>
+
+#include "libtorrent/peer_connection.hpp"
+
+#ifdef _MSC_VER
+#pragma warning(push, 1)
+#endif
+
+#include <boost/date_time/posix_time/posix_time.hpp>
+#include <boost/bind.hpp>
+
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+#include "libtorrent/web_peer_connection.hpp"
+#include "libtorrent/policy.hpp"
+#include "libtorrent/torrent.hpp"
+#include "libtorrent/socket.hpp"
+#include "libtorrent/alert_types.hpp"
+#include "libtorrent/invariant_check.hpp"
+#include "libtorrent/aux_/session_impl.hpp"
+
+namespace libtorrent
+{
+ class peer_connection;
+}
+
+using namespace boost::posix_time;
+using boost::bind;
+
+namespace
+{
+ using namespace libtorrent;
+
+ // the case where ignore_peer is motivated is if two peers
+ // have only one piece that we don't have, and it's the
+ // same piece for both peers. Then they might get into an
+ // infinite loop, fighting to request the same blocks.
+ void request_a_block(
+ torrent& t
+ , peer_connection& c
+ , std::vector<peer_connection*> ignore = std::vector<peer_connection*>())
+ {
+ int num_requests = c.desired_queue_size()
+ - (int)c.download_queue().size()
+ - (int)c.request_queue().size();
+
+ // if our request queue is already full, we
+ // don't have to make any new requests yet
+ if (num_requests <= 0) return;
+
+ piece_picker& p = t.picker();
+ std::vector<piece_block> interesting_pieces;
+ interesting_pieces.reserve(100);
+
+ // picks the interesting pieces from this peer
+ // the integer is the number of pieces that
+ // should be guaranteed to be available for download
+ // (if num_requests is too big, too many pieces are
+ // picked and cpu-time is wasted)
+ // the last argument is if we should prefer whole pieces
+ // for this peer. If we're downloading one piece in 20 seconds
+ // then use this mode.
+ bool prefer_whole_pieces = c.statistics().download_payload_rate()
+ * t.settings().whole_pieces_threshold
+ > t.torrent_file().piece_length();
+
+ // if we prefer whole pieces, the piece picker will pick at least
+ // the number of blocks we want, but it will try to make the picked
+ // blocks be from whole pieces, possibly by returning more blocks
+ // than we requested.
+#ifndef NDEBUG
+ assert(c.remote() == c.get_socket()->remote_endpoint());
+#endif
+ p.pick_pieces(c.get_bitfield(), interesting_pieces
+ , num_requests, prefer_whole_pieces, c.remote());
+
+ // this vector is filled with the interesting pieces
+ // that some other peer is currently downloading
+ // we should then compare this peer's download speed
+ // with the other's, to see if we should abort another
+ // peer_connection in favour of this one
+ std::vector<piece_block> busy_pieces;
+ busy_pieces.reserve(10);
+
+ for (std::vector<piece_block>::iterator i = interesting_pieces.begin();
+ i != interesting_pieces.end(); ++i)
+ {
+ if (p.is_downloading(*i))
+ {
+ busy_pieces.push_back(*i);
+ continue;
+ }
+
+ // ok, we found a piece that's not being downloaded
+ // by somebody else. request it from this peer
+ // and return
+ c.add_request(*i);
+ num_requests--;
+ }
+
+ c.send_block_requests();
+
+ // in this case, we could not find any blocks
+ // that was free. If we couldn't find any busy
+ // blocks as well, we cannot download anything
+ // more from this peer.
+
+ if (busy_pieces.empty()) return;
+
+ // first look for blocks that are just queued
+ // and not actually sent to us yet
+ // (then we can cancel those and request them
+ // from this peer instead)
+
+ while (num_requests > 0)
+ {
+ peer_connection* peer = 0;
+
+ const int initial_queue_size = (int)c.download_queue().size()
+ + (int)c.request_queue().size();
+
+ // This peer's weight will be the minimum, to prevent
+ // cancelling requests from a faster peer.
+ float min_weight = initial_queue_size == 0
+ ? std::numeric_limits<float>::max()
+ : c.statistics().download_payload_rate() / initial_queue_size;
+
+ // find the peer with the lowest download
+ // speed that also has a piece that this
+ // peer could send us
+ for (torrent::peer_iterator i = t.begin();
+ i != t.end(); ++i)
+ {
+ // don't try to take over blocks from ourself
+ if (i->second == &c)
+ continue;
+
+ // ignore all peers in the ignore list
+ if (std::find(ignore.begin(), ignore.end(), i->second) != ignore.end())
+ continue;
+
+ const std::deque<piece_block>& download_queue = i->second->download_queue();
+ const std::deque<piece_block>& request_queue = i->second->request_queue();
+ const int queue_size = (int)i->second->download_queue().size()
+ + (int)i->second->request_queue().size();
+ const float weight = queue_size == 0
+ ? std::numeric_limits<float>::max()
+ : i->second->statistics().download_payload_rate() / queue_size;
+
+ // if the peer's (i) weight is less than the lowest we've found so
+ // far (weight == priority) and it has blocks in its request-
+ // or download queue that we could request from this peer (c),
+ // replace the currently lowest ranking peer.
+ if (weight < min_weight
+ && (std::find_first_of(
+ busy_pieces.begin()
+ , busy_pieces.end()
+ , request_queue.begin()
+ , request_queue.end()) != busy_pieces.end()
+ || std::find_first_of(
+ busy_pieces.begin()
+ , busy_pieces.end()
+ , download_queue.begin()
+ , download_queue.end()) != busy_pieces.end()))
+ {
+ peer = i->second;
+ min_weight = weight;
+ }
+ }
+
+ if (peer == 0)
+ {
+ // we probably couldn't request the block because
+ // we are ignoring some peers
+ break;
+ }
+
+ // find a suitable block to take over from this peer
+
+ std::deque<piece_block>::const_reverse_iterator common_block =
+ std::find_first_of(
+ peer->request_queue().rbegin()
+ , peer->request_queue().rend()
+ , busy_pieces.begin()
+ , busy_pieces.end());
+
+ if (common_block == peer->request_queue().rend())
+ {
+ common_block = std::find_first_of(
+ peer->download_queue().rbegin()
+ , peer->download_queue().rend()
+ , busy_pieces.begin()
+ , busy_pieces.end());
+ assert(common_block != peer->download_queue().rend());
+ }
+
+ piece_block block = *common_block;
+ peer->cancel_request(block);
+ c.add_request(block);
+
+ // the one we interrupted may need to request a new piece.
+ // make sure it doesn't take over a block from the peer
+ // that just took over its block
+ ignore.push_back(&c);
+ request_a_block(t, *peer, ignore);
+ num_requests--;
+
+ const int queue_size = (int)c.download_queue().size()
+ + (int)c.request_queue().size();
+ const float weight = queue_size == 0
+ ? std::numeric_limits<float>::max()
+ : c.statistics().download_payload_rate() / queue_size;
+
+ // this peer doesn't have a faster connection than the
+ // slowest peer. Don't take over any blocks
+ if (weight <= min_weight) break;
+ }
+ c.send_block_requests();
+ }
+
+
+ size_type collect_free_download(
+ torrent::peer_iterator start
+ , torrent::peer_iterator end)
+ {
+ size_type accumulator = 0;
+ for (torrent::peer_iterator i = start; i != end; ++i)
+ {
+ // if the peer is interested in us, it means it may
+ // want to trade it's surplus uploads for downloads itself
+ // (and we should not consider it free). If the share diff is
+ // negative, there's no free download to get from this peer.
+ size_type diff = i->second->share_diff();
+ assert(diff < std::numeric_limits<size_type>::max());
+ if (i->second->is_peer_interested() || diff <= 0)
+ continue;
+
+ assert(diff > 0);
+ i->second->add_free_upload(-diff);
+ accumulator += diff;
+ assert(accumulator > 0);
+ }
+ assert(accumulator >= 0);
+ return accumulator;
+ }
+
+
+ // returns the amount of free upload left after
+ // it has been distributed to the peers
+ size_type distribute_free_upload(
+ torrent::peer_iterator start
+ , torrent::peer_iterator end
+ , size_type free_upload)
+ {
+ if (free_upload <= 0) return free_upload;
+ int num_peers = 0;
+ size_type total_diff = 0;
+ for (torrent::peer_iterator i = start; i != end; ++i)
+ {
+ size_type d = i->second->share_diff();
+ assert(d < std::numeric_limits<size_type>::max());
+ total_diff += d;
+ if (!i->second->is_peer_interested() || i->second->share_diff() >= 0) continue;
+ ++num_peers;
+ }
+
+ if (num_peers == 0) return free_upload;
+ size_type upload_share;
+ if (total_diff >= 0)
+ {
+ upload_share = std::min(free_upload, total_diff) / num_peers;
+ }
+ else
+ {
+ upload_share = (free_upload + total_diff) / num_peers;
+ }
+ if (upload_share < 0) return free_upload;
+
+ for (torrent::peer_iterator i = start; i != end; ++i)
+ {
+ peer_connection* p = i->second;
+ if (!p->is_peer_interested() || p->share_diff() >= 0) continue;
+ p->add_free_upload(upload_share);
+ free_upload -= upload_share;
+ }
+ return free_upload;
+ }
+
+ struct match_peer_ip
+ {
+ match_peer_ip(const tcp::endpoint& ip)
+ : m_ip(ip)
+ {}
+
+ bool operator()(const policy::peer& p) const
+ { return p.ip.address() == m_ip.address(); }
+
+ tcp::endpoint m_ip;
+ };
+
+ struct match_peer_connection
+ {
+ match_peer_connection(const peer_connection& c)
+ : m_conn(c)
+ {}
+
+ bool operator()(const policy::peer& p) const
+ { return p.connection == &m_conn; }
+
+ const peer_connection& m_conn;
+ };
+
+
+}
+
+namespace libtorrent
+{
+ policy::policy(torrent* t)
+ : m_torrent(t)
+// , m_max_uploads(std::numeric_limits<int>::max())
+// , m_max_connections(std::numeric_limits<int>::max())
+ , m_num_unchoked(0)
+ , m_available_free_upload(0)
+ , m_last_optimistic_disconnect(boost::gregorian::date(1970,boost::gregorian::Jan,1))
+ { assert(t); }
+ // finds the peer that has the worst download rate
+ // and returns it. May return 0 if all peers are
+ // choked.
+ policy::peer* policy::find_choke_candidate()
+ {
+ INVARIANT_CHECK;
+
+ peer* worst_peer = 0;
+ size_type min_weight = std::numeric_limits<int>::min();
+
+#ifndef NDEBUG
+ int unchoked_counter = m_num_unchoked;
+#endif
+
+ // TODO: make this selection better
+
+ for (std::vector<peer>::iterator i = m_peers.begin();
+ i != m_peers.end(); ++i)
+ {
+ peer_connection* c = i->connection;
+
+ if (c == 0) continue;
+ if (c->is_choked()) continue;
+#ifndef NDEBUG
+ unchoked_counter--;
+#endif
+ if (c->is_disconnecting()) continue;
+ // if the peer isn't interested, just choke it
+ if (!c->is_peer_interested())
+ return &(*i);
+
+ size_type diff = i->total_download()
+ - i->total_upload();
+
+ size_type weight = static_cast<int>(c->statistics().download_rate() * 10.f)
+ + diff
+ + ((c->is_interesting() && c->has_peer_choked())?-10:10)*1024;
+
+ if (weight >= min_weight && worst_peer) continue;
+
+ min_weight = weight;
+ worst_peer = &(*i);
+ continue;
+ }
+ assert(unchoked_counter == 0);
+ return worst_peer;
+ }
+
+ policy::peer* policy::find_unchoke_candidate()
+ {
+ INVARIANT_CHECK;
+
+ // if all of our peers are unchoked, there's
+ // no left to unchoke
+ if (m_num_unchoked == m_torrent->num_peers())
+ return 0;
+
+ using namespace boost::posix_time;
+ using namespace boost::gregorian;
+
+ peer* unchoke_peer = 0;
+ ptime min_time(date(9999,Jan,1));
+ float max_down_speed = 0.f;
+
+ // TODO: make this selection better
+
+ for (std::vector<peer>::iterator i = m_peers.begin();
+ i != m_peers.end(); ++i)
+ {
+ peer_connection* c = i->connection;
+ if (c == 0) continue;
+ if (c->is_disconnecting()) continue;
+ if (!c->is_choked()) continue;
+ if (!c->is_peer_interested()) continue;
+ if (c->share_diff() < -free_upload_amount
+ && m_torrent->ratio() != 0) continue;
+ if (c->statistics().download_rate() < max_down_speed) continue;
+// if (i->last_optimistically_unchoked > min_time) continue;
+
+ min_time = i->last_optimistically_unchoked;
+ max_down_speed = c->statistics().download_rate();
+ unchoke_peer = &(*i);
+ }
+ return unchoke_peer;
+ }
+
+ policy::peer* policy::find_disconnect_candidate()
+ {
+ peer *disconnect_peer = 0;
+ double slowest_transfer_rate = std::numeric_limits<double>::max();
+
+ boost::posix_time::ptime local_time
+ = second_clock::universal_time();
+
+ for (std::vector<peer>::iterator i = m_peers.begin();
+ i != m_peers.end(); ++i)
+ {
+ peer_connection* c = i->connection;
+ if(c == 0)
+ continue;
+ if(c->is_disconnecting())
+ continue;
+
+ double transferred_amount
+ = (double)c->statistics().total_payload_download();
+
+ boost::posix_time::time_duration connected_time
+ = local_time - i->connected;
+
+ double connected_time_in_seconds
+ = connected_time.seconds()
+ + connected_time.minutes()*60.0
+ + connected_time.hours()*60.0*60.0;
+
+ double transfer_rate
+ = transferred_amount / (connected_time_in_seconds+1);
+
+ if (transfer_rate <= slowest_transfer_rate)
+ {
+ slowest_transfer_rate = transfer_rate;
+ disconnect_peer = &(*i);
+ }
+ }
+ return disconnect_peer;
+ }
+
+ policy::peer *policy::find_connect_candidate()
+ {
+ boost::posix_time::ptime local_time=second_clock::universal_time();
+ boost::posix_time::ptime ptime(local_time);
+ policy::peer* candidate =0;
+
+ for (std::vector<peer>::iterator i = m_peers.begin();
+ i != m_peers.end(); ++i)
+ {
+ if(i->connection) continue;
+ if(i->banned) continue;
+ if(i->type == peer::not_connectable) continue;
+
+ assert(i->connected <= local_time);
+
+ boost::posix_time::ptime next_connect = i->connected;
+
+ if (next_connect <= ptime)
+ {
+ ptime = next_connect;
+ candidate = &(*i);
+ }
+ }
+
+ assert(ptime <= local_time);
+
+ return candidate;
+ }
+
+ policy::peer* policy::find_seed_choke_candidate()
+ {
+ INVARIANT_CHECK;
+
+ assert(m_num_unchoked > 0);
+ // first choice candidate.
+ // it is a candidate we owe nothing to and which has been unchoked
+ // the longest.
+ using namespace boost::posix_time;
+ using namespace boost::gregorian;
+
+ peer* candidate = 0;
+
+ // not valid when candidate == 0
+ ptime last_unchoke = ptime(date(1970, Jan, 1));
+
+ // second choice candidate.
+ // if there is no first choice candidate, this candidate will be chosen.
+ // it is the candidate that we owe the least to.
+ peer* second_candidate = 0;
+ size_type lowest_share_diff = 0; // not valid when secondCandidate==0
+
+ for (std::vector<peer>::iterator i = m_peers.begin();
+ i != m_peers.end(); ++i)
+ {
+ peer_connection* c = i->connection;
+ // ignore peers that are choked or
+ // whose connection is closed
+ if (c == 0) continue;
+
+ if (c->is_choked()) continue;
+ if (c->is_disconnecting()) continue;
+
+ size_type share_diff = c->share_diff();
+
+ // select as second candidate the one that we owe the least
+ // to
+ if (!second_candidate || share_diff <= lowest_share_diff)
+ {
+ lowest_share_diff = share_diff;
+ second_candidate = &(*i);
+ }
+
+ // select as first candidate the one that we don't owe anything to
+ // and has been waiting for an unchoke the longest
+ if (share_diff > 0) continue;
+ if (!candidate || last_unchoke > i->last_optimistically_unchoked)
+ {
+ last_unchoke = i->last_optimistically_unchoked;
+ candidate = &(*i);
+ }
+ }
+ if (candidate) return candidate;
+ if (second_candidate) return second_candidate;
+ assert(false);
+ return 0;
+ }
+
+ policy::peer* policy::find_seed_unchoke_candidate()
+ {
+ INVARIANT_CHECK;
+
+ peer* candidate = 0;
+ boost::posix_time::ptime last_unchoke
+ = second_clock::universal_time();
+
+ for (std::vector<peer>::iterator i = m_peers.begin();
+ i != m_peers.end(); ++i)
+ {
+ peer_connection* c = i->connection;
+ if (c == 0) continue;
+ if (!c->is_choked()) continue;
+ if (!c->is_peer_interested()) continue;
+ if (c->is_disconnecting()) continue;
+ if (last_unchoke < i->last_optimistically_unchoked) continue;
+ last_unchoke = i->last_optimistically_unchoked;
+ candidate = &(*i);
+ }
+ return candidate;
+ }
+
+ bool policy::seed_unchoke_one_peer()
+ {
+ INVARIANT_CHECK;
+
+ peer* p = find_seed_unchoke_candidate();
+ if (p != 0)
+ {
+ assert(p->connection->is_choked());
+ p->connection->send_unchoke();
+ p->last_optimistically_unchoked
+ = second_clock::universal_time();
+ ++m_num_unchoked;
+ }
+ return p != 0;
+ }
+
+ void policy::seed_choke_one_peer()
+ {
+ INVARIANT_CHECK;
+
+ peer* p = find_seed_choke_candidate();
+ if (p != 0)
+ {
+ assert(!p->connection->is_choked());
+ p->connection->send_choke();
+ --m_num_unchoked;
+ }
+ }
+
+ void policy::pulse()
+ {
+ INVARIANT_CHECK;
+
+ if (m_torrent->is_paused()) return;
+
+ using namespace boost::posix_time;
+
+ // TODO: we must also remove peers that
+ // we failed to connect to from this list
+ // to avoid being part of a DDOS-attack
+
+ // remove old disconnected peers from the list
+ m_peers.erase(
+ std::remove_if(m_peers.begin()
+ , m_peers.end()
+ , old_disconnected_peer())
+ , m_peers.end());
+
+ // -------------------------------------
+ // maintain the number of connections
+ // -------------------------------------
+
+ // count the number of connected peers except for peers
+ // that are currently in the process of disconnecting
+ int num_connected_peers = 0;
+
+ for (std::vector<peer>::iterator i = m_peers.begin();
+ i != m_peers.end(); ++i)
+ {
+ if (i->connection && !i->connection->is_disconnecting())
+ ++num_connected_peers;
+ }
+
+ if (m_torrent->m_connections_quota.given != std::numeric_limits<int>::max())
+ {
+
+ int max_connections = m_torrent->m_connections_quota.given;
+
+ if (num_connected_peers >= max_connections)
+ {
+ // every minute, disconnect the worst peer in hope of finding a better peer
+
+ boost::posix_time::ptime local_time = second_clock::universal_time();
+ if (m_last_optimistic_disconnect + boost::posix_time::seconds(120) <= local_time)
+ {
+ m_last_optimistic_disconnect = local_time;
+ --max_connections; // this will have the effect of disconnecting the worst peer
+ }
+ }
+ else
+ {
+ // don't do a disconnect earlier than 1 minute after some peer was connected
+ m_last_optimistic_disconnect = second_clock::universal_time();
+ }
+
+ while (num_connected_peers > max_connections)
+ {
+ bool ret = disconnect_one_peer();
+ (void)ret;
+ assert(ret);
+ --num_connected_peers;
+ }
+ }
+
+ while (m_torrent->num_peers() < m_torrent->m_connections_quota.given)
+ {
+ if (!connect_one_peer())
+ break;
+ }
+
+
+ // ------------------------
+ // upload shift
+ // ------------------------
+
+ // this part will shift downloads
+ // from peers that are seeds and peers
+ // that don't want to download from us
+ // to peers that cannot upload anything
+ // to us. The shifting will make sure
+ // that the torrent's share ratio
+ // will be maintained
+
+ // if the share ratio is 0 (infinite)
+ // m_available_free_upload isn't used
+ // because it isn't necessary
+ if (m_torrent->ratio() != 0.f)
+ {
+ // accumulate all the free download we get
+ // and add it to the available free upload
+ m_available_free_upload
+ += collect_free_download(
+ m_torrent->begin()
+ , m_torrent->end());
+
+ // distribute the free upload among the peers
+ m_available_free_upload = distribute_free_upload(
+ m_torrent->begin()
+ , m_torrent->end()
+ , m_available_free_upload);
+ }
+
+ // ------------------------
+ // seed choking policy
+ // ------------------------
+ if (m_torrent->is_seed())
+ {
+ if (m_num_unchoked > m_torrent->m_uploads_quota.given)
+ {
+ do
+ {
+ peer* p = find_seed_choke_candidate();
+ --m_num_unchoked;
+ assert(p != 0);
+ if (p == 0) break;
+
+ assert(!p->connection->is_choked());
+ p->connection->send_choke();
+ } while (m_num_unchoked > m_torrent->m_uploads_quota.given);
+ }
+ else if (m_num_unchoked > 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());
+ peer* p = find_seed_unchoke_candidate();
+ if (p)
+ {
+ assert(p->connection->is_choked());
+ seed_choke_one_peer();
+ p->connection->send_unchoke();
+ ++m_num_unchoked;
+ }
+
+ }
+
+ // make sure we have enough
+ // unchoked peers
+ while (m_num_unchoked < m_torrent->m_uploads_quota.given)
+ {
+ if (!seed_unchoke_one_peer()) break;
+ }
+#ifndef NDEBUG
+ check_invariant();
+#endif
+ }
+
+ // ----------------------------
+ // downloading choking policy
+ // ----------------------------
+ else
+ {
+ if (m_torrent->ratio() != 0)
+ {
+ // choke peers that have leeched too much without giving anything back
+ for (std::vector<peer>::iterator i = m_peers.begin();
+ i != m_peers.end(); ++i)
+ {
+ peer_connection* c = i->connection;
+ if (c == 0) continue;
+
+ size_type diff = i->connection->share_diff();
+ if (diff < -free_upload_amount
+ && !c->is_choked())
+ {
+ // if we have uploaded more than a piece for free, choke peer and
+ // wait until we catch up with our download.
+ c->send_choke();
+ --m_num_unchoked;
+ }
+ }
+ }
+
+ if (m_torrent->m_uploads_quota.given < m_torrent->num_peers())
+ {
+ assert(m_torrent->m_uploads_quota.given >= 0);
+
+ // make sure we don't have too many
+ // unchoked peers
+ if (m_num_unchoked > m_torrent->m_uploads_quota.given)
+ {
+ do
+ {
+ peer* p = find_choke_candidate();
+ if (!p) break;
+ assert(p);
+ assert(!p->connection->is_choked());
+ p->connection->send_choke();
+ --m_num_unchoked;
+ } while (m_num_unchoked > m_torrent->m_uploads_quota.given);
+ }
+ else
+ {
+ // 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());
+ peer* p = find_unchoke_candidate();
+ if (p)
+ {
+ assert(p->connection->is_choked());
+ choke_one_peer();
+ p->connection->send_unchoke();
+ ++m_num_unchoked;
+ }
+ }
+ }
+
+ // make sure we have enough
+ // unchoked peers
+ while (m_num_unchoked < m_torrent->m_uploads_quota.given
+ && unchoke_one_peer());
+ }
+ }
+
+ void policy::ban_peer(const peer_connection& c)
+ {
+ INVARIANT_CHECK;
+
+ std::vector<peer>::iterator i = std::find_if(
+ m_peers.begin()
+ , m_peers.end()
+ , match_peer_connection(c));
+
+ if (i == m_peers.end())
+ {
+ // this is probably an http seed
+ if (web_peer_connection const* p = dynamic_cast<web_peer_connection const*>(&c))
+ {
+ m_torrent->remove_url_seed(p->url());
+ }
+ return;
+ }
+
+ i->type = peer::not_connectable;
+ i->ip.port(0);
+ i->banned = true;
+ }
+
+ void policy::new_connection(peer_connection& c)
+ {
+ assert(!c.is_local());
+/*
+#ifndef NDEBUG
+ // avoid the invariant check to fail
+ peer p(tcp::endpoint("0.0.0.0", 0), peer::not_connectable);
+ p.connection = &c;
+ m_peers.push_back(p);
+#endif
+*/
+ INVARIANT_CHECK;
+/*
+#ifndef NDEBUG
+ // avoid the invariant check to fail
+ m_peers.erase(m_peers.end() - 1);
+#endif
+*/
+ // if the connection comes from the tracker,
+ // it's probably just a NAT-check. Ignore the
+ // num connections constraint then.
+
+ // TODO: only allow _one_ connection to use this
+ // override at a time
+#ifndef NDEBUG
+ assert(c.remote() == c.get_socket()->remote_endpoint());
+#endif
+ if (m_torrent->num_peers() >= m_torrent->m_connections_quota.given
+ && c.remote().address() != m_torrent->current_tracker().address())
+ {
+ throw protocol_error("too many connections, refusing incoming connection"); // cause a disconnect
+ }
+
+#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
+ if (c.remote().address() == m_torrent->current_tracker().address())
+ {
+ m_torrent->debug_log("overriding connection limit for tracker NAT-check");
+ }
+#endif
+
+ std::vector<peer>::iterator i = std::find_if(
+ m_peers.begin()
+ , m_peers.end()
+ , match_peer_ip(c.remote()));
+
+
+ if (i != m_peers.end())
+ {
+ if (i->banned)
+ throw protocol_error("ip address banned, closing");
+
+ if (i->connection != 0)
+ {
+ // the new connection is a local (outgoing) connection
+ // or the current one is already connected
+ if (!i->connection->is_connecting() || c.is_local())
+ {
+ throw protocol_error("duplicate connection, closing");
+ }
+ else
+ {
+#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
+ m_torrent->debug_log("duplicate connection. existing connection"
+ " is connecting and this connection is incoming. closing existing "
+ "connection in favour of this one");
+#endif
+ i->connection->disconnect();
+ i->connection = 0;
+ }
+ }
+ }
+ else
+ {
+ using namespace boost::posix_time;
+ using namespace boost::gregorian;
+
+ // we don't have ny info about this peer.
+ // add a new entry
+#ifndef NDEBUG
+ assert(c.remote() == c.get_socket()->remote_endpoint());
+#endif
+ peer p(c.remote(), peer::not_connectable);
+ m_peers.push_back(p);
+ i = m_peers.end()-1;
+ }
+
+ assert(i->connection == 0);
+ c.add_stat(i->prev_amount_download, i->prev_amount_upload);
+ i->prev_amount_download = 0;
+ i->prev_amount_upload = 0;
+ i->connection = &c;
+ assert(i->connection);
+ i->connected = second_clock::universal_time();
+ m_last_optimistic_disconnect = second_clock::universal_time();
+ }
+
+ void policy::peer_from_tracker(const tcp::endpoint& remote, const peer_id& pid)
+ {
+ INVARIANT_CHECK;
+
+ // just ignore the obviously invalid entries from the tracker
+ if(remote.address() == address() || remote.port() == 0)
+ return;
+
+ try
+ {
+ std::vector<peer>::iterator i = std::find_if(
+ m_peers.begin()
+ , m_peers.end()
+ , match_peer_ip(remote));
+
+ bool just_added = false;
+
+ if (i == m_peers.end())
+ {
+ using namespace boost::posix_time;
+ using namespace boost::gregorian;
+
+ // we don't have any info about this peer.
+ // add a new entry
+ peer p(remote, peer::connectable);
+ m_peers.push_back(p);
+ // the iterator is invalid
+ // because of the push_back()
+ i = m_peers.end() - 1;
+ just_added = true;
+ }
+ else
+ {
+ i->type = peer::connectable;
+
+ // in case we got the ip from a remote connection, port is
+ // not known, so save it. Client may also have changed port
+ // for some reason.
+ i->ip = remote;
+
+ if (i->connection)
+ {
+ // this means we're already connected
+ // to this peer. don't connect to
+ // it again.
+
+#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
+ m_torrent->debug_log("already connected to peer: " + remote.address().to_string() + ":"
+ + boost::lexical_cast<std::string>(remote.port()));
+#endif
+
+ assert(i->connection->associated_torrent().lock().get() == m_torrent);
+ return;
+ }
+ }
+
+ if (i->banned) return;
+
+ if (m_torrent->num_peers() < m_torrent->m_connections_quota.given
+ && !m_torrent->is_paused())
+ {
+ if (!connect_peer(&*i) && just_added)
+ {
+ // if this peer was just added, and it
+ // failed to connect. Remove it from the list
+ // (to keep it in sync with the session's list)
+ assert(i == m_peers.end() - 1);
+ m_peers.erase(i);
+ }
+ }
+ return;
+ }
+ catch(std::exception& e)
+ {
+ if (m_torrent->alerts().should_post(alert::debug))
+ {
+ m_torrent->alerts().post_alert(
+ peer_error_alert(remote, pid, e.what()));
+ }
+ }
+ }
+
+ // this is called when we are choked by a peer
+ // i.e. a peer lets us know that we will not receive
+ // anything for a while
+ void policy::choked(peer_connection&)
+ {
+ }
+
+ void policy::piece_finished(int index, bool successfully_verified)
+ {
+ INVARIANT_CHECK;
+
+ assert(index >= 0 && index < m_torrent->torrent_file().num_pieces());
+
+ if (successfully_verified)
+ {
+ // have all peers update their interested-flag
+ for (std::vector<peer>::iterator i = m_peers.begin();
+ i != m_peers.end(); ++i)
+ {
+ if (i->connection == 0) continue;
+ // if we're not interested, we will not become interested
+ if (!i->connection->is_interesting()) continue;
+ if (!i->connection->has_piece(index)) continue;
+
+ bool interested = false;
+ const std::vector<bool>& peer_has = i->connection->get_bitfield();
+ const std::vector<bool>& we_have = m_torrent->pieces();
+ assert(we_have.size() == peer_has.size());
+ for (int j = 0; j != (int)we_have.size(); ++j)
+ {
+ if (!we_have[j] && peer_has[j])
+ {
+ interested = true;
+ break;
+ }
+ }
+ if (!interested)
+ i->connection->send_not_interested();
+ assert(i->connection->is_interesting() == interested);
+ }
+ }
+ }
+
+ // 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())
+ 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
+ void policy::unchoked(peer_connection& c)
+ {
+ INVARIANT_CHECK;
+ if (c.is_interesting())
+ {
+ request_a_block(*m_torrent, c);
+ }
+ }
+
+ // called when a peer is interested in us
+ void policy::interested(peer_connection& c)
+ {
+ INVARIANT_CHECK;
+
+ assert(std::find_if(m_peers.begin(), m_peers.end()
+ , boost::bind<bool>(std::equal_to<peer_connection*>(), bind(&peer::connection, _1)
+ , &c)) != m_peers.end());
+
+ // if the peer is choked and we have upload slots left,
+ // then unchoke it. Another condition that has to be met
+ // is that the torrent doesn't keep track of the individual
+ // up/down ratio for each peer (ratio == 0) or (if it does
+ // keep track) this particular connection isn't a leecher.
+ // If the peer was choked because it was leeching, don't
+ // unchoke it again.
+ // The exception to this last condition is if we're a seed.
+ // In that case we don't care if people are leeching, they
+ // can't pay for their downloads anyway.
+ if (c.is_choked()
+ && m_num_unchoked < m_torrent->m_uploads_quota.given
+ && (m_torrent->ratio() == 0
+ || c.share_diff() >= -free_upload_amount
+ || m_torrent->is_seed()))
+ {
+ c.send_unchoke();
+ ++m_num_unchoked;
+ }
+ }
+
+ // called when a peer is no longer interested in us
+ void policy::not_interested(peer_connection& c)
+ {
+ INVARIANT_CHECK;
+
+ if (m_torrent->ratio() != 0.f)
+ {
+ assert(c.share_diff() < std::numeric_limits<size_type>::max());
+ size_type diff = c.share_diff();
+ if (diff > 0 && c.is_seed())
+ {
+ // the peer is a seed and has sent
+ // us more than we have sent it back.
+ // consider the download as free download
+ m_available_free_upload += diff;
+ c.add_free_upload(-diff);
+ }
+ }
+ if (!c.is_choked())
+ {
+ c.send_choke();
+ --m_num_unchoked;
+
+ if (m_torrent->is_seed()) seed_unchoke_one_peer();
+ else unchoke_one_peer();
+ }
+ }
+
+ bool policy::unchoke_one_peer()
+ {
+ peer* p = find_unchoke_candidate();
+ if (p == 0) return false;
+ assert(p->connection);
+ assert(!p->connection->is_disconnecting());
+
+ assert(p->connection->is_choked());
+ p->connection->send_unchoke();
+ p->last_optimistically_unchoked = second_clock::universal_time();
+ ++m_num_unchoked;
+ return true;
+ }
+
+ void policy::choke_one_peer()
+ {
+ peer* p = find_choke_candidate();
+ if (p == 0) return;
+ assert(p->connection);
+ assert(!p->connection->is_disconnecting());
+ assert(!p->connection->is_choked());
+ p->connection->send_choke();
+ --m_num_unchoked;
+ }
+
+ bool policy::connect_one_peer()
+ {
+ if(m_torrent->num_peers() >= m_torrent->m_connections_quota.given)
+ return false;
+ peer* p = find_connect_candidate();
+ if (p == 0) return false;
+ assert(!p->banned);
+ assert(!p->connection);
+ assert(p->type == peer::connectable);
+
+ return connect_peer(p);
+ }
+
+ bool policy::connect_peer(peer *p)
+ {
+ INVARIANT_CHECK;
+ try
+ {
+ assert(!p->connection);
+ p->connection = &m_torrent->connect_to_peer(p->ip);
+ assert(p->connection);
+ p->connection->add_stat(p->prev_amount_download, p->prev_amount_upload);
+ p->prev_amount_download = 0;
+ p->prev_amount_upload = 0;
+ p->connected =
+ m_last_optimistic_disconnect =
+ second_clock::universal_time();
+ return true;
+ }
+ catch (std::exception& e)
+ {}
+ return false;
+ }
+
+ bool policy::disconnect_one_peer()
+ {
+ peer *p = find_disconnect_candidate();
+ if(!p)
+ return false;
+#if defined(TORRENT_VERBOSE_LOGGING)
+ (*p->connection->m_logger) << "*** CLOSING CONNECTION 'too many connections'\n";
+#endif
+
+ p->connection->disconnect();
+ return true;
+ }
+
+ // this is called whenever a peer connection is closed
+ void policy::connection_closed(const peer_connection& c) try
+ {
+ INVARIANT_CHECK;
+
+// assert(c.is_disconnecting());
+ bool unchoked = false;
+
+ std::vector<peer>::iterator i = std::find_if(
+ m_peers.begin()
+ , m_peers.end()
+ , match_peer_connection(c));
+
+ // if we couldn't find the connection in our list, just ignore it.
+ if (i == m_peers.end()) return;
+ assert(i->connection == &c);
+
+ i->connected = second_clock::universal_time();
+ if (!i->connection->is_choked() && !m_torrent->is_aborted())
+ {
+ unchoked = true;
+ }
+
+ if (c.failed())
+ {
+ i->type = peer::not_connectable;
+ i->ip.port(0);
+ }
+
+ // if the share ratio is 0 (infinite), the
+ // m_available_free_upload isn't used,
+ // because it isn't necessary.
+ if (m_torrent->ratio() != 0.f)
+ {
+ assert(i->connection->associated_torrent().lock().get() == m_torrent);
+ assert(i->connection->share_diff() < std::numeric_limits<size_type>::max());
+ m_available_free_upload += i->connection->share_diff();
+ }
+ i->prev_amount_download += c.statistics().total_payload_download();
+ i->prev_amount_upload += c.statistics().total_payload_upload();
+ i->connection = 0;
+
+ if (unchoked)
+ {
+ // if the peer that is diconnecting is unchoked
+ // then unchoke another peer in order to maintain
+ // the total number of unchoked peers
+ --m_num_unchoked;
+ if (m_torrent->is_seed()) seed_unchoke_one_peer();
+ else unchoke_one_peer();
+ }
+ }
+ catch (std::exception& e)
+ {
+#ifndef NDEBUG
+ std::string err = e.what();
+#endif
+ assert(false);
+ }
+
+ void policy::peer_is_interesting(peer_connection& c)
+ {
+ INVARIANT_CHECK;
+
+ c.send_interested();
+ if (c.has_peer_choked()) return;
+ request_a_block(*m_torrent, c);
+ }
+
+#ifndef NDEBUG
+ bool policy::has_connection(const peer_connection* c)
+ {
+ assert(c);
+#ifndef NDEBUG
+ assert(c->remote() == c->get_socket()->remote_endpoint());
+#endif
+ return std::find_if(
+ m_peers.begin()
+ , m_peers.end()
+ , match_peer_ip(c->remote())) != m_peers.end();
+ }
+
+ void policy::check_invariant() const
+ {
+ if (m_torrent->is_aborted()) return;
+ int actual_unchoked = 0;
+ int connected_peers = 0;
+
+ int total_connections = 0;
+ int nonempty_connections = 0;
+
+
+ for (std::vector<peer>::const_iterator i = m_peers.begin();
+ i != m_peers.end(); ++i)
+ {
+ ++total_connections;
+ if (!i->connection) continue;
+ ++nonempty_connections;
+ if (!i->connection->is_disconnecting())
+ ++connected_peers;
+ if (!i->connection->is_choked()) ++actual_unchoked;
+ }
+// assert(actual_unchoked <= m_torrent->m_uploads_quota.given);
+ assert(actual_unchoked == m_num_unchoked);
+
+ int num_torrent_peers = 0;
+ for (torrent::const_peer_iterator i = m_torrent->begin();
+ i != m_torrent->end(); ++i)
+ {
+ if (i->second->is_disconnecting()) continue;
+ // ignore web_peer_connections since they are not managed
+ // by the policy class
+ if (dynamic_cast<web_peer_connection*>(i->second)) continue;
+ ++num_torrent_peers;
+ }
+
+ // this invariant is a bit complicated.
+ // the usual case should be that connected_peers
+ // == num_torrent_peers. But when there's an incoming
+ // connection, it will first be added to the policy
+ // and then be added to the torrent.
+ // When there's an outgoing connection, it will first
+ // be added to the torrent and then to the policy.
+ // that's why the two second cases are in there.
+
+ assert(connected_peers == num_torrent_peers
+ || (connected_peers == num_torrent_peers + 1
+ && connected_peers > 0)
+ || (connected_peers + 1 == num_torrent_peers
+ && num_torrent_peers > 0));
+
+ // TODO: Make sure the number of peers in m_torrent is equal
+ // to the number of connected peers in m_peers.
+ }
+#endif
+
+ policy::peer::peer(const tcp::endpoint& ip_, peer::connection_type t)
+ : ip(ip_)
+ , type(t)
+ , last_optimistically_unchoked(
+ boost::gregorian::date(1970,boost::gregorian::Jan,1))
+ , connected(boost::gregorian::date(1970,boost::gregorian::Jan,1))
+ , prev_amount_upload(0)
+ , prev_amount_download(0)
+ , banned(false)
+ , connection(0)
+ {
+ assert(connected < second_clock::universal_time());
+ }
+
+ size_type policy::peer::total_download() const
+ {
+ if (connection != 0)
+ {
+ assert(prev_amount_download == 0);
+ return connection->statistics().total_payload_download();
+ }
+ else
+ {
+ return prev_amount_download;
+ }
+ }
+
+ size_type policy::peer::total_upload() const
+ {
+ if (connection != 0)
+ {
+ assert(prev_amount_upload == 0);
+ return connection->statistics().total_payload_upload();
+ }
+ else
+ {
+ return prev_amount_upload;
+ }
+ }
+}
+
diff --git a/library/preset.txt b/library/preset.txt
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/library/preset.txt
diff --git a/library/python-libtorrent.cpp b/library/python-libtorrent.cpp
new file mode 100755
index 000000000..3d38556a9
--- /dev/null
+++ b/library/python-libtorrent.cpp
@@ -0,0 +1,1103 @@
+/*
+ * Copyright © 2006 Alon Zakai ('Kripken') <kripkensteiner@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Thank You: Some code portions were derived from BSD-licensed work by
+ * Arvid Norberg, and GPL-licensed work by Christophe Dumez
+ */
+
+
+//------------------
+// TODO:
+//
+// The DHT capability requires UDP. We need to check that this port is in fact
+// open, just like the normal TCP port for bittorrent.
+//
+// Wait for answers about what to do with the router.*torrent.com's...
+//
+//------------------
+
+
+//-----------------
+// INCLUDES
+//-----------------
+
+#include <Python.h>
+
+#include <boost/filesystem/exception.hpp>
+#include <boost/filesystem/operations.hpp>
+
+#include "libtorrent/entry.hpp"
+#include "libtorrent/bencode.hpp"
+#include "libtorrent/session.hpp"
+#include "libtorrent/identify_client.hpp"
+#include "libtorrent/alert_types.hpp"
+#include "libtorrent/storage.hpp"
+#include "libtorrent/hasher.hpp"
+#include "libtorrent/ip_filter.hpp"
+
+using namespace libtorrent;
+
+
+//-----------------
+// CONSTANTS
+//-----------------
+
+#ifdef AMD64
+#define python_long int
+#else
+#define python_long long
+#endif
+
+#define EVENT_NULL 0
+#define EVENT_FINISHED 1
+#define EVENT_PEER_ERROR 2
+#define EVENT_INVALID_REQUEST 3
+#define EVENT_FILE_ERROR 4
+#define EVENT_HASH_FAILED_ERROR 5
+#define EVENT_PEER_BAN_ERROR 6
+#define EVENT_FASTRESUME_REJECTED_ERROR 8
+#define EVENT_TRACKER 9
+#define EVENT_OTHER 10
+
+#define STATE_QUEUED 0
+#define STATE_CHECKING 1
+#define STATE_CONNECTING 2
+#define STATE_DOWNLOADING_META 3
+#define STATE_DOWNLOADING 4
+#define STATE_FINISHED 5
+#define STATE_SEEDING 6
+#define STATE_ALLOCATING 7
+
+#define DHT_ROUTER_PORT 6881
+
+#define ERROR_INVALID_ENCODING -10
+#define ERROR_FILESYSTEM -20
+#define ERROR_DUPLICATE_TORRENT -30
+#define ERROR_INVALID_TORRENT -40
+
+
+//-----------------
+// TYPES
+//-----------------
+
+typedef long unique_ID_t;
+typedef std::vector<bool> filter_out_t;
+typedef std::string torrent_name_t;
+
+struct torrent_t {
+ torrent_handle handle;
+ unique_ID_t unique_ID;
+ filter_out_t filter_out;
+ torrent_name_t name;
+};
+
+typedef std::vector<torrent_t> torrents_t;
+typedef torrents_t::iterator torrents_t_iterator;
+
+
+//---------------------------
+// MODULE-GLOBAL VARIABLES
+//---------------------------
+
+long M_unique_counter = 0;
+session_settings *M_settings = NULL;
+session *M_ses = NULL;
+PyObject *M_constants = NULL;
+ip_filter *M_the_filter = NULL;
+torrents_t *M_torrents = NULL;
+
+//---------------------
+// Internal functions
+//---------------------
+
+bool empty_name_check(const std::string & name)
+{
+ return 1;
+}
+
+long handle_exists(torrent_handle &handle)
+{
+ for (unsigned long i = 0; i < M_torrents->size(); i++)
+ if ((*M_torrents)[i].handle == handle)
+ return 1;
+
+ return 0;
+}
+
+long get_torrent_index(torrent_handle &handle)
+{
+ for (unsigned long i = 0; i < M_torrents->size(); i++)
+ if ((*M_torrents)[i].handle == handle)
+ {
+// printf("Found: %li\r\n", i);
+ return i;
+ }
+
+ throw std::runtime_error("P-LT: Handle not found.");
+ return -1;
+}
+
+long get_index_from_unique_ID(long unique_ID)
+{
+ assert(M_handles->size() == M_unique_IDs->size());
+
+ for (unsigned long i = 0; i < M_torrents->size(); i++)
+ if ((*M_torrents)[i].unique_ID == unique_ID)
+ return i;
+
+ throw std::runtime_error("P-LT: No such unique_ID.");
+ return -1;
+}
+
+long internal_add_torrent(std::string const& torrent_name,
+ float preferred_ratio,
+ bool compact_mode,
+ boost::filesystem::path const& save_path)
+{
+ std::ifstream in(torrent_name.c_str(), std::ios_base::binary);
+ in.unsetf(std::ios_base::skipws);
+ entry e = bdecode(std::istream_iterator<char>(in), std::istream_iterator<char>());
+ torrent_info t(e);
+
+ entry resume_data;
+ try
+ {
+ std::stringstream s;
+ s << torrent_name << ".fastresume";
+ boost::filesystem::ifstream resumeFile(s.str(), std::ios_base::binary);
+ resumeFile.unsetf(std::ios_base::skipws);
+ resume_data = bdecode(std::istream_iterator<char>(resumeFile),
+ std::istream_iterator<char>());
+ }
+ catch (invalid_encoding&) {}
+ catch (boost::filesystem::filesystem_error&) {}
+
+ // Create new torrent object
+
+ torrent_t new_torrent;
+
+ torrent_handle h = M_ses->add_torrent(t, save_path, resume_data, compact_mode, 16 * 1024);
+// h.set_max_connections(60); // at some point we should use this
+ h.set_max_uploads(-1);
+ h.set_ratio(preferred_ratio);
+ new_torrent.handle = h;
+
+ new_torrent.unique_ID = M_unique_counter;
+ M_unique_counter++;
+
+ long num_files = h.get_torrent_info().num_files();
+
+ new_torrent.filter_out.reserve(num_files);
+
+ for (long i = 0; i < num_files; i++)
+ new_torrent.filter_out[i] = 0;
+
+ new_torrent.name = torrent_name;
+
+ M_torrents->push_back(new_torrent);
+
+ return (new_torrent.unique_ID);
+}
+
+void internal_remove_torrent(long index)
+{
+ assert(index < M_torrents->size());
+
+ torrent_handle& h = M_torrents->at(index).handle;
+
+ // For valid torrents, save fastresume data
+ if (h.is_valid() && h.has_metadata())
+ {
+ h.pause();
+
+ entry data = h.write_resume_data();
+
+ std::stringstream s;
+ s << M_torrents->at(index).name << ".fastresume";
+
+ boost::filesystem::ofstream out(s.str(), std::ios_base::binary);
+
+ out.unsetf(std::ios_base::skipws);
+
+ bencode(std::ostream_iterator<char>(out), data);
+ }
+
+ M_ses->remove_torrent(h);
+
+ torrents_t_iterator it = M_torrents->begin() + index;
+ M_torrents->erase(it);
+}
+
+long get_peer_index(tcp::endpoint addr, std::vector<peer_info> const& peers)
+{
+ long index = -1;
+
+ for (unsigned long i = 0; i < peers.size(); i++)
+ if (peers[i].ip == addr)
+ index = i;
+
+ return index;
+}
+
+// The following function contains code by Christophe Dumez and Arvid Norberg
+void internal_add_files(torrent_info& t,
+ boost::filesystem::path const& p,
+ boost::filesystem::path const& l)
+{
+ boost::filesystem::path f(p / l); // change default checker, perhaps?
+ if (is_directory(f))
+ {
+ for (boost::filesystem::directory_iterator i(f), end; i != end; ++i)
+ internal_add_files(t, p, l / i->leaf());
+ } else
+ t.add_file(l, file_size(f));
+}
+
+long count_DHT_peers(entry &state)
+{
+ long num_peers = 0;
+ entry *nodes = state.find_key("nodes");
+ if (nodes)
+ {
+ entry::list_type &peers = nodes->list();
+ entry::list_type::const_iterator i;
+ i = peers.begin();
+
+ while (i != peers.end())
+ {
+ num_peers++;
+ i++;
+ }
+ }
+
+ return num_peers;
+}
+
+
+//=====================
+// External functions
+//=====================
+
+static PyObject *torrent_init(PyObject *self, PyObject *args)
+{
+ printf("python-libtorrent, using libtorrent %s. Compiled with NDEBUG value: %d\r\n",
+ LIBTORRENT_VERSION,
+ NDEBUG);
+
+ // Tell Boost that we are on *NIX, so bloody '.'s are ok inside a directory name!
+ boost::filesystem::path::default_name_check(empty_name_check);
+
+ char *client_ID, *user_agent;
+ python_long v1,v2,v3,v4;
+
+ PyArg_ParseTuple(args, "siiiis", &client_ID, &v1, &v2, &v3, &v4, &user_agent);
+
+ M_settings = new session_settings;
+ M_ses = new session(fingerprint(client_ID, v1, v2, v3, v4));
+
+ M_torrents = new torrents_t;
+ M_torrents->reserve(10); // pretty cheap, just 10
+
+ // Init values
+
+ M_settings->user_agent = std::string(user_agent);
+
+ M_ses->set_max_half_open_connections(-1);
+ M_ses->set_download_rate_limit(-1);
+ M_ses->set_upload_rate_limit(-1);
+
+ M_ses->set_settings(*M_settings);
+ M_ses->set_severity_level(alert::debug);
+
+ 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,s:i,s:i,s:i,s:i}",
+ "EVENT_NULL", EVENT_NULL,
+ "EVENT_FINISHED", EVENT_FINISHED,
+ "EVENT_PEER_ERROR", EVENT_PEER_ERROR,
+ "EVENT_INVALID_REQUEST", EVENT_INVALID_REQUEST,
+ "EVENT_FILE_ERROR", EVENT_FILE_ERROR,
+ "EVENT_HASH_FAILED_ERROR", EVENT_HASH_FAILED_ERROR,
+ "EVENT_PEER_BAN_ERROR", EVENT_PEER_BAN_ERROR,
+ "EVENT_FASTRESUME_REJECTED_ERROR", EVENT_FASTRESUME_REJECTED_ERROR,
+ "EVENT_TRACKER", EVENT_TRACKER,
+ "EVENT_OTHER", EVENT_OTHER,
+ "STATE_QUEUED", STATE_QUEUED,
+ "STATE_CHECKING", STATE_CHECKING,
+ "STATE_CONNECTING", STATE_CONNECTING,
+ "STATE_DOWNLOADING_META", STATE_DOWNLOADING_META,
+ "STATE_DOWNLOADING", STATE_DOWNLOADING,
+ "STATE_FINISHED", STATE_FINISHED,
+ "STATE_SEEDING", STATE_SEEDING,
+ "STATE_ALLOCATING", STATE_ALLOCATING,
+ "ERROR_INVALID_ENCODING", ERROR_INVALID_ENCODING,
+ "ERROR_INVALID_TORRENT", ERROR_INVALID_TORRENT,
+ "ERROR_FILESYSTEM", ERROR_FILESYSTEM,
+ "ERROR_DUPLICATE_TORRENT", ERROR_DUPLICATE_TORRENT);
+
+ Py_INCREF(Py_None); return Py_None;
+};
+
+static PyObject *torrent_quit(PyObject *self, PyObject *args)
+{
+ long Num = M_torrents->size();
+
+ // Shut down torrents gracefully
+ for (long i = 0; i < Num; i++)
+ internal_remove_torrent(0);
+
+ delete M_ses; // SLOWPOKE because of waiting for the trackers before shutting down
+ delete M_settings;
+ delete M_torrents;
+
+ Py_DECREF(M_constants);
+
+ Py_INCREF(Py_None); return Py_None;
+};
+
+static PyObject *torrent_set_max_half_open(PyObject *self, PyObject *args)
+{
+ python_long arg;
+ PyArg_ParseTuple(args, "i", &arg);
+
+ M_ses->set_max_half_open_connections(arg);
+
+ Py_INCREF(Py_None); return Py_None;
+}
+
+static PyObject *torrent_set_download_rate_limit(PyObject *self, PyObject *args)
+{
+ python_long arg;
+ PyArg_ParseTuple(args, "i", &arg);
+// printf("Capping download to %d bytes per second\r\n", (int)arg);
+ M_ses->set_download_rate_limit(arg);
+
+ Py_INCREF(Py_None); return Py_None;
+}
+
+static PyObject *torrent_set_upload_rate_limit(PyObject *self, PyObject *args)
+{
+ python_long arg;
+ PyArg_ParseTuple(args, "i", &arg);
+// printf("Capping upload to %d bytes per second\r\n", (int)arg);
+ M_ses->set_upload_rate_limit(arg);
+
+ Py_INCREF(Py_None); return Py_None;
+}
+
+static PyObject *torrent_set_listen_on(PyObject *self, PyObject *args)
+{
+ python_long port_start, port_end;
+ PyArg_ParseTuple(args, "ii", &port_start, &port_end);
+
+ M_ses->listen_on(std::make_pair(port_start, port_end), "");
+
+ Py_INCREF(Py_None); return Py_None;
+}
+
+static PyObject *torrent_is_listening(PyObject *self, PyObject *args)
+{
+ long ret = (M_ses->is_listening() != 0);
+
+ return Py_BuildValue("i", ret);
+}
+
+static PyObject *torrent_listening_port(PyObject *self, PyObject *args)
+{
+ return Py_BuildValue("i", (python_long)M_ses->listen_port());
+}
+
+static PyObject *torrent_set_max_uploads(PyObject *self, PyObject *args)
+{
+ python_long max_up;
+ PyArg_ParseTuple(args, "i", &max_up);
+
+ M_ses->set_max_uploads(max_up);
+
+ Py_INCREF(Py_None); return Py_None;
+}
+
+static PyObject *torrent_set_max_connections(PyObject *self, PyObject *args)
+{
+ python_long max_conn;
+ PyArg_ParseTuple(args, "i", &max_conn);
+
+// printf("Setting max connections: %d\r\n", max_conn);
+ M_ses->set_max_connections(max_conn);
+
+ Py_INCREF(Py_None); return Py_None;
+}
+
+static PyObject *torrent_add_torrent(PyObject *self, PyObject *args)
+{
+ const char *name, *save_dir;
+ python_long compact;
+ PyArg_ParseTuple(args, "ssi", &name, &save_dir, &compact);
+
+ boost::filesystem::path save_dir_2 (save_dir, empty_name_check);
+
+ try
+ {
+ return Py_BuildValue("i", internal_add_torrent(name, 0, compact, save_dir_2));
+ }
+ catch (invalid_encoding&)
+ {
+ return Py_BuildValue("i", ERROR_INVALID_ENCODING);
+ }
+ catch (invalid_torrent_file&)
+ {
+ return Py_BuildValue("i", ERROR_INVALID_TORRENT);
+ }
+ catch (boost::filesystem::filesystem_error&)
+ {
+ return Py_BuildValue("i", ERROR_FILESYSTEM);
+ }
+ catch (duplicate_torrent&)
+ {
+ return Py_BuildValue("i", ERROR_DUPLICATE_TORRENT);
+ }
+}
+
+static PyObject *torrent_remove_torrent(PyObject *self, PyObject *args)
+{
+ python_long unique_ID;
+ PyArg_ParseTuple(args, "i", &unique_ID);
+ long index = get_index_from_unique_ID(unique_ID);
+
+ internal_remove_torrent(index);
+
+ Py_INCREF(Py_None); return Py_None;
+}
+
+static PyObject *torrent_get_num_torrents(PyObject *self, PyObject *args)
+{
+ return Py_BuildValue("i", M_torrents->size());
+}
+
+static PyObject *torrent_reannounce(PyObject *self, PyObject *args)
+{
+ python_long unique_ID;
+ PyArg_ParseTuple(args, "i", &unique_ID);
+ long index = get_index_from_unique_ID(unique_ID);
+
+ M_torrents->at(index).handle.force_reannounce();
+
+ Py_INCREF(Py_None); return Py_None;
+}
+
+static PyObject *torrent_pause(PyObject *self, PyObject *args)
+{
+ python_long unique_ID;
+ PyArg_ParseTuple(args, "i", &unique_ID);
+ long index = get_index_from_unique_ID(unique_ID);
+
+ M_torrents->at(index).handle.pause();
+
+ Py_INCREF(Py_None); return Py_None;
+}
+
+static PyObject *torrent_resume(PyObject *self, PyObject *args)
+{
+ python_long unique_ID;
+ PyArg_ParseTuple(args, "i", &unique_ID);
+ long index = get_index_from_unique_ID(unique_ID);
+
+ M_torrents->at(index).handle.resume();
+
+ Py_INCREF(Py_None); return Py_None;
+}
+
+static PyObject *torrent_get_name(PyObject *self, PyObject *args)
+{
+ python_long unique_ID;
+ PyArg_ParseTuple(args, "i", &unique_ID);
+ long index = get_index_from_unique_ID(unique_ID);
+
+ return Py_BuildValue("s", M_torrents->at(index).handle.get_torrent_info().name().c_str());
+}
+
+static PyObject *torrent_get_state(PyObject *self, PyObject *args)
+{
+ python_long unique_ID;
+ PyArg_ParseTuple(args, "i", &unique_ID);
+ long index = get_index_from_unique_ID(unique_ID);
+
+ torrent_t &t = M_torrents->at(index);
+ torrent_status s = t.handle.status();
+ const torrent_info &i = t.handle.get_torrent_info();
+
+ std::vector<peer_info> peers;
+ t.handle.get_peer_info(peers);
+
+ long total_seeds = 0;
+ long total_peers = 0;
+
+ for (unsigned long i = 0; i < peers.size(); i++)
+ if (peers[i].seed)
+ total_seeds++;
+ else
+ total_peers++;
+
+ return Py_BuildValue("{s:l,s:l,s:l,s:f,s:f,s:d,s:f,s:l,s:l,s:s,s:s,s:f,s:d,s:l,s:l,s:l,s:d,s:l,s:l,s:l,s:l,s:l,s:l,s:d,s:d,s:l,s:l}",
+ "state", s.state,
+ "num_peers", s.num_peers,
+ "num_seeds", s.num_seeds,
+ "distributed_copies", s.distributed_copies,
+ "download_rate", s.download_rate,
+ "total_download", double(s.total_download), // WAS: payload in the middle there
+ "upload_rate", s.upload_rate,
+ "total_upload", long(s.total_upload), // WAS: payload
+//"ratio", float(-1),//float(s.total_payload_download)/float(s.total_payload_upload),
+ "tracker_ok", !s.current_tracker.empty(),
+ "next_announce", boost::posix_time::to_simple_string(s.next_announce).c_str(),
+ "tracker", s.current_tracker.c_str(),
+ "progress", float(s.progress),
+ "total_done", double(s.total_done),
+ "pieces", long(s.pieces),
+ "pieces_done", long(s.num_pieces),
+ "block_size", long(s.block_size),
+ "total_size", double(i.total_size()),
+ "piece_length", long(i.piece_length()),
+ "num_pieces", long(i.num_pieces()),
+ "total_seeds", total_seeds,
+ "total_peers", total_peers,
+ "is_paused", long(t.handle.is_paused()),
+ "is_seed", long(t.handle.is_seed()),
+ "total_wanted", double(s.total_wanted),
+ "total_wanted_done", double(s.total_wanted_done),
+ "num_complete", long(s.num_complete),
+ "num_incomplete", long(s.num_incomplete));
+};
+
+static PyObject *torrent_pop_event(PyObject *self, PyObject *args)
+{
+ std::auto_ptr<alert> a;
+
+ a = M_ses->pop_alert();
+
+ alert *popped_alert = a.get();
+
+ if (!popped_alert)
+ {
+ Py_INCREF(Py_None); return Py_None;
+ } else if (dynamic_cast<torrent_finished_alert*>(popped_alert))
+ {
+ torrent_handle handle = (dynamic_cast<torrent_finished_alert*>(popped_alert))->handle;
+
+ if (handle_exists(handle))
+ return Py_BuildValue("{s:i,s:i}", "event_type", EVENT_FINISHED,
+ "unique_ID",
+ M_torrents->at(get_torrent_index(handle)).unique_ID);
+ else
+ { Py_INCREF(Py_None); return Py_None; }
+ } else if (dynamic_cast<peer_error_alert*>(popped_alert))
+ {
+ peer_id peer_ID = (dynamic_cast<peer_error_alert*>(popped_alert))->pid;
+ std::string peer_IP =
+ (dynamic_cast<peer_error_alert*>(popped_alert))->ip.address().to_string();
+
+ return Py_BuildValue("{s:i,s:s,s:s,s:s}", "event_type", EVENT_PEER_ERROR,
+ "client_ID", identify_client(peer_ID).c_str(),
+ "ip", peer_IP.c_str(),
+ "message", a->msg().c_str() );
+ } else if (dynamic_cast<invalid_request_alert*>(popped_alert))
+ {
+ peer_id peer_ID = (dynamic_cast<invalid_request_alert*>(popped_alert))->pid;
+
+ return Py_BuildValue("{s:i,s:s,s:s}",
+ "event_type", EVENT_INVALID_REQUEST,
+ "client_ID", identify_client(peer_ID).c_str(),
+ "message", a->msg().c_str() );
+ } else if (dynamic_cast<file_error_alert*>(popped_alert))
+ {
+ torrent_handle handle = (dynamic_cast<file_error_alert*>(popped_alert))->handle;
+
+ if (handle_exists(handle))
+ return Py_BuildValue("{s:i,s:i,s:s}",
+ "event_type", EVENT_FILE_ERROR,
+ "unique_ID", M_torrents->at(get_torrent_index(handle)).unique_ID,
+ "message", a->msg().c_str() );
+ else
+ { Py_INCREF(Py_None); return Py_None; }
+ } else if (dynamic_cast<hash_failed_alert*>(popped_alert))
+ {
+ torrent_handle handle = (dynamic_cast<hash_failed_alert*>(popped_alert))->handle;
+
+ if (handle_exists(handle))
+ return Py_BuildValue("{s:i,s:i,s:i,s:s}",
+ "event_type", EVENT_HASH_FAILED_ERROR,
+ "unique_ID", M_torrents->at(get_torrent_index(handle)).unique_ID,
+ "piece_index",
+ long((dynamic_cast<hash_failed_alert*>(popped_alert))->piece_index),
+ "message", a->msg().c_str() );
+ else
+ { Py_INCREF(Py_None); return Py_None; }
+ } else if (dynamic_cast<peer_ban_alert*>(popped_alert))
+ {
+ torrent_handle handle = (dynamic_cast<peer_ban_alert*>(popped_alert))->handle;
+ std::string peer_IP = (dynamic_cast<peer_ban_alert*>(popped_alert))->ip.address().to_string();
+
+ if (handle_exists(handle))
+ return Py_BuildValue("{s:i,s:i,s:s,s:s}",
+ "event_type", EVENT_PEER_BAN_ERROR,
+ "unique_ID", M_torrents->at(get_torrent_index(handle)).unique_ID,
+ "ip", peer_IP.c_str(),
+ "message", a->msg().c_str() );
+ else
+ { Py_INCREF(Py_None); return Py_None; }
+ } else if (dynamic_cast<fastresume_rejected_alert*>(popped_alert))
+ {
+ torrent_handle handle = (dynamic_cast<fastresume_rejected_alert*>(popped_alert))->handle;
+
+ if (handle_exists(handle))
+ return Py_BuildValue("{s:i,s:i,s:s}",
+ "event_type", EVENT_FASTRESUME_REJECTED_ERROR,
+ "unique_ID", M_torrents->at(get_torrent_index(handle)).unique_ID,
+ "message", a->msg().c_str() );
+ else
+ { Py_INCREF(Py_None); return Py_None; }
+ } else if (dynamic_cast<tracker_announce_alert*>(popped_alert))
+ {
+ torrent_handle handle = (dynamic_cast<tracker_announce_alert*>(popped_alert))->handle;
+
+ if (handle_exists(handle))
+ return Py_BuildValue("{s:i,s:i,s:s,s:s}",
+ "event_type", EVENT_TRACKER,
+ "unique_ID",
+ M_torrents->at(get_torrent_index(handle)).unique_ID,
+ "tracker_status", "Announce sent",
+ "message", a->msg().c_str() );
+ else
+ { Py_INCREF(Py_None); return Py_None; }
+ } else if (dynamic_cast<tracker_alert*>(popped_alert))
+ {
+ torrent_handle handle = (dynamic_cast<tracker_alert*>(popped_alert))->handle;
+
+ if (handle_exists(handle))
+ return Py_BuildValue("{s:i,s:i,s:s,s:s}",
+ "event_type", EVENT_TRACKER,
+ "unique_ID",
+ M_torrents->at(get_torrent_index(handle)).unique_ID,
+ "trackerStatus", "Bad response (status code=?)",
+ "message", a->msg().c_str() );
+ else
+ { Py_INCREF(Py_None); return Py_None; }
+ } else if (dynamic_cast<tracker_reply_alert*>(popped_alert))
+ {
+ torrent_handle handle = (dynamic_cast<tracker_reply_alert*>(popped_alert))->handle;
+
+ if (handle_exists(handle))
+ return Py_BuildValue("{s:i,s:i,s:s,s:s}",
+ "event_type", EVENT_TRACKER,
+ "unique_ID",
+ M_torrents->at(get_torrent_index(handle)).unique_ID,
+ "tracker_status", "Announce succeeded",
+ "message", a->msg().c_str() );
+ else
+ { Py_INCREF(Py_None); return Py_None; }
+ } else if (dynamic_cast<tracker_warning_alert*>(popped_alert))
+ {
+ torrent_handle handle = (dynamic_cast<tracker_warning_alert*>(popped_alert))->handle;
+
+ if (handle_exists(handle))
+ return Py_BuildValue("{s:i,s:i,s:s,s:s}",
+ "event_type", EVENT_TRACKER,
+ "unique_ID",
+ M_torrents->at(get_torrent_index(handle)).unique_ID,
+ "tracker_status", "Warning in response",
+ "message", a->msg().c_str() );
+ else
+ { Py_INCREF(Py_None); return Py_None; }
+ }
+
+ return Py_BuildValue("{s:i,s:s}", "event_type", EVENT_OTHER,
+ "message", a->msg().c_str() );
+}
+
+static PyObject *torrent_get_session_info(PyObject *self, PyObject *args)
+{
+ session_status s = M_ses->status();
+
+ return Py_BuildValue("{s:l,s:f,s:f,s:f,s:f,s:l}",
+ "has_incoming_connections", long(s.has_incoming_connections),
+ "upload_rate", float(s.upload_rate),
+ "download_rate", float(s.download_rate),
+ "payload_upload_rate", float(s.payload_upload_rate),
+ "payload_download_rate", float(s.payload_download_rate),
+ "num_peers", long(s.num_peers));
+}
+
+static PyObject *torrent_get_peer_info(PyObject *self, PyObject *args)
+{
+ python_long unique_ID;
+ PyArg_ParseTuple(args, "i", &unique_ID);
+ long index = get_index_from_unique_ID(unique_ID);
+
+ std::vector<peer_info> peers;
+ M_torrents->at(index).handle.get_peer_info(peers);
+
+ PyObject *peer_info;
+
+ PyObject *ret = PyTuple_New(peers.size());
+
+ for (unsigned long i = 0; i < peers.size(); i++)
+ {
+ std::vector<bool> &pieces = peers[i].pieces;
+ unsigned long pieces_had = 0;
+
+ for (unsigned long piece = 0; piece < pieces.size(); piece++)
+ if (pieces[piece])
+ pieces_had++;
+
+ peer_info = Py_BuildValue(
+ "{s:f,s:d,s:f,s:d,s:i,s:i,s:i,s:i,s:i,s:i,s:i,s:i,s:i,s:i,s:i,s:s,s:i,s:s,s:f}",
+ "download_speed", float(peers[i].down_speed),
+ "total_download", double(peers[i].total_download),
+ "upload_speed", float(peers[i].up_speed),
+ "total_upload", double(peers[i].total_upload),
+ "download_queue_length", long(peers[i].download_queue_length),
+ "upload_queue_length", long(peers[i].upload_queue_length),
+ "is_interesting", long((peers[i].flags & peer_info::interesting) != 0),
+ "is_choked", long((peers[i].flags & peer_info::choked) != 0),
+ "is_remote_interested", long((peers[i].flags & peer_info::remote_interested) != 0),
+ "is_remote_choked", long((peers[i].flags & peer_info::remote_choked) != 0),
+ "supports_extensions", long((peers[i].flags & peer_info::supports_extensions)!= 0),
+ "is_local_connection", long((peers[i].flags & peer_info::local_connection) != 0),
+ "is_awaiting_handshake", long((peers[i].flags & peer_info::handshake) != 0),
+ "is_connecting", long((peers[i].flags & peer_info::connecting) != 0),
+ "is_queued", long((peers[i].flags & peer_info::queued) != 0),
+ "client", peers[i].client.c_str(),
+ "is_seed", long(peers[i].seed),
+ "ip", peers[i].ip.address().to_string().c_str(),
+ "peer_has", float(float(pieces_had)*100.0/pieces.size())
+ );
+
+ PyTuple_SetItem(ret, i, peer_info);
+ };
+
+ return ret;
+};
+
+static PyObject *torrent_get_file_info(PyObject *self, PyObject *args)
+{
+ python_long unique_ID;
+ PyArg_ParseTuple(args, "i", &unique_ID);
+ long index = get_index_from_unique_ID(unique_ID);
+
+ std::vector<PyObject *> temp_files;
+
+ PyObject *file_info;
+
+ std::vector<float> progresses;
+
+ torrent_t &t = M_torrents->at(index);
+ t.handle.file_progress(progresses);
+
+ torrent_info::file_iterator start =
+ t.handle.get_torrent_info().begin_files();
+ torrent_info::file_iterator end =
+ t.handle.get_torrent_info().end_files();
+
+ long fileIndex = 0;
+
+ filter_out_t &filter_out = t.filter_out;
+
+ for(torrent_info::file_iterator i = start; i != end; ++i)
+ {
+ file_entry const &currFile = (*i);
+
+ file_info = Py_BuildValue(
+ "{s:s,s:d,s:d,s:f,s:i}",
+ "path", currFile.path.string().c_str(),
+ "offset", double(currFile.offset),
+ "size", double(currFile.size),
+ "progress", progresses[i - start]*100.0,
+ "filtered_out", long(filter_out.at(fileIndex))
+ );
+
+ fileIndex++;
+
+ temp_files.push_back(file_info);
+ };
+
+ PyObject *ret = PyTuple_New(temp_files.size());
+
+ for (unsigned long i = 0; i < temp_files.size(); i++)
+ PyTuple_SetItem(ret, i, temp_files[i]);
+
+ return ret;
+};
+
+static PyObject *torrent_set_filter_out(PyObject *self, PyObject *args)
+{
+ python_long unique_ID;
+ PyObject *filter_out_object;
+ PyArg_ParseTuple(args, "iO", &unique_ID, &filter_out_object);
+ long index = get_index_from_unique_ID(unique_ID);
+
+ torrent_t &t = M_torrents->at(index);
+ long num_files = t.handle.get_torrent_info().num_files();
+ assert(PyList_Size(filter_out_object) == num_files);
+
+ for (long i = 0; i < num_files; i++)
+ {
+ t.filter_out.at(i) =
+ PyInt_AsLong(PyList_GetItem(filter_out_object, i));
+ };
+
+ t.handle.filter_files(t.filter_out);
+
+ Py_INCREF(Py_None); return Py_None;
+}
+
+static PyObject *torrent_constants(PyObject *self, PyObject *args)
+{
+ Py_INCREF(M_constants); return M_constants;
+}
+
+static PyObject *torrent_start_DHT(PyObject *self, PyObject *args)
+{
+ const char *DHT_path;
+ PyArg_ParseTuple(args, "s", &DHT_path);
+
+ printf("Loading DHT state from %s\r\n", DHT_path);
+
+ boost::filesystem::path tempPath(DHT_path, empty_name_check);
+ boost::filesystem::ifstream DHT_state_file(tempPath, std::ios_base::binary);
+ DHT_state_file.unsetf(std::ios_base::skipws);
+
+ entry DHT_state;
+ try{
+ DHT_state = bdecode(std::istream_iterator<char>(DHT_state_file),
+ std::istream_iterator<char>());
+ M_ses->start_dht(DHT_state);
+ printf("DHT state recovered.\r\n");
+
+ // Print out the state data from the FILE (not the session!)
+ printf("Number of DHT peers in recovered state: %ld\r\n", count_DHT_peers(DHT_state));
+
+ } catch (std::exception&) {
+ printf("No DHT file to resume\r\n");
+ M_ses->start_dht();
+ }
+
+// M_ses->add_dht_router(std::make_pair(std::string("router.bittorrent.com"),
+// DHT_ROUTER_PORT));
+// M_ses->add_dht_router(std::make_pair(std::string("router.utorrent.com"),
+// DHT_ROUTER_PORT));
+//// M_ses->add_dht_router(std::make_pair(std::string("router.bitcomet.com"),
+//// DHT_ROUTER_PORT));
+
+ Py_INCREF(Py_None); return Py_None;
+}
+
+static PyObject *torrent_stop_DHT(PyObject *self, PyObject *args)
+{
+ const char *DHT_path;
+ PyArg_ParseTuple(args, "s", &DHT_path);
+
+ printf("Saving DHT state to %s\r\n", DHT_path);
+
+ boost::filesystem::path tempPath = boost::filesystem::path(DHT_path, empty_name_check);
+
+ try {
+ entry DHT_state = M_ses->dht_state();
+
+ printf("Number of DHT peers in state, saving: %ld\r\n", count_DHT_peers(DHT_state));
+
+ boost::filesystem::ofstream out(tempPath, std::ios_base::binary);
+ out.unsetf(std::ios_base::skipws);
+ bencode(std::ostream_iterator<char>(out), DHT_state);
+ } catch (std::exception& e) {
+ printf("An error occured in saving DHT\r\n");
+ std::cerr << e.what() << "\n";
+ }
+
+ Py_INCREF(Py_None); return Py_None;
+}
+
+static PyObject *torrent_get_DHT_info(PyObject *self, PyObject *args)
+{
+ entry DHT_state = M_ses->dht_state();
+
+ return Py_BuildValue("l", python_long(count_DHT_peers(DHT_state)));
+
+/*
+// DHT_state.print(cout);
+ entry *nodes = DHT_state.find_key("nodes");
+ if (!nodes)
+ return Py_BuildValue("l", -1); // No nodes - we are just starting up...
+
+ entry::list_type &peers = nodes->list();
+ entry::list_type::const_iterator i;
+
+ python_long num_peers = 0;
+
+ i = peers.begin();
+ while (i != peers.end())
+ {
+ num_peers++;
+ i++;
+ }
+
+ return Py_BuildValue("l", num_peers);
+*/
+}
+
+// Create Torrents: call with something like:
+// create_torrent("mytorrent.torrent", "directory or file to make a torrent out of",
+// "tracker1\ntracker2\ntracker3", "no comment", 256, "Deluge");
+// That makes a torrent with pieces of 256K, with "Deluge" as the creator string.
+//
+// The following function contains code by Christophe Dumez and Arvid Norberg
+static PyObject *torrent_create_torrent(PyObject *self, PyObject *args)
+{
+ char *destination, *comment, *creator_str, *input, *trackers;
+ python_long piece_size;
+ PyArg_ParseTuple(args, "ssssis", &destination, &input, &trackers, &comment, &piece_size, &creator_str);
+
+ piece_size = piece_size * 1024;
+
+ try
+ {
+ torrent_info t;
+ boost::filesystem::path full_path = complete(boost::filesystem::path(input));
+ boost::filesystem::ofstream out(complete(boost::filesystem::path(destination)),
+ std::ios_base::binary);
+
+ internal_add_files(t, full_path.branch_path(), full_path.leaf());
+ t.set_piece_size(piece_size);
+
+ storage st(t, full_path.branch_path());
+
+ std::string stdTrackers(trackers);
+ unsigned long index = 0, next = stdTrackers.find("\n");
+ while (1 == 1)
+ {
+ t.add_tracker(stdTrackers.substr(index, next-index));
+ index = next + 1;
+ if (next >= stdTrackers.length())
+ break;
+ next = stdTrackers.find("\n", index);
+ if (next == std::string::npos)
+ break;
+ }
+
+ int num = t.num_pieces();
+ std::vector<char> buf(piece_size);
+ for (int i = 0; i < num; ++i)
+ {
+ st.read(&buf[0], i, 0, t.piece_size(i));
+ hasher h(&buf[0], t.piece_size(i));
+ t.set_hash(i, h.final());
+ }
+
+ t.set_creator(creator_str);
+ t.set_comment(comment);
+
+ entry e = t.create_torrent();
+ bencode(std::ostream_iterator<char>(out), e);
+ return Py_BuildValue("l", 1);
+ } catch (std::exception& e)
+ {
+ std::cerr << e.what() << "\n";
+ return Py_BuildValue("l", 0);
+ }
+}
+
+static PyObject *torrent_apply_IP_filter(PyObject *self, PyObject *args)
+{
+ PyObject *ranges;
+ PyArg_ParseTuple(args, "O", &ranges);
+
+ long num_ranges = PyList_Size(ranges);
+
+// printf("Number of ranges: %ld\r\n", num_ranges);
+// Py_INCREF(Py_None); return Py_None;
+
+ // Remove existing filter, if there is one
+ if (M_the_filter != NULL)
+ delete M_the_filter;
+
+ M_the_filter = new ip_filter();
+
+ address_v4 from, to;
+ PyObject *curr;
+
+// printf("Can I 10.10.10.10? %d\r\n", the_filter->access(address_v4::from_string("10.10.10.10")));
+
+ for (long i = 0; i < num_ranges; i++)
+ {
+ curr = PyList_GetItem(ranges, i);
+// PyObject_Print(curr, stdout, 0);
+ from = address_v4::from_string(PyString_AsString(PyList_GetItem(curr, 0)));
+ to = address_v4::from_string(PyString_AsString(PyList_GetItem(curr, 1)));
+// printf("Filtering: %s - %s\r\n", from.to_string().c_str(), to.to_string().c_str());
+ M_the_filter->add_rule(from, to, ip_filter::blocked);
+ };
+
+// printf("Can I 10.10.10.10? %d\r\n", the_filter->access(address_v4::from_string("10.10.10.10")));
+
+ M_ses->set_ip_filter(*M_the_filter);
+
+// printf("Can I 10.10.10.10? %d\r\n", the_filter->access(address_v4::from_string("10.10.10.10")));
+
+ Py_INCREF(Py_None); return Py_None;
+}
+
+
+//====================
+// Python Module data
+//====================
+
+static PyMethodDef TorrentMethods[] = {
+ {"init", torrent_init, METH_VARARGS, "."},
+ {"quit", torrent_quit, METH_VARARGS, "."},
+ {"set_max_half_open", torrent_set_max_half_open, METH_VARARGS, "."},
+ {"set_download_rate_limit", torrent_set_download_rate_limit, METH_VARARGS, "."},
+ {"set_upload_rate_limit", torrent_set_upload_rate_limit, METH_VARARGS, "."},
+ {"set_listen_on", torrent_set_listen_on, METH_VARARGS, "."},
+ {"is_listening", torrent_is_listening, METH_VARARGS, "."},
+ {"listening_port", torrent_listening_port, METH_VARARGS, "."},
+ {"set_max_uploads", torrent_set_max_uploads, METH_VARARGS, "."},
+ {"set_max_connections", torrent_set_max_connections, METH_VARARGS, "."},
+ {"add_torrent", torrent_add_torrent, METH_VARARGS, "."},
+ {"remove_torrent", torrent_remove_torrent, METH_VARARGS, "."},
+ {"get_num_torrents", torrent_get_num_torrents, METH_VARARGS, "."},
+ {"reannounce", torrent_reannounce, METH_VARARGS, "."},
+ {"pause", torrent_pause, METH_VARARGS, "."},
+ {"resume", torrent_resume, METH_VARARGS, "."},
+ {"get_name", torrent_get_name, METH_VARARGS, "."},
+ {"get_state", torrent_get_state, METH_VARARGS, "."},
+ {"pop_event", torrent_pop_event, METH_VARARGS, "."},
+ {"get_session_info", torrent_get_session_info, METH_VARARGS, "."},
+ {"get_peer_info", torrent_get_peer_info, METH_VARARGS, "."},
+ {"get_file_info", torrent_get_file_info, METH_VARARGS, "."},
+ {"set_filter_out", torrent_set_filter_out, METH_VARARGS, "."},
+ {"constants", torrent_constants, METH_VARARGS, "."},
+ {"start_DHT", torrent_start_DHT, METH_VARARGS, "."},
+ {"stop_DHT", torrent_stop_DHT, METH_VARARGS, "."},
+ {"get_DHT_info", torrent_get_DHT_info, METH_VARARGS, "."},
+ {"create_torrent", torrent_create_torrent, METH_VARARGS, "."},
+ {"apply_IP_filter", torrent_apply_IP_filter, METH_VARARGS, "."},
+ {NULL} /* Sentinel */
+};
+
+
+PyMODINIT_FUNC
+inittorrent(void)
+{
+ Py_InitModule("torrent", TorrentMethods);
+}
diff --git a/library/session.cpp b/library/session.cpp
new file mode 100755
index 000000000..611267427
--- /dev/null
+++ b/library/session.cpp
@@ -0,0 +1,287 @@
+/*
+
+Copyright (c) 2006, Arvid Norberg, Magnus Jonsson
+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.
+
+*/
+
+#include <ctime>
+#include <iostream>
+#include <fstream>
+#include <iomanip>
+#include <iterator>
+#include <algorithm>
+#include <set>
+#include <cctype>
+#include <algorithm>
+
+#ifdef _MSC_VER
+#pragma warning(push, 1)
+#endif
+
+#include <boost/lexical_cast.hpp>
+#include <boost/filesystem/convenience.hpp>
+#include <boost/filesystem/exception.hpp>
+#include <boost/limits.hpp>
+#include <boost/bind.hpp>
+
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+#include "libtorrent/peer_id.hpp"
+#include "libtorrent/torrent_info.hpp"
+#include "libtorrent/tracker_manager.hpp"
+#include "libtorrent/bencode.hpp"
+#include "libtorrent/hasher.hpp"
+#include "libtorrent/entry.hpp"
+#include "libtorrent/session.hpp"
+#include "libtorrent/fingerprint.hpp"
+#include "libtorrent/entry.hpp"
+#include "libtorrent/alert_types.hpp"
+#include "libtorrent/invariant_check.hpp"
+#include "libtorrent/file.hpp"
+#include "libtorrent/allocate_resources.hpp"
+#include "libtorrent/bt_peer_connection.hpp"
+#include "libtorrent/ip_filter.hpp"
+#include "libtorrent/socket.hpp"
+#include "libtorrent/aux_/session_impl.hpp"
+#include "libtorrent/kademlia/dht_tracker.hpp"
+
+using namespace boost::posix_time;
+using boost::shared_ptr;
+using boost::weak_ptr;
+using boost::bind;
+using boost::mutex;
+using libtorrent::aux::session_impl;
+
+namespace libtorrent
+{
+
+ session::session(
+ fingerprint const& id
+ , std::pair<int, int> listen_port_range
+ , char const* listen_interface)
+ : m_impl(new session_impl(listen_port_range, id, listen_interface))
+ {
+ // turn off the filename checking in boost.filesystem
+ using namespace boost::filesystem;
+ if (path::default_name_check_writable())
+ path::default_name_check(no_check);
+ assert(listen_port_range.first > 0);
+ assert(listen_port_range.first < listen_port_range.second);
+#ifndef NDEBUG
+ // this test was added after it came to my attention
+ // that devstudios managed c++ failed to generate
+ // correct code for boost.function
+ boost::function0<void> test = boost::ref(*m_impl);
+ assert(!test.empty());
+#endif
+ }
+
+ session::session(fingerprint const& id)
+ : m_impl(new session_impl(std::make_pair(0, 0), id))
+ {
+#ifndef NDEBUG
+ boost::function0<void> test = boost::ref(*m_impl);
+ assert(!test.empty());
+#endif
+ }
+
+ session::~session()
+ {
+ assert(m_impl);
+ // if there is at least one destruction-proxy
+ // abort the session and let the destructor
+ // of the proxy to syncronize
+ if (!m_impl.unique())
+ m_impl->abort();
+ }
+
+ void session::disable_extensions()
+ {
+ m_impl->disable_extensions();
+ }
+
+ void session::set_ip_filter(ip_filter const& f)
+ {
+ m_impl->set_ip_filter(f);
+ }
+
+ void session::set_peer_id(peer_id const& id)
+ {
+ m_impl->set_peer_id(id);
+ }
+
+ void session::set_key(int key)
+ {
+ m_impl->set_key(key);
+ }
+
+ void session::enable_extension(extension_index i)
+ {
+ m_impl->enable_extension(i);
+ }
+
+ std::vector<torrent_handle> session::get_torrents() const
+ {
+ return m_impl->get_torrents();
+ }
+
+ // 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
+ , entry const& resume_data
+ , bool compact_mode
+ , int block_size)
+ {
+ return m_impl->add_torrent(ti, save_path, resume_data
+ , compact_mode, block_size);
+ }
+
+ torrent_handle session::add_torrent(
+ char const* tracker_url
+ , sha1_hash const& info_hash
+ , boost::filesystem::path const& save_path
+ , entry const& e
+ , bool compact_mode
+ , int block_size)
+ {
+ return m_impl->add_torrent(tracker_url, info_hash, save_path, e
+ , compact_mode, block_size);
+ }
+
+ void session::remove_torrent(const torrent_handle& h)
+ {
+ m_impl->remove_torrent(h);
+ }
+
+ bool session::listen_on(
+ std::pair<int, int> const& port_range
+ , const char* net_interface)
+ {
+ return m_impl->listen_on(port_range, net_interface);
+ }
+
+ unsigned short session::listen_port() const
+ {
+ return m_impl->listen_port();
+ }
+
+ session_status session::status() const
+ {
+ return m_impl->status();
+ }
+
+#ifndef TORRENT_DISABLE_DHT
+
+ void session::start_dht(entry const& startup_state)
+ {
+ m_impl->start_dht(startup_state);
+ }
+
+ void session::stop_dht()
+ {
+ m_impl->stop_dht();
+ }
+
+ void session::set_dht_settings(dht_settings const& settings)
+ {
+ m_impl->set_dht_settings(settings);
+ }
+
+ entry session::dht_state() const
+ {
+ return m_impl->dht_state();
+ }
+
+ void session::add_dht_node(std::pair<std::string, int> const& node)
+ {
+ m_impl->add_dht_node(node);
+ }
+
+ void session::add_dht_router(std::pair<std::string, int> const& node)
+ {
+ m_impl->add_dht_router(node);
+ }
+
+#endif
+
+ bool session::is_listening() const
+ {
+ return m_impl->is_listening();
+ }
+
+ void session::set_settings(session_settings const& s)
+ {
+ m_impl->set_settings(s);
+ }
+
+ session_settings const& session::settings()
+ {
+ return m_impl->settings();
+ }
+
+ void session::set_max_uploads(int limit)
+ {
+ m_impl->set_max_uploads(limit);
+ }
+
+ void session::set_max_connections(int limit)
+ {
+ m_impl->set_max_connections(limit);
+ }
+
+ void session::set_max_half_open_connections(int limit)
+ {
+ m_impl->set_max_half_open_connections(limit);
+ }
+
+ void session::set_upload_rate_limit(int bytes_per_second)
+ {
+ m_impl->set_upload_rate_limit(bytes_per_second);
+ }
+
+ void session::set_download_rate_limit(int bytes_per_second)
+ {
+ m_impl->set_download_rate_limit(bytes_per_second);
+ }
+
+ std::auto_ptr<alert> session::pop_alert()
+ {
+ return m_impl->pop_alert();
+ }
+
+ void session::set_severity_level(alert::severity_t s)
+ {
+ m_impl->set_severity_level(s);
+ }
+
+}
+
diff --git a/library/session_impl.cpp b/library/session_impl.cpp
new file mode 100755
index 000000000..c8005879a
--- /dev/null
+++ b/library/session_impl.cpp
@@ -0,0 +1,1855 @@
+/*
+
+Copyright (c) 2006, Arvid Norberg, Magnus Jonsson
+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.
+
+*/
+
+#include <ctime>
+#include <iostream>
+#include <fstream>
+#include <iomanip>
+#include <iterator>
+#include <algorithm>
+#include <set>
+#include <cctype>
+#include <algorithm>
+
+#ifdef _MSC_VER
+#pragma warning(push, 1)
+#endif
+
+#include <boost/lexical_cast.hpp>
+#include <boost/filesystem/convenience.hpp>
+#include <boost/filesystem/exception.hpp>
+#include <boost/limits.hpp>
+#include <boost/bind.hpp>
+
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+#include "libtorrent/peer_id.hpp"
+#include "libtorrent/torrent_info.hpp"
+#include "libtorrent/tracker_manager.hpp"
+#include "libtorrent/bencode.hpp"
+#include "libtorrent/hasher.hpp"
+#include "libtorrent/entry.hpp"
+#include "libtorrent/session.hpp"
+#include "libtorrent/fingerprint.hpp"
+#include "libtorrent/entry.hpp"
+#include "libtorrent/alert_types.hpp"
+#include "libtorrent/invariant_check.hpp"
+#include "libtorrent/file.hpp"
+#include "libtorrent/allocate_resources.hpp"
+#include "libtorrent/bt_peer_connection.hpp"
+#include "libtorrent/ip_filter.hpp"
+#include "libtorrent/socket.hpp"
+#include "libtorrent/aux_/session_impl.hpp"
+#include "libtorrent/kademlia/dht_tracker.hpp"
+
+using namespace boost::posix_time;
+using boost::shared_ptr;
+using boost::weak_ptr;
+using boost::bind;
+using boost::mutex;
+using libtorrent::aux::session_impl;
+
+namespace libtorrent { namespace detail
+{
+
+ std::string generate_auth_string(std::string const& user
+ , std::string const& passwd)
+ {
+ if (user.empty()) return std::string();
+ return user + ":" + passwd;
+ }
+
+
+ } namespace aux {
+ // This is the checker thread
+ // it is looping in an infinite loop
+ // until the session is aborted. It will
+ // normally just block in a wait() call,
+ // waiting for a signal from session that
+ // there's a new torrent to check.
+
+ void checker_impl::operator()()
+ {
+ eh_initializer();
+ // if we're currently performing a full file check,
+ // this is the torrent being processed
+ boost::shared_ptr<piece_checker_data> processing;
+ boost::shared_ptr<piece_checker_data> t;
+ for (;;)
+ {
+ // temporary torrent used while checking fastresume data
+ try
+ {
+ t.reset();
+ {
+ boost::mutex::scoped_lock l(m_mutex);
+
+ INVARIANT_CHECK;
+
+ // if the job queue is empty and
+ // we shouldn't abort
+ // wait for a signal
+ if (m_torrents.empty() && !m_abort && !processing)
+ m_cond.wait(l);
+
+ if (m_abort)
+ {
+ // no lock is needed here, because the main thread
+ // has already been shut down by now
+ processing.reset();
+ t.reset();
+ std::for_each(m_torrents.begin(), m_torrents.end()
+ , boost::bind(&torrent::abort
+ , boost::bind(&shared_ptr<torrent>::get
+ , boost::bind(&piece_checker_data::torrent_ptr, _1))));
+ m_torrents.clear();
+ std::for_each(m_processing.begin(), m_processing.end()
+ , boost::bind(&torrent::abort
+ , boost::bind(&shared_ptr<torrent>::get
+ , boost::bind(&piece_checker_data::torrent_ptr, _1))));
+ m_processing.clear();
+ return;
+ }
+
+ if (!m_torrents.empty())
+ {
+ t = m_torrents.front();
+ if (t->abort)
+ {
+ // make sure the locking order is
+ // consistent to avoid dead locks
+ // we need to lock the session because closing
+ // torrents assume to have access to it
+ l.unlock();
+ session_impl::mutex_t::scoped_lock l2(m_ses.m_mutex);
+ l.lock();
+
+ t->torrent_ptr->abort();
+ m_torrents.pop_front();
+ continue;
+ }
+ }
+ }
+
+ if (t)
+ {
+ std::string error_msg;
+ t->parse_resume_data(t->resume_data, t->torrent_ptr->torrent_file()
+ , error_msg);
+
+ if (!error_msg.empty() && m_ses.m_alerts.should_post(alert::warning))
+ {
+ session_impl::mutex_t::scoped_lock l(m_ses.m_mutex);
+ m_ses.m_alerts.post_alert(fastresume_rejected_alert(
+ t->torrent_ptr->get_handle()
+ , error_msg));
+#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
+ (*m_ses.m_logger) << "fastresume data for "
+ << t->torrent_ptr->torrent_file().name() << " rejected: "
+ << error_msg << "\n";
+#endif
+ }
+
+ // clear the resume data now that it has been used
+ // (the fast resume data is now parsed and stored in t)
+ t->resume_data = entry();
+ bool up_to_date = t->torrent_ptr->check_fastresume(*t);
+
+ if (up_to_date)
+ {
+ // lock the session to add the new torrent
+ session_impl::mutex_t::scoped_lock l(m_ses.m_mutex);
+ mutex::scoped_lock l2(m_mutex);
+ INVARIANT_CHECK;
+
+ assert(m_torrents.front() == t);
+
+ t->torrent_ptr->files_checked(t->unfinished_pieces);
+ m_torrents.pop_front();
+
+ // we cannot add the torrent if the session is aborted.
+ if (!m_ses.is_aborted())
+ {
+ m_ses.m_torrents.insert(std::make_pair(t->info_hash, t->torrent_ptr));
+ if (t->torrent_ptr->is_seed() && m_ses.m_alerts.should_post(alert::info))
+ {
+ m_ses.m_alerts.post_alert(torrent_finished_alert(
+ t->torrent_ptr->get_handle()
+ , "torrent is complete"));
+ }
+
+ peer_id id;
+ std::fill(id.begin(), id.end(), 0);
+ for (std::vector<tcp::endpoint>::const_iterator i = t->peers.begin();
+ i != t->peers.end(); ++i)
+ {
+ t->torrent_ptr->get_policy().peer_from_tracker(*i, id);
+ }
+ }
+ else
+ {
+ t->torrent_ptr->abort();
+ }
+ t.reset();
+ continue;
+ }
+
+ // lock the checker while we move the torrent from
+ // m_torrents to m_processing
+ {
+ mutex::scoped_lock l(m_mutex);
+ assert(m_torrents.front() == t);
+
+ m_torrents.pop_front();
+ m_processing.push_back(t);
+ if (!processing)
+ {
+ processing = t;
+ processing->processing = true;
+ t.reset();
+ }
+ }
+ }
+ }
+ catch (const std::exception& e)
+ {
+ // This will happen if the storage fails to initialize
+ session_impl::mutex_t::scoped_lock l(m_ses.m_mutex);
+ mutex::scoped_lock l2(m_mutex);
+
+ if (m_ses.m_alerts.should_post(alert::fatal))
+ {
+ m_ses.m_alerts.post_alert(
+ file_error_alert(
+ t->torrent_ptr->get_handle()
+ , e.what()));
+ }
+ t->torrent_ptr->abort();
+
+ assert(!m_torrents.empty());
+ m_torrents.pop_front();
+ }
+ catch(...)
+ {
+#ifndef NDEBUG
+ std::cerr << "error while checking resume data\n";
+#endif
+ mutex::scoped_lock l(m_mutex);
+ assert(!m_torrents.empty());
+ m_torrents.pop_front();
+ assert(false);
+ }
+
+ if (!processing) continue;
+
+ try
+ {
+ assert(processing);
+
+ float finished = false;
+ float progress = 0.f;
+ boost::tie(finished, progress) = processing->torrent_ptr->check_files();
+
+ {
+ mutex::scoped_lock l(m_mutex);
+
+ INVARIANT_CHECK;
+
+ processing->progress = progress;
+ if (processing->abort)
+ {
+ assert(!m_processing.empty());
+ assert(m_processing.front() == processing);
+
+ processing->torrent_ptr->abort();
+
+ processing.reset();
+ m_processing.pop_front();
+ if (!m_processing.empty())
+ {
+ processing = m_processing.front();
+ processing->processing = true;
+ }
+ continue;
+ }
+ }
+ if (finished)
+ {
+ // lock the session to add the new torrent
+ session_impl::mutex_t::scoped_lock l(m_ses.m_mutex);
+ mutex::scoped_lock l2(m_mutex);
+
+ INVARIANT_CHECK;
+
+ assert(!m_processing.empty());
+ assert(m_processing.front() == processing);
+
+ // TODO: factor out the adding of torrents to the session
+ // and to the checker thread to avoid duplicating the
+ // check for abortion.
+ if (!m_ses.is_aborted())
+ {
+ processing->torrent_ptr->files_checked(processing->unfinished_pieces);
+ m_ses.m_torrents.insert(std::make_pair(
+ processing->info_hash, processing->torrent_ptr));
+ if (processing->torrent_ptr->is_seed()
+ && m_ses.m_alerts.should_post(alert::info))
+ {
+ m_ses.m_alerts.post_alert(torrent_finished_alert(
+ processing->torrent_ptr->get_handle()
+ , "torrent is complete"));
+ }
+
+ peer_id id;
+ std::fill(id.begin(), id.end(), 0);
+ for (std::vector<tcp::endpoint>::const_iterator i = processing->peers.begin();
+ i != processing->peers.end(); ++i)
+ {
+ processing->torrent_ptr->get_policy().peer_from_tracker(*i, id);
+ }
+ }
+ else
+ {
+ processing->torrent_ptr->abort();
+ }
+ processing.reset();
+ m_processing.pop_front();
+ if (!m_processing.empty())
+ {
+ processing = m_processing.front();
+ processing->processing = true;
+ }
+ }
+ }
+ catch(std::exception const& e)
+ {
+ // This will happen if the storage fails to initialize
+ session_impl::mutex_t::scoped_lock l(m_ses.m_mutex);
+ mutex::scoped_lock l2(m_mutex);
+
+ if (m_ses.m_alerts.should_post(alert::fatal))
+ {
+ m_ses.m_alerts.post_alert(
+ file_error_alert(
+ processing->torrent_ptr->get_handle()
+ , e.what()));
+ }
+ assert(!m_processing.empty());
+
+ processing->torrent_ptr->abort();
+
+ processing.reset();
+ m_processing.pop_front();
+ if (!m_processing.empty())
+ {
+ processing = m_processing.front();
+ processing->processing = true;
+ }
+ }
+ catch(...)
+ {
+#ifndef NDEBUG
+ std::cerr << "error while checking files\n";
+#endif
+ mutex::scoped_lock l(m_mutex);
+ assert(!m_processing.empty());
+
+ processing.reset();
+ m_processing.pop_front();
+ if (!m_processing.empty())
+ {
+ processing = m_processing.front();
+ processing->processing = true;
+ }
+
+ assert(false);
+ }
+ }
+ }
+
+ aux::piece_checker_data* checker_impl::find_torrent(sha1_hash const& info_hash)
+ {
+ INVARIANT_CHECK;
+ for (std::deque<boost::shared_ptr<piece_checker_data> >::iterator i
+ = m_torrents.begin(); i != m_torrents.end(); ++i)
+ {
+ if ((*i)->info_hash == info_hash) return i->get();
+ }
+ 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();
+ }
+
+ return 0;
+ }
+
+ void checker_impl::remove_torrent(sha1_hash const& info_hash)
+ {
+ INVARIANT_CHECK;
+ for (std::deque<boost::shared_ptr<piece_checker_data> >::iterator i
+ = m_torrents.begin(); i != m_torrents.end(); ++i)
+ {
+ if ((*i)->info_hash == info_hash)
+ {
+ assert((*i)->processing == false);
+ m_torrents.erase(i);
+ return;
+ }
+ }
+ 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)
+ {
+ assert((*i)->processing == false);
+ m_processing.erase(i);
+ return;
+ }
+ }
+
+ assert(false);
+ }
+
+#ifndef NDEBUG
+ void checker_impl::check_invariant() const
+ {
+ for (std::deque<boost::shared_ptr<piece_checker_data> >::const_iterator i
+ = m_torrents.begin(); i != m_torrents.end(); ++i)
+ {
+ assert(*i);
+ assert((*i)->torrent_ptr);
+ }
+ for (std::deque<boost::shared_ptr<piece_checker_data> >::const_iterator i
+ = m_processing.begin(); i != m_processing.end(); ++i)
+ {
+ assert(*i);
+ assert((*i)->torrent_ptr);
+ }
+ }
+#endif
+
+ session_impl::session_impl(
+ std::pair<int, int> listen_port_range
+ , fingerprint const& cl_fprint
+ , char const* listen_interface)
+ : m_tracker_manager(m_settings)
+ , m_listen_port_range(listen_port_range)
+ , m_listen_interface(address::from_string(listen_interface), listen_port_range.first)
+ , m_abort(false)
+ , m_upload_rate(-1)
+ , m_download_rate(-1)
+ , m_max_uploads(-1)
+ , m_max_connections(-1)
+ , m_half_open_limit(-1)
+ , m_incoming_connection(false)
+ , m_last_tick(microsec_clock::universal_time())
+ , m_timer(m_selector)
+ , m_checker_impl(*this)
+ {
+
+#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
+ m_logger = create_log("main_session", false);
+ using boost::posix_time::second_clock;
+ using boost::posix_time::to_simple_string;
+ (*m_logger) << to_simple_string(second_clock::universal_time()) << "\n";
+#endif
+ std::fill(m_extension_enabled, m_extension_enabled
+ + num_supported_extensions, true);
+ // ---- generate a peer id ----
+
+ std::srand((unsigned int)std::time(0));
+
+ m_key = rand() + (rand() << 15) + (rand() << 30);
+ std::string print = cl_fprint.to_string();
+ assert(print.length() <= 20);
+
+ // the client's fingerprint
+ std::copy(
+ print.begin()
+ , print.begin() + print.length()
+ , m_peer_id.begin());
+
+ // http-accepted characters:
+ static char const printable[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "abcdefghijklmnopqrstuvwxyz-_.!~*'()";
+
+ // the random number
+ for (unsigned char* i = m_peer_id.begin() + print.length();
+ i != m_peer_id.end(); ++i)
+ {
+ *i = printable[rand() % (sizeof(printable)-1)];
+ }
+
+ m_timer.expires_from_now(seconds(1));
+ m_timer.async_wait(bind(&session_impl::second_tick, this, _1));
+
+ m_thread.reset(new boost::thread(boost::ref(*this)));
+ m_checker_thread.reset(new boost::thread(boost::ref(m_checker_impl)));
+ }
+
+#ifndef TORRENT_DISABLE_DHT
+ void session_impl::add_dht_node(udp::endpoint n)
+ {
+ if (m_dht) m_dht->add_node(n);
+ }
+#endif
+
+ void session_impl::abort()
+ {
+ mutex_t::scoped_lock l(m_mutex);
+ assert(!m_abort);
+ // abort the main thread
+ m_abort = true;
+ m_selector.interrupt();
+ l.unlock();
+
+ mutex::scoped_lock l2(m_checker_impl.m_mutex);
+ // abort the checker thread
+ m_checker_impl.m_abort = true;
+ }
+
+ void session_impl::set_ip_filter(ip_filter const& f)
+ {
+ mutex_t::scoped_lock l(m_mutex);
+ m_ip_filter = f;
+
+ // Close connections whose endpoint is filtered
+ // by the new ip-filter
+ for (session_impl::connection_map::iterator i
+ = m_connections.begin(); i != m_connections.end();)
+ {
+ tcp::endpoint sender = i->first->remote_endpoint();
+ if (m_ip_filter.access(sender.address()) & ip_filter::blocked)
+ {
+#if defined(TORRENT_VERBOSE_LOGGING)
+ (*i->second->m_logger) << "*** CONNECTION FILTERED\n";
+#endif
+ session_impl::connection_map::iterator j = i;
+ ++i;
+ j->second->disconnect();
+ }
+ else ++i;
+ }
+ }
+
+ bool session_impl::extensions_enabled() const
+ {
+ const int n = num_supported_extensions;
+ return std::find(m_extension_enabled
+ , m_extension_enabled + n, true) != m_extension_enabled + n;
+ }
+
+ void session_impl::set_settings(session_settings const& s)
+ {
+ mutex_t::scoped_lock l(m_mutex);
+ m_settings = s;
+ // replace all occurances of '\n' with ' '.
+ std::string::iterator i = m_settings.user_agent.begin();
+ while ((i = std::find(i, m_settings.user_agent.end(), '\n'))
+ != m_settings.user_agent.end())
+ *i = ' ';
+ }
+
+ void session_impl::open_listen_port()
+ {
+ try
+ {
+ // create listener socket
+ m_listen_socket = boost::shared_ptr<socket_acceptor>(new socket_acceptor(m_selector));
+
+ for(;;)
+ {
+ try
+ {
+ m_listen_socket->open(asio::ip::tcp::v4());
+ m_listen_socket->bind(m_listen_interface);
+ m_listen_socket->listen();
+ break;
+ }
+ catch (asio::error& e)
+ {
+ // TODO: make sure this is correct
+ if (e.code() == asio::error::host_not_found)
+ {
+ if (m_alerts.should_post(alert::fatal))
+ {
+ std::string msg = "cannot listen on the given interface '"
+ + m_listen_interface.address().to_string() + "'";
+ m_alerts.post_alert(listen_failed_alert(msg));
+ }
+#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
+ std::string msg = "cannot listen on the given interface '"
+ + m_listen_interface.address().to_string() + "'";
+ (*m_logger) << msg << "\n";
+#endif
+ assert(m_listen_socket.unique());
+ m_listen_socket.reset();
+ break;
+ }
+ m_listen_interface.port(m_listen_interface.port() + 1);
+ if (m_listen_interface.port() > m_listen_port_range.second)
+ {
+ std::stringstream msg;
+ msg << "none of the ports in the range ["
+ << m_listen_port_range.first
+ << ", " << m_listen_port_range.second
+ << "] could be opened for listening";
+ m_alerts.post_alert(listen_failed_alert(msg.str()));
+#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
+ (*m_logger) << msg.str() << "\n";
+#endif
+ m_listen_socket.reset();
+ break;
+ }
+ }
+ }
+ }
+ catch (asio::error& e)
+ {
+ if (m_alerts.should_post(alert::fatal))
+ {
+ m_alerts.post_alert(listen_failed_alert(
+ std::string("failed to open listen port") + e.what()));
+ }
+ }
+
+#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
+ if (m_listen_socket)
+ {
+ (*m_logger) << "listening on port: " << m_listen_interface.port() << "\n";
+ }
+#endif
+ if (m_listen_socket) async_accept();
+ }
+
+ void session_impl::process_connection_queue()
+ {
+ while (!m_connection_queue.empty())
+ {
+ if ((int)m_half_open.size() >= m_half_open_limit
+ && m_half_open_limit > 0)
+ return;
+
+ connection_queue::value_type c = m_connection_queue.front();
+
+ try
+ {
+ m_connection_queue.pop_front();
+ assert(c->associated_torrent().lock().get());
+ c->connect();
+ m_half_open.insert(std::make_pair(c->get_socket(), c));
+ }
+ catch (std::exception& e)
+ {
+ c->disconnect();
+
+#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
+ (*m_logger) << "connect failed [" << c->remote() << "]: "
+ << e.what() << "\n";
+#endif
+ }
+ }
+ }
+
+ void session_impl::async_accept()
+ {
+ shared_ptr<stream_socket> c(new stream_socket(m_selector));
+ m_listen_socket->async_accept(*c
+ , bind(&session_impl::on_incoming_connection, this, c
+ , weak_ptr<socket_acceptor>(m_listen_socket), _1));
+ }
+
+ void session_impl::on_incoming_connection(shared_ptr<stream_socket> const& s
+ , weak_ptr<socket_acceptor> const& listen_socket, asio::error const& e) try
+ {
+ if (listen_socket.expired())
+ return;
+
+ if (e == asio::error::operation_aborted)
+ return;
+
+ mutex_t::scoped_lock l(m_mutex);
+ assert(listen_socket.lock() == m_listen_socket);
+
+ if (m_abort) return;
+
+ async_accept();
+ if (e)
+ {
+#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
+ std::string msg = "error accepting connection on '"
+ + m_listen_interface.address().to_string() + "'";
+ (*m_logger) << msg << "\n";
+#endif
+ assert(m_listen_socket.unique());
+ return;
+ }
+
+ // we got a connection request!
+ m_incoming_connection = true;
+ tcp::endpoint endp = s->remote_endpoint();
+
+#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
+ (*m_logger) << endp << " <== INCOMING CONNECTION\n";
+#endif
+ if (m_ip_filter.access(endp.address().to_v4()) & ip_filter::blocked)
+ {
+#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
+ (*m_logger) << "filtered blocked ip\n";
+#endif
+ // TODO: issue an info-alert when an ip is blocked!!
+ return;
+ }
+
+ boost::intrusive_ptr<peer_connection> c(
+ new bt_peer_connection(*this, s));
+#ifndef NDEBUG
+ c->m_in_constructor = false;
+#endif
+
+ m_connections.insert(std::make_pair(s, c));
+ }
+ catch (std::exception& exc)
+ {
+#ifndef NDEBUG
+ std::string err = exc.what();
+#endif
+ }
+
+ void session_impl::connection_failed(boost::shared_ptr<stream_socket> const& s
+ , tcp::endpoint const& a, char const* message)
+#ifndef NDEBUG
+ try
+#endif
+ {
+ mutex_t::scoped_lock l(m_mutex);
+
+ connection_map::iterator p = m_connections.find(s);
+
+ // the connection may have been disconnected in the receive or send phase
+ if (p != m_connections.end())
+ {
+ if (m_alerts.should_post(alert::debug))
+ {
+ m_alerts.post_alert(
+ peer_error_alert(
+ a
+ , p->second->pid()
+ , message));
+ }
+
+#if defined(TORRENT_VERBOSE_LOGGING)
+ (*p->second->m_logger) << "*** CONNECTION FAILED " << message << "\n";
+#endif
+ p->second->set_failed();
+ p->second->disconnect();
+ }
+ else
+ {
+ // the error was not in one of the connected
+ // conenctions. Look among the half-open ones.
+ p = m_half_open.find(s);
+ if (p != m_half_open.end())
+ {
+ if (m_alerts.should_post(alert::debug))
+ {
+ m_alerts.post_alert(
+ peer_error_alert(
+ a
+ , p->second->pid()
+ , message));
+ }
+#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
+ (*m_logger) << "CLOSED: " << a.address().to_string()
+ << " " << message << "\n";
+#endif
+ p->second->set_failed();
+ p->second->disconnect();
+ }
+ }
+ }
+#ifndef NDEBUG
+ catch (...)
+ {
+ assert(false);
+ };
+#endif
+
+ void session_impl::close_connection(boost::intrusive_ptr<peer_connection> const& p)
+ {
+ mutex_t::scoped_lock l(m_mutex);
+
+ assert(p->is_disconnecting());
+
+ if (p->is_connecting())
+ {
+ assert(p->is_local());
+ assert(m_connections.find(p->get_socket()) == m_connections.end());
+ // Since this peer is still connecting, will not be
+ // in the list of completed connections.
+ connection_map::iterator i = m_half_open.find(p->get_socket());
+ if (i == m_half_open.end())
+ {
+ // this connection is not in the half-open list, so it
+ // has to be in the queue, waiting to be connected.
+ connection_queue::iterator j = std::find(
+ m_connection_queue.begin(), m_connection_queue.end(), p);
+
+ // if this connection was closed while being connected
+ // it has been removed from the connection queue and
+ // not yet put into the half-open queue.
+ if (j != m_connection_queue.end())
+ m_connection_queue.erase(j);
+ }
+ else
+ {
+ m_half_open.erase(i);
+ process_connection_queue();
+ }
+ }
+ else
+ {
+ assert(m_half_open.find(p->get_socket()) == m_half_open.end());
+ assert(std::find(m_connection_queue.begin()
+ , m_connection_queue.end(), p) == m_connection_queue.end());
+ connection_map::iterator i = m_connections.find(p->get_socket());
+// assert (i != m_connections.end());
+ if (i != m_connections.end())
+ m_connections.erase(i);
+ }
+ }
+
+ void session_impl::set_peer_id(peer_id const& id)
+ {
+ mutex_t::scoped_lock l(m_mutex);
+ m_peer_id = id;
+ }
+
+ void session_impl::set_key(int key)
+ {
+ mutex_t::scoped_lock l(m_mutex);
+ m_key = key;
+ }
+
+ void session_impl::second_tick(asio::error const& e) try
+ {
+ session_impl::mutex_t::scoped_lock l(m_mutex);
+
+ if (e)
+ {
+#if defined(TORRENT_LOGGING)
+ (*m_logger) << "*** SECOND TIMER FAILED " << e.what() << "\n";
+#endif
+ m_abort = true;
+ m_selector.interrupt();
+ return;
+ }
+
+ if (m_abort) return;
+ float tick_interval = (microsec_clock::universal_time()
+ - m_last_tick).total_milliseconds() / 1000.f;
+ m_last_tick = microsec_clock::universal_time();
+
+ m_timer.expires_from_now(seconds(1));
+ m_timer.async_wait(bind(&session_impl::second_tick, this, _1));
+
+ // do the second_tick() on each connection
+ // this will update their statistics (download and upload speeds)
+ // also purge sockets that have timed out
+ // and keep sockets open by keeping them alive.
+ for (connection_map::iterator i = m_connections.begin();
+ i != m_connections.end();)
+ {
+ // we need to do like this because j->second->disconnect() will
+ // erase the connection from the map we're iterating
+ connection_map::iterator j = i;
+ ++i;
+ // if this socket has timed out
+ // close it.
+ peer_connection& c = *j->second;
+ if (c.has_timed_out())
+ {
+ if (m_alerts.should_post(alert::debug))
+ {
+ m_alerts.post_alert(
+ peer_error_alert(
+ c.remote()
+ , c.pid()
+ , "connection timed out"));
+ }
+#if defined(TORRENT_VERBOSE_LOGGING)
+ (*c.m_logger) << "*** CONNECTION TIMED OUT\n";
+#endif
+
+ c.set_failed();
+ c.disconnect();
+ continue;
+ }
+
+ c.keep_alive();
+ }
+
+ // check each torrent for tracker updates
+ // TODO: do this in a timer-event in each torrent instead
+ for (std::map<sha1_hash, boost::shared_ptr<torrent> >::iterator i
+ = m_torrents.begin(); i != m_torrents.end();)
+ {
+ torrent& t = *i->second;
+ assert(!t.is_aborted());
+ if (t.should_request())
+ {
+ tracker_request req = t.generate_tracker_request();
+ req.listen_port = m_listen_interface.port();
+ req.key = m_key;
+ m_tracker_manager.queue_request(m_selector, req, t.tracker_login()
+ , i->second);
+
+ if (m_alerts.should_post(alert::info))
+ {
+ m_alerts.post_alert(
+ tracker_announce_alert(
+ t.get_handle(), "tracker announce"));
+ }
+ }
+
+ // second_tick() will set the used upload quota
+ t.second_tick(m_stat, tick_interval);
+ ++i;
+ }
+
+ m_stat.second_tick(tick_interval);
+
+ // distribute the maximum upload rate among the torrents
+
+ assert(m_upload_rate >= -1);
+ assert(m_download_rate >= -1);
+ assert(m_max_uploads >= -1);
+ assert(m_max_connections >= -1);
+
+ allocate_resources(m_upload_rate == -1
+ ? std::numeric_limits<int>::max()
+ : int(m_upload_rate * tick_interval)
+ , m_torrents
+ , &torrent::m_ul_bandwidth_quota);
+
+ allocate_resources(m_download_rate == -1
+ ? std::numeric_limits<int>::max()
+ : int(m_download_rate * tick_interval)
+ , m_torrents
+ , &torrent::m_dl_bandwidth_quota);
+
+ allocate_resources(m_max_uploads == -1
+ ? std::numeric_limits<int>::max()
+ : m_max_uploads
+ , m_torrents
+ , &torrent::m_uploads_quota);
+
+ allocate_resources(m_max_connections == -1
+ ? std::numeric_limits<int>::max()
+ : m_max_connections
+ , m_torrents
+ , &torrent::m_connections_quota);
+
+ for (std::map<sha1_hash, boost::shared_ptr<torrent> >::iterator i
+ = m_torrents.begin(); i != m_torrents.end(); ++i)
+ {
+#ifndef NDEBUG
+ i->second->check_invariant();
+#endif
+ i->second->distribute_resources();
+ }
+ }
+ catch (std::exception& exc)
+ {
+#ifndef NDEBUG
+ std::string err = exc.what();
+#endif
+ }; // msvc 7.1 seems to require this
+
+ void session_impl::connection_completed(
+ boost::intrusive_ptr<peer_connection> const& p)
+#ifndef NDEBUG
+ try
+#endif
+ {
+ mutex_t::scoped_lock l(m_mutex);
+
+ if (m_abort) return;
+
+ connection_map::iterator i = m_half_open.find(p->get_socket());
+
+ m_connections.insert(std::make_pair(p->get_socket(), p));
+ if (i != m_half_open.end()) m_half_open.erase(i);
+ process_connection_queue();
+ }
+#ifndef NDEBUG
+ catch (std::exception& e)
+ {
+ assert(false);
+ };
+#endif
+
+ void session_impl::operator()()
+ {
+ eh_initializer();
+
+ if (m_listen_port_range.first != 0 && m_listen_port_range.second != 0)
+ {
+ session_impl::mutex_t::scoped_lock l(m_mutex);
+ open_listen_port();
+ }
+
+ boost::posix_time::ptime timer = second_clock::universal_time();
+
+ do
+ {
+ try
+ {
+ m_selector.run();
+ assert(m_abort == true);
+ }
+ catch (std::exception& e)
+ {
+ #ifndef NDEBUG
+ std::cerr << e.what() << "\n";
+ std::string err = e.what();
+ #endif
+ assert(false);
+ }
+ }
+ while (!m_abort);
+
+ deadline_timer tracker_timer(m_selector);
+
+ session_impl::mutex_t::scoped_lock l(m_mutex);
+
+ m_tracker_manager.abort_all_requests();
+ for (std::map<sha1_hash, boost::shared_ptr<torrent> >::iterator i =
+ m_torrents.begin(); i != m_torrents.end(); ++i)
+ {
+ i->second->abort();
+ if (!i->second->is_paused() || i->second->should_request())
+ {
+ tracker_request req = i->second->generate_tracker_request();
+ req.listen_port = m_listen_interface.port();
+ req.key = m_key;
+ std::string login = i->second->tracker_login();
+ m_tracker_manager.queue_request(m_selector, req, login);
+ }
+ }
+
+ ptime start(microsec_clock::universal_time());
+ l.unlock();
+
+ while (microsec_clock::universal_time() - start < seconds(
+ m_settings.stop_tracker_timeout)
+ && !m_tracker_manager.empty())
+ {
+ tracker_timer.expires_from_now(boost::posix_time::milliseconds(100));
+ tracker_timer.async_wait(bind(&demuxer::interrupt, &m_selector));
+
+ m_selector.reset();
+ m_selector.run();
+ }
+
+ l.lock();
+ assert(m_abort);
+ m_abort = true;
+
+ while (!m_connections.empty())
+ m_connections.begin()->second->disconnect();
+
+ while (!m_half_open.empty())
+ m_half_open.begin()->second->disconnect();
+
+ m_connection_queue.clear();
+
+#ifndef NDEBUG
+ for (torrent_map::iterator i = m_torrents.begin();
+ i != m_torrents.end(); ++i)
+ {
+ assert(i->second->num_peers() == 0);
+ }
+#endif
+
+ m_torrents.clear();
+
+ assert(m_torrents.empty());
+ assert(m_connections.empty());
+ }
+
+
+ // the return value from this function is valid only as long as the
+ // session is locked!
+ boost::weak_ptr<torrent> session_impl::find_torrent(sha1_hash const& info_hash)
+ {
+ std::map<sha1_hash, boost::shared_ptr<torrent> >::iterator i
+ = m_torrents.find(info_hash);
+#ifndef NDEBUG
+ for (std::map<sha1_hash, boost::shared_ptr<torrent> >::iterator j
+ = m_torrents.begin(); j != m_torrents.end(); ++j)
+ {
+ torrent* p = boost::get_pointer(j->second);
+ assert(p);
+ }
+#endif
+ if (i != m_torrents.end()) return i->second;
+ return boost::weak_ptr<torrent>();
+ }
+
+#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
+ boost::shared_ptr<logger> session_impl::create_log(std::string const& name, bool append)
+ {
+ // current options are file_logger, cout_logger and null_logger
+ return boost::shared_ptr<logger>(new logger(name + ".log", append));
+ }
+#endif
+
+ void session_impl::disable_extensions()
+ {
+ mutex_t::scoped_lock l(m_mutex);
+ std::fill(m_extension_enabled, m_extension_enabled
+ + num_supported_extensions, false);
+ }
+
+ void session_impl::enable_extension(extension_index i)
+ {
+ assert(i >= 0);
+ assert(i < num_supported_extensions);
+ mutex_t::scoped_lock l(m_mutex);
+ m_extension_enabled[i] = true;
+ }
+
+ std::vector<torrent_handle> session_impl::get_torrents()
+ {
+ mutex_t::scoped_lock l(m_mutex);
+ mutex::scoped_lock l2(m_checker_impl.m_mutex);
+ std::vector<torrent_handle> ret;
+ for (std::deque<boost::shared_ptr<aux::piece_checker_data> >::iterator i
+ = m_checker_impl.m_torrents.begin()
+ , end(m_checker_impl.m_torrents.end()); i != end; ++i)
+ {
+ if ((*i)->abort) continue;
+ ret.push_back(torrent_handle(this, &m_checker_impl
+ , (*i)->info_hash));
+ }
+
+ for (session_impl::torrent_map::iterator i
+ = m_torrents.begin(), end(m_torrents.end());
+ i != end; ++i)
+ {
+ if (i->second->is_aborted()) continue;
+ ret.push_back(torrent_handle(this, &m_checker_impl
+ , i->first));
+ }
+ return ret;
+ }
+
+ torrent_handle session_impl::add_torrent(
+ torrent_info const& ti
+ , boost::filesystem::path const& save_path
+ , entry const& resume_data
+ , bool compact_mode
+ , int block_size)
+ {
+ // make sure the block_size is an even power of 2
+#ifndef NDEBUG
+ for (int i = 0; i < 32; ++i)
+ {
+ if (block_size & (1 << i))
+ {
+ assert((block_size & ~(1 << i)) == 0);
+ break;
+ }
+ }
+#endif
+
+ assert(!save_path.empty());
+
+ if (ti.begin_files() == ti.end_files())
+ throw std::runtime_error("no files in torrent");
+
+ // lock the session and the checker thread (the order is important!)
+ mutex_t::scoped_lock l(m_mutex);
+ mutex::scoped_lock l2(m_checker_impl.m_mutex);
+
+ if (is_aborted())
+ throw std::runtime_error("session is closing");
+
+ // is the torrent already active?
+ if (!find_torrent(ti.info_hash()).expired())
+ throw duplicate_torrent();
+
+ // is the torrent currently being checked?
+ if (m_checker_impl.find_torrent(ti.info_hash()))
+ throw duplicate_torrent();
+
+ // create the torrent and the data associated with
+ // the checker thread and store it before starting
+ // the thread
+ boost::shared_ptr<torrent> torrent_ptr(
+ new torrent(*this, m_checker_impl, ti, save_path
+ , m_listen_interface, compact_mode, block_size
+ , settings()));
+
+ boost::shared_ptr<aux::piece_checker_data> d(
+ new aux::piece_checker_data);
+ d->torrent_ptr = torrent_ptr;
+ d->save_path = save_path;
+ d->info_hash = ti.info_hash();
+ d->resume_data = resume_data;
+
+#ifndef TORRENT_DISABLE_DHT
+ if (m_dht)
+ {
+ torrent_info::nodes_t const& nodes = ti.nodes();
+ std::for_each(nodes.begin(), nodes.end(), bind(
+ (void(dht::dht_tracker::*)(std::pair<std::string, int> const&))
+ &dht::dht_tracker::add_node
+ , boost::ref(m_dht), _1));
+ }
+#endif
+
+ // add the torrent to the queue to be checked
+ m_checker_impl.m_torrents.push_back(d);
+ // and notify the thread that it got another
+ // job in its queue
+ m_checker_impl.m_cond.notify_one();
+
+ return torrent_handle(this, &m_checker_impl, ti.info_hash());
+ }
+
+ torrent_handle session_impl::add_torrent(
+ char const* tracker_url
+ , sha1_hash const& info_hash
+ , boost::filesystem::path const& save_path
+ , entry const&
+ , bool compact_mode
+ , int block_size)
+ {
+ // make sure the block_size is an even power of 2
+#ifndef NDEBUG
+ for (int i = 0; i < 32; ++i)
+ {
+ if (block_size & (1 << i))
+ {
+ assert((block_size & ~(1 << i)) == 0);
+ break;
+ }
+ }
+#endif
+
+ // TODO: support resume data in this case
+ assert(!save_path.empty());
+ {
+ // lock the checker_thread
+ mutex::scoped_lock l(m_checker_impl.m_mutex);
+
+ // is the torrent currently being checked?
+ if (m_checker_impl.find_torrent(info_hash))
+ throw duplicate_torrent();
+ }
+
+ // lock the session
+ session_impl::mutex_t::scoped_lock l(m_mutex);
+
+ // the metadata extension has to be enabled for this to work
+ assert(m_extension_enabled
+ [extended_metadata_message]);
+
+ // is the torrent already active?
+ if (!find_torrent(info_hash).expired())
+ throw duplicate_torrent();
+
+ // you cannot add new torrents to a session that is closing down
+ assert(!is_aborted());
+
+ // create the torrent and the data associated with
+ // the checker thread and store it before starting
+ // the thread
+ boost::shared_ptr<torrent> torrent_ptr(
+ new torrent(*this, m_checker_impl, tracker_url, info_hash, save_path
+ , m_listen_interface, compact_mode, block_size
+ , settings()));
+
+ m_torrents.insert(
+ std::make_pair(info_hash, torrent_ptr)).first;
+
+ return torrent_handle(this, &m_checker_impl, info_hash);
+ }
+
+ void session_impl::remove_torrent(const torrent_handle& h)
+ {
+ if (h.m_ses != this) return;
+ assert(h.m_chk == &m_checker_impl || h.m_chk == 0);
+ assert(h.m_ses != 0);
+
+ mutex_t::scoped_lock l(m_mutex);
+ session_impl::torrent_map::iterator i =
+ m_torrents.find(h.m_info_hash);
+ if (i != m_torrents.end())
+ {
+ torrent& t = *i->second;
+ t.abort();
+
+ if (!t.is_paused() || t.should_request())
+ {
+ tracker_request req = t.generate_tracker_request();
+ assert(req.event == tracker_request::stopped);
+ req.listen_port = m_listen_interface.port();
+ req.key = m_key;
+ m_tracker_manager.queue_request(m_selector, req
+ , t.tracker_login());
+
+ if (m_alerts.should_post(alert::info))
+ {
+ m_alerts.post_alert(
+ tracker_announce_alert(
+ t.get_handle(), "tracker announce, event=stopped"));
+ }
+ }
+#ifndef NDEBUG
+ sha1_hash i_hash = t.torrent_file().info_hash();
+#endif
+ m_torrents.erase(i);
+ assert(m_torrents.find(i_hash) == m_torrents.end());
+ return;
+ }
+ l.unlock();
+
+ if (h.m_chk)
+ {
+ mutex::scoped_lock l(m_checker_impl.m_mutex);
+
+ aux::piece_checker_data* d = m_checker_impl.find_torrent(h.m_info_hash);
+ if (d != 0)
+ {
+ if (d->processing) d->abort = true;
+ else m_checker_impl.remove_torrent(h.m_info_hash);
+ return;
+ }
+ }
+ }
+
+ bool session_impl::listen_on(
+ std::pair<int, int> const& port_range
+ , const char* net_interface)
+ {
+ session_impl::mutex_t::scoped_lock l(m_mutex);
+
+ tcp::endpoint new_interface;
+ if (net_interface && std::strlen(net_interface) > 0)
+ new_interface = tcp::endpoint(address::from_string(net_interface), port_range.first);
+ else
+ new_interface = tcp::endpoint(address(), port_range.first);
+
+ m_listen_port_range = port_range;
+
+ // if the interface is the same and the socket is open
+ // don't do anything
+ if (new_interface == m_listen_interface
+ && m_listen_socket) return true;
+
+ if (m_listen_socket)
+ m_listen_socket.reset();
+
+#ifndef TORRENT_DISABLE_DHT
+ if (m_listen_interface.address() != new_interface.address()
+ && m_dht)
+ {
+ // the listen interface changed, rebind the dht listen socket as well
+ m_dht->rebind(new_interface.address()
+ , m_dht_settings.service_port);
+ }
+#endif
+
+ m_incoming_connection = false;
+ m_listen_interface = new_interface;
+
+ open_listen_port();
+ return m_listen_socket;
+ }
+
+ unsigned short session_impl::listen_port() const
+ {
+ mutex_t::scoped_lock l(m_mutex);
+ return m_listen_interface.port();
+ }
+
+ session_status session_impl::status() const
+ {
+ mutex_t::scoped_lock l(m_mutex);
+ session_status s;
+ s.has_incoming_connections = m_incoming_connection;
+ s.num_peers = (int)m_connections.size();
+
+ s.download_rate = m_stat.download_rate();
+ s.upload_rate = m_stat.upload_rate();
+
+ s.payload_download_rate = m_stat.download_payload_rate();
+ s.payload_upload_rate = m_stat.upload_payload_rate();
+
+ s.total_download = m_stat.total_protocol_download()
+ + m_stat.total_payload_download();
+
+ s.total_upload = m_stat.total_protocol_upload()
+ + m_stat.total_payload_upload();
+
+ s.total_payload_download = m_stat.total_payload_download();
+ s.total_payload_upload = m_stat.total_payload_upload();
+
+#ifndef TORRENT_DISABLE_DHT
+ if (m_dht)
+ {
+ m_dht->dht_status(s);
+ }
+ else
+ {
+ s.m_dht_nodes = 0;
+ s.m_dht_node_cache = 0;
+ s.m_dht_torrents = 0;
+ }
+#endif
+
+ return s;
+ }
+
+#ifndef TORRENT_DISABLE_DHT
+
+ void session_impl::start_dht(entry const& startup_state)
+ {
+ mutex_t::scoped_lock l(m_mutex);
+ m_dht.reset(new dht::dht_tracker(m_selector
+ , m_dht_settings, m_listen_interface.address()
+ , startup_state));
+ }
+
+ void session_impl::stop_dht()
+ {
+ mutex_t::scoped_lock l(m_mutex);
+ m_dht.reset();
+ }
+
+ void session_impl::set_dht_settings(dht_settings const& settings)
+ {
+ mutex_t::scoped_lock l(m_mutex);
+ if (settings.service_port != m_dht_settings.service_port
+ && m_dht)
+ {
+ m_dht->rebind(m_listen_interface.address()
+ , settings.service_port);
+ }
+ m_dht_settings = settings;
+ }
+
+ entry session_impl::dht_state() const
+ {
+ assert(m_dht);
+ mutex_t::scoped_lock l(m_mutex);
+ return m_dht->state();
+ }
+
+ void session_impl::add_dht_node(std::pair<std::string, int> const& node)
+ {
+ assert(m_dht);
+ mutex_t::scoped_lock l(m_mutex);
+ m_dht->add_node(node);
+ }
+
+ void session_impl::add_dht_router(std::pair<std::string, int> const& node)
+ {
+ assert(m_dht);
+ mutex_t::scoped_lock l(m_mutex);
+ m_dht->add_router_node(node);
+ }
+
+#endif
+
+
+ void session_impl::set_download_rate_limit(int bytes_per_second)
+ {
+ assert(bytes_per_second > 0 || bytes_per_second == -1);
+ mutex_t::scoped_lock l(m_mutex);
+ m_download_rate = bytes_per_second;
+ }
+ bool session_impl::is_listening() const
+ {
+ mutex_t::scoped_lock l(m_mutex);
+ return m_listen_socket;
+ }
+
+ session_impl::~session_impl()
+ {
+ {
+ // lock the main thread and abort it
+ mutex_t::scoped_lock l(m_mutex);
+ m_abort = true;
+ m_selector.interrupt();
+ }
+ m_thread->join();
+
+ // it's important that the main thread is closed completely before
+ // the checker thread is terminated. Because all the connections
+ // have to be closed and removed from the torrents before they
+ // can be destructed. (because the weak pointers in the
+ // peer_connections will be invalidated when the torrents are
+ // destructed and then the invariant will be broken).
+
+ {
+ mutex::scoped_lock l(m_checker_impl.m_mutex);
+ // abort the checker thread
+ m_checker_impl.m_abort = true;
+
+ // abort the currently checking torrent
+ if (!m_checker_impl.m_torrents.empty())
+ {
+ m_checker_impl.m_torrents.front()->abort = true;
+ }
+ m_checker_impl.m_cond.notify_one();
+ }
+
+ m_checker_thread->join();
+
+ assert(m_torrents.empty());
+ assert(m_connections.empty());
+ }
+
+ void session_impl::set_max_uploads(int limit)
+ {
+ assert(limit > 0 || limit == -1);
+ mutex_t::scoped_lock l(m_mutex);
+ m_max_uploads = limit;
+ }
+
+ void session_impl::set_max_connections(int limit)
+ {
+ assert(limit > 0 || limit == -1);
+ mutex_t::scoped_lock l(m_mutex);
+ m_max_connections = limit;
+ }
+
+ void session_impl::set_max_half_open_connections(int limit)
+ {
+ assert(limit > 0 || limit == -1);
+ mutex_t::scoped_lock l(m_mutex);
+ m_half_open_limit = limit;
+ }
+
+ void session_impl::set_upload_rate_limit(int bytes_per_second)
+ {
+ assert(bytes_per_second > 0 || bytes_per_second == -1);
+ mutex_t::scoped_lock l(m_mutex);
+ m_upload_rate = bytes_per_second;
+ }
+
+ std::auto_ptr<alert> session_impl::pop_alert()
+ {
+ mutex_t::scoped_lock l(m_mutex);
+ if (m_alerts.pending())
+ return m_alerts.get();
+ return std::auto_ptr<alert>(0);
+ }
+
+ void session_impl::set_severity_level(alert::severity_t s)
+ {
+ mutex_t::scoped_lock l(m_mutex);
+ m_alerts.set_severity(s);
+ }
+
+#ifndef NDEBUG
+ void session_impl::check_invariant(const char *place)
+ {
+ assert(place);
+
+ for (connection_map::iterator i = m_half_open.begin();
+ i != m_half_open.end(); ++i)
+ {
+ assert(i->second->is_connecting());
+ }
+
+ for (connection_map::iterator i = m_connections.begin();
+ i != m_connections.end(); ++i)
+ {
+ assert(i->second);
+ assert(!i->second->is_connecting());
+ if (i->second->is_connecting())
+ {
+ std::ofstream error_log("error.log", std::ios_base::app);
+ boost::intrusive_ptr<peer_connection> p = i->second;
+ error_log << "peer_connection::is_connecting() " << p->is_connecting() << "\n";
+ error_log << "peer_connection::can_write() " << p->can_write() << "\n";
+ error_log << "peer_connection::can_read() " << p->can_read() << "\n";
+ error_log << "peer_connection::ul_quota_left " << p->m_ul_bandwidth_quota.left() << "\n";
+ error_log << "peer_connection::dl_quota_left " << p->m_dl_bandwidth_quota.left() << "\n";
+ error_log << "peer_connection::m_ul_bandwidth_quota.given " << p->m_ul_bandwidth_quota.given << "\n";
+ error_log << "peer_connection::get_peer_id " << p->pid() << "\n";
+ error_log << "place: " << place << "\n";
+ error_log.flush();
+ assert(false);
+ }
+
+ boost::shared_ptr<torrent> t = i->second->associated_torrent().lock();
+
+ if (t)
+ {
+ assert(t->get_policy().has_connection(boost::get_pointer(i->second)));
+ }
+ }
+ }
+#endif
+
+ void piece_checker_data::parse_resume_data(
+ const entry& resume_data
+ , const torrent_info& info
+ , std::string& error)
+ {
+ // if we don't have any resume data, return
+ if (resume_data.type() == entry::undefined_t) return;
+
+ entry rd = resume_data;
+
+ try
+ {
+ if (rd["file-format"].string() != "libtorrent resume file")
+ {
+ error = "missing file format tag";
+ return;
+ }
+
+ if (rd["file-version"].integer() > 1)
+ {
+ error = "incompatible file version "
+ + boost::lexical_cast<std::string>(rd["file-version"].integer());
+ return;
+ }
+
+ // verify info_hash
+ const std::string &hash = rd["info-hash"].string();
+ std::string real_hash((char*)info.info_hash().begin(), (char*)info.info_hash().end());
+ if (hash != real_hash)
+ {
+ error = "mismatching info-hash: " + hash;
+ return;
+ }
+
+ // the peers
+
+ if (rd.find_key("peers"))
+ {
+ entry::list_type& peer_list = rd["peers"].list();
+
+ std::vector<tcp::endpoint> tmp_peers;
+ tmp_peers.reserve(peer_list.size());
+ for (entry::list_type::iterator i = peer_list.begin();
+ i != peer_list.end(); ++i)
+ {
+ tcp::endpoint a(
+ address::from_string((*i)["ip"].string())
+ , (unsigned short)(*i)["port"].integer());
+ tmp_peers.push_back(a);
+ }
+
+ peers.swap(tmp_peers);
+ }
+
+ // read piece map
+ const entry::list_type& slots = rd["slots"].list();
+ if ((int)slots.size() > info.num_pieces())
+ {
+ error = "file has more slots than torrent (slots: "
+ + boost::lexical_cast<std::string>(slots.size()) + " size: "
+ + boost::lexical_cast<std::string>(info.num_pieces()) + " )";
+ return;
+ }
+
+ std::vector<int> tmp_pieces;
+ tmp_pieces.reserve(slots.size());
+ for (entry::list_type::const_iterator i = slots.begin();
+ i != slots.end(); ++i)
+ {
+ int index = (int)i->integer();
+ if (index >= info.num_pieces() || index < -2)
+ {
+ error = "too high index number in slot map (index: "
+ + boost::lexical_cast<std::string>(index) + " size: "
+ + boost::lexical_cast<std::string>(info.num_pieces()) + ")";
+ return;
+ }
+ tmp_pieces.push_back(index);
+ }
+
+ // only bother to check the partial pieces if we have the same block size
+ // as in the fast resume data. If the blocksize has changed, then throw
+ // away all partial pieces.
+ std::vector<piece_picker::downloading_piece> tmp_unfinished;
+ int num_blocks_per_piece = (int)rd["blocks per piece"].integer();
+ if (num_blocks_per_piece == info.piece_length() / torrent_ptr->block_size())
+ {
+ // the unfinished pieces
+
+ entry::list_type& unfinished = rd["unfinished"].list();
+
+ tmp_unfinished.reserve(unfinished.size());
+ for (entry::list_type::iterator i = unfinished.begin();
+ i != unfinished.end(); ++i)
+ {
+ piece_picker::downloading_piece p;
+
+ p.index = (int)(*i)["piece"].integer();
+ if (p.index < 0 || p.index >= info.num_pieces())
+ {
+ error = "invalid piece index in unfinished piece list (index: "
+ + boost::lexical_cast<std::string>(p.index) + " size: "
+ + boost::lexical_cast<std::string>(info.num_pieces()) + ")";
+ return;
+ }
+
+ const std::string& bitmask = (*i)["bitmask"].string();
+
+ const int num_bitmask_bytes = std::max(num_blocks_per_piece / 8, 1);
+ if ((int)bitmask.size() != num_bitmask_bytes)
+ {
+ error = "invalid size of bitmask (" + boost::lexical_cast<std::string>(bitmask.size()) + ")";
+ return;
+ }
+ for (int j = 0; j < num_bitmask_bytes; ++j)
+ {
+ unsigned char bits = bitmask[j];
+ for (int k = 0; k < 8; ++k)
+ {
+ const int bit = j * 8 + k;
+ if (bits & (1 << k))
+ p.finished_blocks[bit] = true;
+ }
+ }
+
+ if (p.finished_blocks.count() == 0) continue;
+
+ std::vector<int>::iterator slot_iter
+ = std::find(tmp_pieces.begin(), tmp_pieces.end(), p.index);
+ if (slot_iter == tmp_pieces.end())
+ {
+ // this piece is marked as unfinished
+ // but doesn't have any storage
+ error = "piece " + boost::lexical_cast<std::string>(p.index) + " is "
+ "marked as unfinished, but doesn't have any storage";
+ return;
+ }
+
+ assert(*slot_iter == p.index);
+ int slot_index = static_cast<int>(slot_iter - tmp_pieces.begin());
+ unsigned long adler
+ = torrent_ptr->filesystem().piece_crc(
+ slot_index
+ , torrent_ptr->block_size()
+ , p.finished_blocks);
+
+ const entry& ad = (*i)["adler32"];
+
+ // crc's didn't match, don't use the resume data
+ if (ad.integer() != adler)
+ {
+ error = "checksum mismatch on piece " + boost::lexical_cast<std::string>(p.index);
+ return;
+ }
+
+ tmp_unfinished.push_back(p);
+ }
+ }
+
+ // verify file sizes
+
+ std::vector<std::pair<size_type, std::time_t> > file_sizes;
+ entry::list_type& l = rd["file sizes"].list();
+
+ for (entry::list_type::iterator i = l.begin();
+ i != l.end(); ++i)
+ {
+ file_sizes.push_back(std::pair<size_type, std::time_t>(
+ i->list().front().integer()
+ , i->list().back().integer()));
+ }
+
+ if ((int)tmp_pieces.size() == info.num_pieces()
+ && std::find_if(tmp_pieces.begin(), tmp_pieces.end()
+ , boost::bind<bool>(std::less<int>(), _1, 0)) == tmp_pieces.end())
+ {
+ if (info.num_files() != (int)file_sizes.size())
+ {
+ error = "the number of files does not match the torrent (num: "
+ + boost::lexical_cast<std::string>(file_sizes.size()) + " actual: "
+ + boost::lexical_cast<std::string>(info.num_files()) + ")";
+ return;
+ }
+
+ std::vector<std::pair<size_type, std::time_t> >::iterator
+ fs = file_sizes.begin();
+ // the resume data says we have the entire torrent
+ // make sure the file sizes are the right ones
+ for (torrent_info::file_iterator i = info.begin_files()
+ , end(info.end_files()); i != end; ++i, ++fs)
+ {
+ if (i->size != fs->first)
+ {
+ error = "file size for '" + i->path.native_file_string() + "' was expected to be "
+ + boost::lexical_cast<std::string>(i->size) + " bytes";
+ return;
+ }
+ }
+ }
+
+
+ if (!match_filesizes(info, save_path, file_sizes, &error))
+ return;
+
+ piece_map.swap(tmp_pieces);
+ unfinished_pieces.swap(tmp_unfinished);
+ }
+ catch (invalid_encoding)
+ {
+ return;
+ }
+ catch (type_error)
+ {
+ return;
+ }
+ catch (file_error)
+ {
+ return;
+ }
+ }
+}}
+
diff --git a/library/setup.py b/library/setup.py
new file mode 100644
index 000000000..25215c729
--- /dev/null
+++ b/library/setup.py
@@ -0,0 +1,64 @@
+#/*
+#Copyright: A. Zakai ('Kripken') <kripkensteiner@gmail.com> http://6thsenseless.blogspot.com
+#
+#2006-15-9
+#
+#This code is licensed under the terms of the GNU General Public License (GPL),
+#version 2 or above; See /usr/share/common-licenses/GPL , or see
+#http://www.fsf.org/licensing/licenses/gpl.html
+#*/
+
+import platform
+
+pythonVersion = platform.python_version()[0:3]
+
+print "========================================="
+print "Creating python-libtorrent for Python " + pythonVersion
+print "========================================="
+
+from distutils.core import setup, Extension
+
+module1 = Extension('torrent',
+ include_dirs = ['./include', './include/libtorrent',
+ '/usr/include/python' + pythonVersion],
+ libraries = ['boost_filesystem', 'boost_date_time',
+ 'boost_program_options', 'boost_regex',
+ 'boost_serialization', 'boost_thread', 'z', 'pthread'],
+ sources = ['alert.cpp',
+ 'allocate_resources.cpp',
+ 'bt_peer_connection.cpp',
+ 'entry.cpp',
+ 'escape_string.cpp',
+ 'file.cpp',
+ 'http_tracker_connection.cpp',
+ 'identify_client.cpp',
+ 'ip_filter.cpp',
+ 'peer_connection.cpp',
+ 'piece_picker.cpp',
+ 'policy.cpp',
+ 'python-libtorrent.cpp',
+ 'session.cpp',
+ 'session_impl.cpp',
+ 'sha1.cpp',
+ 'stat.cpp',
+ 'storage.cpp',
+ 'torrent.cpp',
+ 'torrent_handle.cpp',
+ 'torrent_info.cpp',
+ 'tracker_manager.cpp',
+ 'udp_tracker_connection.cpp',
+ 'web_peer_connection.cpp',
+ './kademlia/closest_nodes.cpp',
+ './kademlia/dht_tracker.cpp',
+ './kademlia/find_data.cpp',
+ './kademlia/node.cpp',
+ './kademlia/node_id.cpp',
+ './kademlia/refresh.cpp',
+ './kademlia/routing_table.cpp',
+ './kademlia/rpc_manager.cpp',
+ './kademlia/traversal_algorithm.cpp'])
+
+setup (name = 'Python-libtorrent',
+ version = '0.3.1',
+ description = 'Wrapper code for libtorrent C++ torrent library (Sourceforge, not Rakshasa)',
+ ext_modules = [module1])
diff --git a/library/sha1.cpp b/library/sha1.cpp
new file mode 100755
index 000000000..1d04b4a17
--- /dev/null
+++ b/library/sha1.cpp
@@ -0,0 +1,314 @@
+/*
+SHA-1 C++ conversion
+
+original version:
+
+SHA-1 in C
+By Steve Reid <sreid@sea-to-sky.net>
+100% Public Domain
+
+changelog at the end of the file.
+*/
+
+#include <cstdio>
+#include <cstring>
+
+// if you don't want boost
+// replace with
+// #include <stdint.h>
+
+#include <boost/cstdint.hpp>
+using boost::uint32_t;
+using boost::uint8_t;
+
+#include "libtorrent/config.hpp"
+
+struct TORRENT_EXPORT SHA1_CTX
+{
+ uint32_t state[5];
+ uint32_t count[2];
+ uint8_t buffer[64];
+};
+
+TORRENT_EXPORT void SHA1Init(SHA1_CTX* context);
+TORRENT_EXPORT void SHA1Update(SHA1_CTX* context, uint8_t const* data, uint32_t len);
+TORRENT_EXPORT void SHA1Final(SHA1_CTX* context, uint8_t* digest);
+
+namespace
+{
+ union CHAR64LONG16
+ {
+ uint8_t c[64];
+ uint32_t l[16];
+ };
+
+#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
+
+// blk0() and blk() perform the initial expand.
+// I got the idea of expanding during the round function from SSLeay
+ struct little_endian_blk0
+ {
+ static uint32_t apply(CHAR64LONG16* block, int i)
+ {
+ return block->l[i] = (rol(block->l[i],24)&0xFF00FF00)
+ | (rol(block->l[i],8)&0x00FF00FF);
+ }
+ };
+
+ struct big_endian_blk0
+ {
+ static uint32_t apply(CHAR64LONG16* block, int i)
+ {
+ return block->l[i];
+ }
+ };
+
+
+#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \
+ ^block->l[(i+2)&15]^block->l[i&15],1))
+
+// (R0+R1), R2, R3, R4 are the different operations used in SHA1
+#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+BlkFun::apply(block, i)+0x5A827999+rol(v,5);w=rol(w,30);
+#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30);
+#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30);
+#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30);
+#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30);
+
+ // Hash a single 512-bit block. This is the core of the algorithm.
+ template <class BlkFun>
+ void SHA1Transform(uint32_t state[5], uint8_t const buffer[64])
+ {
+ using namespace std;
+ uint32_t a, b, c, d, e;
+
+ CHAR64LONG16* block;
+ uint8_t workspace[64];
+ block = (CHAR64LONG16*)workspace;
+ memcpy(block, buffer, 64);
+
+ // Copy context->state[] to working vars
+ a = state[0];
+ b = state[1];
+ c = state[2];
+ d = state[3];
+ e = state[4];
+ // 4 rounds of 20 operations each. Loop unrolled.
+ R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3);
+ R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7);
+ R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11);
+ R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15);
+ R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19);
+ R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23);
+ R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27);
+ R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31);
+ R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35);
+ R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39);
+ R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43);
+ R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47);
+ R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51);
+ R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55);
+ R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59);
+ R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63);
+ R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67);
+ R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71);
+ R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75);
+ R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79);
+ // Add the working vars back into context.state[]
+ state[0] += a;
+ state[1] += b;
+ state[2] += c;
+ state[3] += d;
+ state[4] += e;
+ // Wipe variables
+ a = b = c = d = e = 0;
+ }
+
+ void SHAPrintContext(SHA1_CTX *context, char *msg)
+ {
+ using namespace std;
+ printf("%s (%d,%d) %x %x %x %x %x\n"
+ , msg, context->count[0], context->count[1]
+ , context->state[0], context->state[1]
+ , context->state[2], context->state[3]
+ , context->state[4]);
+ }
+
+ template <class BlkFun>
+ void internal_update(SHA1_CTX* context, uint8_t const* data, uint32_t len)
+ {
+ using namespace std;
+ uint32_t i, j; // JHB
+
+#ifdef VERBOSE
+ SHAPrintContext(context, "before");
+#endif
+ j = (context->count[0] >> 3) & 63;
+ if ((context->count[0] += len << 3) < (len << 3)) context->count[1]++;
+ context->count[1] += (len >> 29);
+ if ((j + len) > 63)
+ {
+ memcpy(&context->buffer[j], data, (i = 64-j));
+ SHA1Transform<BlkFun>(context->state, context->buffer);
+ for ( ; i + 63 < len; i += 64)
+ {
+ SHA1Transform<BlkFun>(context->state, &data[i]);
+ }
+ j = 0;
+ }
+ else
+ {
+ i = 0;
+ }
+ memcpy(&context->buffer[j], &data[i], len - i);
+#ifdef VERBOSE
+ SHAPrintContext(context, "after ");
+#endif
+ }
+
+ bool is_big_endian()
+ {
+ uint32_t test = 1;
+ return *reinterpret_cast<uint8_t*>(&test) == 0;
+ }
+}
+
+// SHA1Init - Initialize new context
+
+void SHA1Init(SHA1_CTX* context)
+{
+ // SHA1 initialization constants
+ context->state[0] = 0x67452301;
+ context->state[1] = 0xEFCDAB89;
+ context->state[2] = 0x98BADCFE;
+ context->state[3] = 0x10325476;
+ context->state[4] = 0xC3D2E1F0;
+ context->count[0] = context->count[1] = 0;
+}
+
+
+// Run your data through this.
+
+void SHA1Update(SHA1_CTX* context, uint8_t const* data, uint32_t len)
+{
+#if defined __BIG_ENDIAN__
+ internal_update<big_endian_blk0>(context, data, len);
+#elif defined LITTLE_ENDIAN
+ internal_update<little_endian_blk0>(context, data, len);
+#else
+ // select different functions depending on endianess
+ // and figure out the endianess runtime
+ if (is_big_endian())
+ internal_update<big_endian_blk0>(context, data, len);
+ else
+ internal_update<little_endian_blk0>(context, data, len);
+#endif
+}
+
+
+// Add padding and return the message digest.
+
+void SHA1Final(SHA1_CTX* context, uint8_t* digest)
+{
+ uint8_t finalcount[8];
+
+ for (uint32_t i = 0; i < 8; ++i)
+ {
+ // Endian independent
+ finalcount[i] = static_cast<uint8_t>(
+ (context->count[(i >= 4 ? 0 : 1)]
+ >> ((3-(i & 3)) * 8) ) & 255);
+ }
+
+ SHA1Update(context, (uint8_t const*)"\200", 1);
+ while ((context->count[0] & 504) != 448)
+ SHA1Update(context, (uint8_t const*)"\0", 1);
+ SHA1Update(context, finalcount, 8); // Should cause a SHA1Transform()
+
+ for (uint32_t i = 0; i < 20; ++i)
+ {
+ digest[i] = static_cast<unsigned char>(
+ (context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);
+ }
+}
+
+/************************************************************
+
+-----------------
+Modified 7/98
+By James H. Brown <jbrown@burgoyne.com>
+Still 100% Public Domain
+
+Corrected a problem which generated improper hash values on 16 bit machines
+Routine SHA1Update changed from
+ void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned int
+len)
+to
+ void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned
+long len)
+
+The 'len' parameter was declared an int which works fine on 32 bit machines.
+However, on 16 bit machines an int is too small for the shifts being done
+against
+it. This caused the hash function to generate incorrect values if len was
+greater than 8191 (8K - 1) due to the 'len << 3' on line 3 of SHA1Update().
+
+Since the file IO in main() reads 16K at a time, any file 8K or larger would
+be guaranteed to generate the wrong hash (e.g. Test Vector #3, a million
+"a"s).
+
+I also changed the declaration of variables i & j in SHA1Update to
+unsigned long from unsigned int for the same reason.
+
+These changes should make no difference to any 32 bit implementations since
+an
+int and a long are the same size in those environments.
+
+--
+I also corrected a few compiler warnings generated by Borland C.
+1. Added #include <process.h> for exit() prototype
+2. Removed unused variable 'j' in SHA1Final
+3. Changed exit(0) to return(0) at end of main.
+
+ALL changes I made can be located by searching for comments containing 'JHB'
+-----------------
+Modified 8/98
+By Steve Reid <sreid@sea-to-sky.net>
+Still 100% public domain
+
+1- Removed #include <process.h> and used return() instead of exit()
+2- Fixed overwriting of finalcount in SHA1Final() (discovered by Chris Hall)
+3- Changed email address from steve@edmweb.com to sreid@sea-to-sky.net
+
+-----------------
+Modified 4/01
+By Saul Kravitz <Saul.Kravitz@celera.com>
+Still 100% PD
+Modified to run on Compaq Alpha hardware.
+
+-----------------
+Converted to C++ 6/04
+By Arvid Norberg <arvidn@sourceforge.net>
+1- made the input buffer const, and made the
+ previous SHA1HANDSOFF implicit
+2- uses C99 types with size guarantees
+ from boost
+3- if none of __BIG_ENDIAN__ or LITTLE_ENDIAN
+ are defined, endianess is determined
+ at runtime. templates are used to duplicate
+ the transform function for each endianess
+4- using anonymous namespace to avoid external
+ linkage on internal functions
+5- using standard C++ includes
+
+still 100% PD
+*/
+
+/*
+Test Vectors (from FIPS PUB 180-1)
+"abc"
+ A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D
+"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
+ 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1
+A million repetitions of "a"
+ 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F
+*/
diff --git a/library/stat.cpp b/library/stat.cpp
new file mode 100755
index 000000000..c36bd3725
--- /dev/null
+++ b/library/stat.cpp
@@ -0,0 +1,91 @@
+/*
+
+Copyright (c) 2003, 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.
+
+*/
+
+// TODO: Use two algorithms to estimate transfer rate.
+// one (simple) for transfer rates that are >= 1 packet
+// per second and one (low pass-filter) for rates < 1
+// packet per second.
+
+#include <numeric>
+
+#include "libtorrent/stat.hpp"
+#include "libtorrent/invariant_check.hpp"
+#include <algorithm>
+
+#if defined _MSC_VER && _MSC_VER <= 1200
+#define for if (false) {} else for
+#endif
+
+using namespace libtorrent;
+
+void libtorrent::stat::second_tick(float tick_interval)
+{
+ INVARIANT_CHECK;
+
+ for (int i = history - 2; i >= 0; --i)
+ {
+ m_download_rate_history[i + 1] = m_download_rate_history[i];
+ m_upload_rate_history[i + 1] = m_upload_rate_history[i];
+ m_download_payload_rate_history[i + 1] = m_download_payload_rate_history[i];
+ m_upload_payload_rate_history[i + 1] = m_upload_payload_rate_history[i];
+ }
+
+ m_download_rate_history[0] = (m_downloaded_payload + m_downloaded_protocol)
+ / tick_interval;
+ m_upload_rate_history[0] = (m_uploaded_payload + m_uploaded_protocol)
+ / tick_interval;
+ m_download_payload_rate_history[0] = m_downloaded_payload / tick_interval;
+ m_upload_payload_rate_history[0] = m_uploaded_payload / tick_interval;
+
+ m_downloaded_payload = 0;
+ m_uploaded_payload = 0;
+ m_downloaded_protocol = 0;
+ m_uploaded_protocol = 0;
+
+ m_mean_download_rate = 0;
+ m_mean_upload_rate = 0;
+ m_mean_download_payload_rate = 0;
+ m_mean_upload_payload_rate = 0;
+
+ for (int i = 0; i < history; ++i)
+ {
+ m_mean_download_rate += m_download_rate_history[i];
+ m_mean_upload_rate += m_upload_rate_history[i];
+ m_mean_download_payload_rate += m_download_payload_rate_history[i];
+ m_mean_upload_payload_rate += m_upload_payload_rate_history[i];
+ }
+
+ m_mean_download_rate /= history;
+ m_mean_upload_rate /= history;
+ m_mean_download_payload_rate /= history;
+ m_mean_upload_payload_rate /= history;
+}
diff --git a/library/state.txt b/library/state.txt
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/library/state.txt
diff --git a/library/storage.cpp b/library/storage.cpp
new file mode 100755
index 000000000..33b32bad3
--- /dev/null
+++ b/library/storage.cpp
@@ -0,0 +1,2172 @@
+/*
+
+Copyright (c) 2003, Arvid Norberg, Daniel Wallin
+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.
+
+*/
+
+#include <ctime>
+#include <iterator>
+#include <algorithm>
+#include <set>
+#include <functional>
+
+#ifdef _MSC_VER
+#pragma warning(push, 1)
+#endif
+
+#include <boost/lexical_cast.hpp>
+#include <boost/filesystem/convenience.hpp>
+#include <boost/filesystem/operations.hpp>
+#include <boost/filesystem/fstream.hpp>
+#include <boost/thread/mutex.hpp>
+#include <boost/ref.hpp>
+#include <boost/date_time/posix_time/posix_time_types.hpp>
+#include <boost/bind.hpp>
+#include <boost/version.hpp>
+#include <boost/multi_index_container.hpp>
+#include <boost/multi_index/member.hpp>
+#include <boost/multi_index/ordered_index.hpp>
+
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+#include "libtorrent/storage.hpp"
+#include "libtorrent/torrent.hpp"
+#include "libtorrent/hasher.hpp"
+#include "libtorrent/session.hpp"
+#include "libtorrent/peer_id.hpp"
+#include "libtorrent/file.hpp"
+#include "libtorrent/invariant_check.hpp"
+#include "libtorrent/aux_/session_impl.hpp"
+
+#ifndef NDEBUG
+#include <ios>
+#include <iostream>
+#include <iomanip>
+#include <cstdio>
+#endif
+
+#if defined(_WIN32) && defined(UNICODE)
+
+#include <windows.h>
+#include <boost/filesystem/exception.hpp>
+#include "libtorrent/utf8.hpp"
+
+namespace libtorrent
+{
+ std::wstring safe_convert(std::string const& s)
+ {
+ try
+ {
+ return libtorrent::utf8_wchar(s);
+ }
+ catch (std::exception)
+ {
+ std::wstring ret;
+ for (const char* i = &*s.begin(); i < &*s.end(); ++i)
+ {
+ wchar_t c;
+ c = '.';
+ std::mbtowc(&c, i, 1);
+ ret += c;
+ }
+ return ret;
+ }
+ }
+}
+
+namespace
+{
+ using libtorrent::safe_convert;
+ using namespace boost::filesystem;
+
+ // based on code from Boost.Fileystem
+ bool create_directories_win(const path& ph)
+ {
+ if (ph.empty() || exists(ph))
+ {
+ if ( !ph.empty() && !is_directory(ph) )
+ boost::throw_exception( filesystem_error(
+ "boost::filesystem::create_directories",
+ ph, "path exists and is not a directory",
+ not_directory_error ) );
+ return false;
+ }
+
+ // First create branch, by calling ourself recursively
+ create_directories_win(ph.branch_path());
+ // Now that parent's path exists, create the directory
+ std::wstring wph(safe_convert(ph.native_directory_string()));
+ CreateDirectory(wph.c_str(), 0);
+ return true;
+ }
+
+ bool exists_win( const path & ph )
+ {
+ std::wstring wpath(safe_convert(ph.string()));
+ if(::GetFileAttributes( wpath.c_str() ) == 0xFFFFFFFF)
+ {
+ UINT err = ::GetLastError();
+ if((err == ERROR_FILE_NOT_FOUND)
+ || (err == ERROR_INVALID_PARAMETER)
+ || (err == ERROR_NOT_READY)
+ || (err == ERROR_PATH_NOT_FOUND)
+ || (err == ERROR_INVALID_NAME)
+ || (err == ERROR_BAD_NETPATH ))
+ return false; // GetFileAttributes failed because the path does not exist
+ // for any other error we assume the file does exist and fall through,
+ // this may not be the best policy though... (JM 20040330)
+ return true;
+ }
+ return true;
+ }
+
+ boost::intmax_t file_size_win( const path & ph )
+ {
+ std::wstring wpath(safe_convert(ph.string()));
+ // by now, intmax_t is 64-bits on all Windows compilers
+ WIN32_FILE_ATTRIBUTE_DATA fad;
+ if ( !::GetFileAttributesExW( wpath.c_str(),
+ ::GetFileExInfoStandard, &fad ) )
+ boost::throw_exception( filesystem_error(
+ "boost::filesystem::file_size",
+ ph, detail::system_error_code() ) );
+ if ( (fad.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) !=0 )
+ boost::throw_exception( filesystem_error(
+ "boost::filesystem::file_size",
+ ph, "invalid: is a directory",
+ is_directory_error ) );
+ return (static_cast<boost::intmax_t>(fad.nFileSizeHigh)
+ << (sizeof(fad.nFileSizeLow)*8))
+ + fad.nFileSizeLow;
+ }
+
+ std::time_t last_write_time_win( const path & ph )
+ {
+ struct _stat path_stat;
+ std::wstring wph(safe_convert(ph.native_file_string()));
+ if ( ::_wstat( wph.c_str(), &path_stat ) != 0 )
+ boost::throw_exception( filesystem_error(
+ "boost::filesystem::last_write_time",
+ ph, detail::system_error_code() ) );
+ return path_stat.st_mtime;
+ }
+
+ void rename_win( const path & old_path,
+ const path & new_path )
+ {
+ std::wstring wold_path(safe_convert(old_path.string()));
+ std::wstring wnew_path(safe_convert(new_path.string()));
+ if ( !::MoveFile( wold_path.c_str(), wnew_path.c_str() ) )
+ boost::throw_exception( filesystem_error(
+ "boost::filesystem::rename",
+ old_path, new_path, detail::system_error_code() ) );
+ }
+
+} // anonymous namespace
+
+#endif
+
+#if BOOST_VERSION < 103200
+bool operator<(boost::filesystem::path const& lhs
+ , boost::filesystem::path const& rhs)
+{
+ return lhs.string() < rhs.string();
+}
+#endif
+
+using namespace boost::filesystem;
+namespace pt = boost::posix_time;
+using boost::bind;
+using namespace ::boost::multi_index;
+using boost::multi_index::multi_index_container;
+
+namespace
+{
+ using namespace libtorrent;
+
+ void print_to_log(const std::string& s)
+ {
+ static std::ofstream log("log.txt");
+ log << s;
+ log.flush();
+ }
+/*
+ struct file_key
+ {
+ file_key(sha1_hash ih, path f): info_hash(ih), file_path(f) {}
+ file_key() {}
+ sha1_hash info_hash;
+ path file_path;
+ bool operator<(file_key const& fk) const
+ {
+ if (info_hash < fk.info_hash) return true;
+ if (fk.info_hash < info_hash) return false;
+ return file_path < fk.file_path;
+ }
+ };
+*/
+ struct lru_file_entry
+ {
+ lru_file_entry(boost::shared_ptr<file> const& f)
+ : file_ptr(f)
+ , last_use(pt::second_clock::universal_time()) {}
+ mutable boost::shared_ptr<file> file_ptr;
+ path file_path;
+ void* key;
+ pt::ptime last_use;
+ file::open_mode mode;
+ };
+
+ struct file_pool
+ {
+ file_pool(int size): m_size(size) {}
+
+ boost::shared_ptr<file> open_file(void* st, path const& p, file::open_mode m)
+ {
+ assert(st != 0);
+ assert(p.is_complete());
+ typedef nth_index<file_set, 0>::type path_view;
+ path_view& pt = get<0>(m_files);
+ path_view::iterator i = pt.find(p);
+ if (i != pt.end())
+ {
+ lru_file_entry e = *i;
+ e.last_use = pt::second_clock::universal_time();
+
+ // if you hit this assert, you probably have more than one
+ // storage/torrent using the same file at the same time!
+ assert(e.key == st);
+
+ e.key = st;
+ if ((e.mode & m) != m)
+ {
+ // close the file before we open it with
+ // the new read/write privilages
+ i->file_ptr.reset();
+ assert(e.file_ptr.unique());
+ e.file_ptr.reset();
+ e.file_ptr.reset(new file(p, m));
+ e.mode = m;
+ }
+ pt.replace(i, e);
+ return e.file_ptr;
+ }
+ // the file is not in our cache
+ if ((int)m_files.size() >= m_size)
+ {
+ // the file cache is at its maximum size, close
+ // the least recently used (lru) file from it
+ typedef nth_index<file_set, 1>::type lru_view;
+ lru_view& lt = get<1>(m_files);
+ lru_view::iterator i = lt.begin();
+ // the first entry in this view is the least recently used
+/* for (lru_view::iterator i = lt.begin(); i != lt.end(); ++i)
+ {
+ std::cerr << i->last_use << "\n";
+ }
+*/ assert(lt.size() == 1 || (i->last_use <= boost::next(i)->last_use));
+ lt.erase(i);
+ }
+ lru_file_entry e(boost::shared_ptr<file>(new file(p, m)));
+ e.mode = m;
+ e.key = st;
+ e.file_path = p;
+ pt.insert(e);
+ return e.file_ptr;
+ }
+
+ void release(void* st)
+ {
+ assert(st != 0);
+ using boost::tie;
+
+ typedef nth_index<file_set, 2>::type key_view;
+ key_view& kt = get<2>(m_files);
+
+ key_view::iterator start, end;
+ tie(start, end) = kt.equal_range(st);
+
+/*
+ std::cerr << "releasing files!\n";
+ for (path_view::iterator i = r.first; i != r.second; ++i)
+ {
+ std::cerr << i->key.file_path.native_file_string() << "\n";
+ }
+*/
+ kt.erase(start, end);
+/*
+ std::cerr << "files left: " << pt.size() << "\n";
+ for (path_view::iterator i = pt.begin(); i != pt.end(); ++i)
+ {
+ std::cerr << i->key.file_path.native_file_string() << "\n";
+ }
+*/
+ }
+
+ private:
+ int m_size;
+
+ typedef multi_index_container<
+ lru_file_entry, indexed_by<
+ ordered_unique<member<lru_file_entry, path
+ , &lru_file_entry::file_path> >
+ , ordered_non_unique<member<lru_file_entry, pt::ptime
+ , &lru_file_entry::last_use> >
+ , ordered_non_unique<member<lru_file_entry, void*
+ , &lru_file_entry::key> >
+ >
+ > file_set;
+
+ file_set m_files;
+ };
+}
+
+namespace libtorrent
+{
+
+ std::vector<std::pair<size_type, std::time_t> > get_filesizes(
+ torrent_info const& t, path p)
+ {
+ p = complete(p);
+ std::vector<std::pair<size_type, std::time_t> > sizes;
+ for (torrent_info::file_iterator i = t.begin_files();
+ i != t.end_files(); ++i)
+ {
+ size_type size = 0;
+ std::time_t time = 0;
+ try
+ {
+ path f = p / i->path;
+#if defined(_WIN32) && defined(UNICODE)
+ size = file_size_win(f);
+ time = last_write_time_win(f);
+#else
+ size = file_size(f);
+ time = last_write_time(f);
+#endif
+ }
+ catch (std::exception&) {}
+ sizes.push_back(std::make_pair(size, time));
+ }
+ return sizes;
+ }
+
+ bool match_filesizes(
+ torrent_info const& t
+ , path p
+ , std::vector<std::pair<size_type, std::time_t> > const& sizes
+ , std::string* error)
+ {
+ if ((int)sizes.size() != t.num_files())
+ {
+ if (error) *error = "mismatching number of files";
+ return false;
+ }
+ p = complete(p);
+
+ std::vector<std::pair<size_type, std::time_t> >::const_iterator s
+ = sizes.begin();
+ for (torrent_info::file_iterator i = t.begin_files();
+ i != t.end_files(); ++i, ++s)
+ {
+ size_type size = 0;
+ std::time_t time = 0;
+ try
+ {
+ path f = p / i->path;
+#if defined(_WIN32) && defined(UNICODE)
+ size = file_size_win(f);
+ time = last_write_time_win(f);
+#else
+ size = file_size(f);
+ time = last_write_time(f);
+#endif
+ }
+ catch (std::exception&) {}
+ if (size != s->first)
+ {
+ if (error) *error = "filesize mismatch for file '"
+ + i->path.native_file_string()
+ + "', expected to be " + boost::lexical_cast<std::string>(s->first)
+ + " bytes";
+ return false;
+ }
+ if (time != s->second)
+ {
+ if (error) *error = "timestamp mismatch for file '"
+ + i->path.native_file_string()
+ + "', expected to have modification date "
+ + boost::lexical_cast<std::string>(s->second);
+ return false;
+ }
+ }
+ return true;
+ }
+
+ struct thread_safe_storage
+ {
+ thread_safe_storage(std::size_t n)
+ : slots(n, false)
+ {}
+
+ boost::mutex mutex;
+ boost::condition condition;
+ std::vector<bool> slots;
+ };
+
+ struct slot_lock
+ {
+ slot_lock(thread_safe_storage& s, int slot_)
+ : storage_(s)
+ , slot(slot_)
+ {
+ assert(slot_>=0 && slot_ < (int)s.slots.size());
+ boost::mutex::scoped_lock lock(storage_.mutex);
+
+ while (storage_.slots[slot])
+ storage_.condition.wait(lock);
+ storage_.slots[slot] = true;
+ }
+
+ ~slot_lock()
+ {
+ storage_.slots[slot] = false;
+ storage_.condition.notify_all();
+ }
+
+ thread_safe_storage& storage_;
+ int slot;
+ };
+
+ class storage::impl : public thread_safe_storage, boost::noncopyable
+ {
+ public:
+ impl(torrent_info const& info, path const& path)
+ : thread_safe_storage(info.num_pieces())
+ , info(info)
+ {
+ save_path = complete(path);
+ assert(save_path.is_complete());
+ }
+
+ impl(impl const& x)
+ : thread_safe_storage(x.info.num_pieces())
+ , info(x.info)
+ , save_path(x.save_path)
+ {}
+
+ ~impl()
+ {
+ files.release(this);
+ }
+
+ torrent_info const& info;
+ path save_path;
+ static file_pool files;
+ };
+
+ file_pool storage::impl::files(40);
+
+ storage::storage(torrent_info const& info, path const& path)
+ : m_pimpl(new impl(info, path))
+ {
+ assert(info.begin_files() != info.end_files());
+ }
+
+ void storage::release_files()
+ {
+ m_pimpl->files.release(m_pimpl.get());
+ }
+
+ void storage::swap(storage& other)
+ {
+ m_pimpl.swap(other.m_pimpl);
+ }
+
+ // returns true on success
+ bool storage::move_storage(path save_path)
+ {
+ path old_path;
+ path new_path;
+
+ save_path = complete(save_path);
+
+#if defined(_WIN32) && defined(UNICODE)
+ std::wstring wsave_path(safe_convert(save_path.native_file_string()));
+ if (!exists_win(save_path))
+ {
+ CreateDirectory(wsave_path.c_str(), 0);
+ }
+ else if ((GetFileAttributes(wsave_path.c_str()) & FILE_ATTRIBUTE_DIRECTORY) == 0)
+ {
+ return false;
+ }
+#else
+ if(!exists(save_path))
+ create_directory(save_path);
+ else if(!is_directory(save_path))
+ return false;
+#endif
+
+ m_pimpl->files.release(m_pimpl.get());
+
+ if (m_pimpl->info.num_files() == 1)
+ {
+ path single_file = m_pimpl->info.begin_files()->path;
+ if (single_file.has_branch_path())
+ {
+#if defined(_WIN32) && defined(UNICODE)
+ std::wstring wsave_path(safe_convert((save_path / single_file.branch_path())
+ .native_directory_string()));
+ CreateDirectory(wsave_path.c_str(), 0);
+#else
+ create_directory(save_path / single_file.branch_path());
+#endif
+ }
+
+ old_path = m_pimpl->save_path / single_file;
+ new_path = save_path / m_pimpl->info.begin_files()->path;
+ }
+ else
+ {
+ assert(m_pimpl->info.num_files() > 1);
+ old_path = m_pimpl->save_path / m_pimpl->info.name();
+ new_path = save_path / m_pimpl->info.name();
+ }
+
+ try
+ {
+#if defined(_WIN32) && defined(UNICODE)
+ rename_win(old_path, new_path);
+#else
+ rename(old_path, new_path);
+#endif
+ m_pimpl->save_path = save_path;
+ return true;
+ }
+ catch (std::exception&) {}
+ return false;
+ }
+
+#ifndef NDEBUG
+
+ void storage::shuffle()
+ {
+ int num_pieces = m_pimpl->info.num_pieces();
+
+ std::vector<int> pieces(num_pieces);
+ for (std::vector<int>::iterator i = pieces.begin();
+ i != pieces.end();
+ ++i)
+ {
+ *i = static_cast<int>(i - pieces.begin());
+ }
+ std::srand((unsigned int)std::time(0));
+ std::vector<int> targets(pieces);
+ std::random_shuffle(pieces.begin(), pieces.end());
+ std::random_shuffle(targets.begin(), targets.end());
+
+ for (int i = 0; i < (std::max)(num_pieces / 50, 1); ++i)
+ {
+ const int slot_index = targets[i];
+ const int piece_index = pieces[i];
+ const int slot_size =static_cast<int>(m_pimpl->info.piece_size(slot_index));
+ std::vector<char> buf(slot_size);
+ read(&buf[0], piece_index, 0, slot_size);
+ write(&buf[0], slot_index, 0, slot_size);
+ }
+ }
+
+#endif
+
+ size_type storage::read(
+ char* buf
+ , int slot
+ , int offset
+ , int size)
+ {
+ assert(buf != 0);
+ assert(slot >= 0 && slot < m_pimpl->info.num_pieces());
+ assert(offset >= 0);
+ assert(offset < m_pimpl->info.piece_size(slot));
+ assert(size > 0);
+
+ slot_lock lock(*m_pimpl, slot);
+
+#ifndef NDEBUG
+ std::vector<file_slice> slices
+ = m_pimpl->info.map_block(slot, offset, size);
+ assert(!slices.empty());
+#endif
+
+ size_type start = slot * (size_type)m_pimpl->info.piece_length() + offset;
+ assert(start + size <= m_pimpl->info.total_size());
+
+ // find the file iterator and file offset
+ size_type file_offset = start;
+ std::vector<file_entry>::const_iterator file_iter;
+
+ for (file_iter = m_pimpl->info.begin_files();;)
+ {
+ if (file_offset < file_iter->size)
+ break;
+
+ file_offset -= file_iter->size;
+ ++file_iter;
+ }
+
+ boost::shared_ptr<file> in(m_pimpl->files.open_file(
+ m_pimpl.get()
+ , m_pimpl->save_path / file_iter->path
+ , file::in));
+
+ assert(file_offset < file_iter->size);
+
+ assert(slices[0].offset == file_offset);
+
+ size_type new_pos = in->seek(file_offset);
+ if (new_pos != file_offset)
+ {
+ // the file was not big enough
+ throw file_error("slot has no storage");
+ }
+
+#ifndef NDEBUG
+ size_type in_tell = in->tell();
+ assert(in_tell == file_offset);
+#endif
+
+ int left_to_read = size;
+ int slot_size = static_cast<int>(m_pimpl->info.piece_size(slot));
+
+ if (offset + left_to_read > slot_size)
+ left_to_read = slot_size - offset;
+
+ assert(left_to_read >= 0);
+
+ size_type result = left_to_read;
+ int buf_pos = 0;
+
+#ifndef NDEBUG
+ int counter = 0;
+#endif
+
+ while (left_to_read > 0)
+ {
+ int read_bytes = left_to_read;
+ if (file_offset + read_bytes > file_iter->size)
+ read_bytes = static_cast<int>(file_iter->size - file_offset);
+
+ if (read_bytes > 0)
+ {
+#ifndef NDEBUG
+ assert(int(slices.size()) > counter);
+ size_type slice_size = slices[counter].size;
+ assert(slice_size == read_bytes);
+ assert(m_pimpl->info.file_at(slices[counter].file_index).path
+ == file_iter->path);
+#endif
+
+ size_type actual_read = in->read(buf + buf_pos, read_bytes);
+
+ if (read_bytes != actual_read)
+ {
+ // the file was not big enough
+ throw file_error("slot has no storage");
+ }
+
+ left_to_read -= read_bytes;
+ buf_pos += read_bytes;
+ assert(buf_pos >= 0);
+ file_offset += read_bytes;
+ }
+
+ if (left_to_read > 0)
+ {
+ ++file_iter;
+#ifndef NDEBUG
+ // empty files are not returned by map_block, so if
+ // this file was empty, don't increment the slice counter
+ if (read_bytes > 0) ++counter;
+#endif
+ path path = m_pimpl->save_path / file_iter->path;
+
+ file_offset = 0;
+ in = m_pimpl->files.open_file(
+ m_pimpl.get()
+ , path, file::in);
+ in->seek(0);
+ }
+ }
+
+ return result;
+ }
+
+ // throws file_error if it fails to write
+ void storage::write(
+ const char* buf
+ , int slot
+ , int offset
+ , int size)
+ {
+ assert(buf != 0);
+ assert(slot >= 0);
+ assert(slot < m_pimpl->info.num_pieces());
+ assert(offset >= 0);
+ assert(size > 0);
+
+ slot_lock lock(*m_pimpl, slot);
+
+#ifndef NDEBUG
+ std::vector<file_slice> slices
+ = m_pimpl->info.map_block(slot, offset, size);
+ assert(!slices.empty());
+#endif
+
+ size_type start = slot * (size_type)m_pimpl->info.piece_length() + offset;
+
+ // find the file iterator and file offset
+ size_type file_offset = start;
+ std::vector<file_entry>::const_iterator file_iter;
+
+ for (file_iter = m_pimpl->info.begin_files();;)
+ {
+ if (file_offset < file_iter->size)
+ break;
+
+ file_offset -= file_iter->size;
+ ++file_iter;
+ assert(file_iter != m_pimpl->info.end_files());
+ }
+
+ path p(m_pimpl->save_path / file_iter->path);
+ boost::shared_ptr<file> out = m_pimpl->files.open_file(
+ m_pimpl.get()
+ , p, file::out | file::in);
+
+ assert(file_offset < file_iter->size);
+ assert(slices[0].offset == file_offset);
+
+ size_type pos = out->seek(file_offset);
+
+ if (pos != file_offset)
+ {
+ std::stringstream s;
+ s << "no storage for slot " << slot;
+ throw file_error(s.str());
+ }
+
+ int left_to_write = size;
+ int slot_size = static_cast<int>(m_pimpl->info.piece_size(slot));
+
+ if (offset + left_to_write > slot_size)
+ left_to_write = slot_size - offset;
+
+ assert(left_to_write >= 0);
+
+ int buf_pos = 0;
+#ifndef NDEBUG
+ int counter = 0;
+#endif
+ while (left_to_write > 0)
+ {
+ int write_bytes = left_to_write;
+ if (file_offset + write_bytes > file_iter->size)
+ {
+ assert(file_iter->size >= file_offset);
+ write_bytes = static_cast<int>(file_iter->size - file_offset);
+ }
+
+ if (write_bytes > 0)
+ {
+ assert(int(slices.size()) > counter);
+ assert(slices[counter].size == write_bytes);
+ assert(m_pimpl->info.file_at(slices[counter].file_index).path
+ == file_iter->path);
+
+ assert(buf_pos >= 0);
+ assert(write_bytes >= 0);
+ size_type written = out->write(buf + buf_pos, write_bytes);
+
+ if (written != write_bytes)
+ {
+ std::stringstream s;
+ s << "no storage for slot " << slot;
+ throw file_error(s.str());
+ }
+
+ left_to_write -= write_bytes;
+ buf_pos += write_bytes;
+ assert(buf_pos >= 0);
+ file_offset += write_bytes;
+ assert(file_offset <= file_iter->size);
+ }
+
+ if (left_to_write > 0)
+ {
+ #ifndef NDEBUG
+ if (write_bytes > 0) ++counter;
+ #endif
+ ++file_iter;
+
+ assert(file_iter != m_pimpl->info.end_files());
+ path p = m_pimpl->save_path / file_iter->path;
+ file_offset = 0;
+ out = m_pimpl->files.open_file(
+ m_pimpl.get()
+ , p, file::out | file::in);
+
+ out->seek(0);
+ }
+ }
+ }
+
+
+
+
+
+ // -- piece_manager -----------------------------------------------------
+
+ class piece_manager::impl
+ {
+ friend class invariant_access;
+ public:
+
+ impl(
+ torrent_info const& info
+ , path const& path);
+
+ bool check_fastresume(
+ aux::piece_checker_data& d
+ , std::vector<bool>& pieces
+ , int& num_pieces
+ , bool compact_mode);
+
+ std::pair<bool, float> check_files(
+ std::vector<bool>& pieces
+ , int& num_pieces);
+
+ void release_files();
+
+ void allocate_slots(int num_slots);
+ void mark_failed(int index);
+ unsigned long piece_crc(
+ int slot_index
+ , int block_size
+ , const std::bitset<256>& bitmask);
+
+ int slot_for_piece(int piece_index) const;
+
+ size_type read(
+ char* buf
+ , int piece_index
+ , int offset
+ , int size);
+
+ void write(
+ const char* buf
+ , int piece_index
+ , int offset
+ , int size);
+
+ path const& save_path() const
+ { return m_save_path; }
+
+ bool move_storage(path save_path)
+ {
+ if (m_storage.move_storage(save_path))
+ {
+ m_save_path = complete(save_path);
+ return true;
+ }
+ return false;
+ }
+
+ void export_piece_map(std::vector<int>& p) const;
+
+ // returns the slot currently associated with the given
+ // piece or assigns the given piece_index to a free slot
+
+ 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);
+
+ int allocate_slot_for_piece(int piece_index);
+#ifndef NDEBUG
+ void check_invariant() const;
+#ifdef TORRENT_STORAGE_DEBUG
+ void debug_log() const;
+#endif
+#endif
+ storage 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;
+
+ 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;
+
+ // used as temporary piece data storage in allocate_slots
+ // it is a member in order to avoid allocating it on
+ // the heap every time a new slot is allocated. (This is quite
+ // frequent with high download speeds)
+ std::vector<char> m_scratch_buffer;
+ };
+
+ piece_manager::impl::impl(
+ torrent_info const& info
+ , path const& save_path)
+ : m_storage(info, save_path)
+ , m_compact_mode(false)
+ , m_fill_mode(true)
+ , m_info(info)
+ , m_save_path(complete(save_path))
+ , m_allocating(false)
+ {
+ assert(m_save_path.is_complete());
+ }
+
+ piece_manager::piece_manager(
+ torrent_info const& info
+ , path const& save_path)
+ : m_pimpl(new impl(info, save_path))
+ {
+ }
+
+ piece_manager::~piece_manager()
+ {
+ }
+
+ void piece_manager::release_files()
+ {
+ m_pimpl->release_files();
+ }
+
+ void piece_manager::impl::release_files()
+ {
+ m_storage.release_files();
+ }
+
+ void piece_manager::impl::export_piece_map(
+ std::vector<int>& p) const
+ {
+ // synchronization ------------------------------------------------------
+ boost::recursive_mutex::scoped_lock lock(m_mutex);
+ // ----------------------------------------------------------------------
+
+ INVARIANT_CHECK;
+
+ p.clear();
+ std::vector<int>::const_reverse_iterator last;
+ for (last = m_slot_to_piece.rbegin();
+ last != m_slot_to_piece.rend(); ++last)
+ {
+ if (*last != unallocated) break;
+ }
+
+ for (std::vector<int>::const_iterator i =
+ m_slot_to_piece.begin();
+ i != last.base(); ++i)
+ {
+ p.push_back(*i);
+ }
+ }
+
+ void piece_manager::export_piece_map(
+ std::vector<int>& p) const
+ {
+ m_pimpl->export_piece_map(p);
+ }
+
+ void piece_manager::impl::mark_failed(int piece_index)
+ {
+ // synchronization ------------------------------------------------------
+ boost::recursive_mutex::scoped_lock lock(m_mutex);
+ // ----------------------------------------------------------------------
+
+ INVARIANT_CHECK;
+
+ assert(piece_index >= 0 && piece_index < (int)m_piece_to_slot.size());
+ assert(m_piece_to_slot[piece_index] >= 0);
+
+ int slot_index = m_piece_to_slot[piece_index];
+
+ assert(slot_index >= 0);
+
+ m_slot_to_piece[slot_index] = unassigned;
+ m_piece_to_slot[piece_index] = has_no_slot;
+ m_free_slots.push_back(slot_index);
+ }
+
+ void piece_manager::mark_failed(int index)
+ {
+ m_pimpl->mark_failed(index);
+ }
+
+ bool piece_manager::is_allocating() const
+ {
+ return m_pimpl->m_state
+ == impl::state_allocating;
+ }
+
+ int piece_manager::slot_for_piece(int piece_index) const
+ {
+ return m_pimpl->slot_for_piece(piece_index);
+ }
+
+ int piece_manager::impl::slot_for_piece(int piece_index) const
+ {
+ assert(piece_index >= 0 && piece_index < m_info.num_pieces());
+ return m_piece_to_slot[piece_index];
+ }
+
+ unsigned long piece_manager::piece_crc(
+ int index
+ , int block_size
+ , const std::bitset<256>& bitmask)
+ {
+ return m_pimpl->piece_crc(index, block_size, bitmask);
+ }
+
+ unsigned long piece_manager::impl::piece_crc(
+ int slot_index
+ , int block_size
+ , const std::bitset<256>& bitmask)
+ {
+ assert(slot_index >= 0);
+ assert(slot_index < m_info.num_pieces());
+ assert(block_size > 0);
+
+ adler32_crc crc;
+ std::vector<char> buf(block_size);
+ int num_blocks = static_cast<int>(m_info.piece_size(slot_index)) / block_size;
+ int last_block_size = static_cast<int>(m_info.piece_size(slot_index)) % block_size;
+ if (last_block_size == 0) last_block_size = block_size;
+
+ for (int i = 0; i < num_blocks-1; ++i)
+ {
+ if (!bitmask[i]) continue;
+ m_storage.read(
+ &buf[0]
+ , slot_index
+ , i * block_size
+ , block_size);
+ crc.update(&buf[0], block_size);
+ }
+ if (bitmask[num_blocks - 1])
+ {
+ m_storage.read(
+ &buf[0]
+ , slot_index
+ , block_size * (num_blocks - 1)
+ , last_block_size);
+ crc.update(&buf[0], last_block_size);
+ }
+ return crc.final();
+ }
+
+ size_type piece_manager::impl::read(
+ char* buf
+ , int piece_index
+ , int offset
+ , int size)
+ {
+ assert(buf);
+ assert(offset >= 0);
+ assert(size > 0);
+ assert(piece_index >= 0 && piece_index < (int)m_piece_to_slot.size());
+ assert(m_piece_to_slot[piece_index] >= 0 && m_piece_to_slot[piece_index] < (int)m_slot_to_piece.size());
+ int slot = m_piece_to_slot[piece_index];
+ assert(slot >= 0 && slot < (int)m_slot_to_piece.size());
+ return m_storage.read(buf, slot, offset, size);
+ }
+
+ size_type piece_manager::read(
+ char* buf
+ , int piece_index
+ , int offset
+ , int size)
+ {
+ return m_pimpl->read(buf, piece_index, offset, size);
+ }
+
+ void piece_manager::impl::write(
+ const char* buf
+ , int piece_index
+ , int offset
+ , int size)
+ {
+ assert(buf);
+ assert(offset >= 0);
+ assert(size > 0);
+ assert(piece_index >= 0 && piece_index < (int)m_piece_to_slot.size());
+ int slot = allocate_slot_for_piece(piece_index);
+ assert(slot >= 0 && slot < (int)m_slot_to_piece.size());
+ m_storage.write(buf, slot, offset, size);
+ }
+
+ void piece_manager::write(
+ const char* buf
+ , int piece_index
+ , int offset
+ , int size)
+ {
+ m_pimpl->write(buf, piece_index, offset, size);
+ }
+
+ int piece_manager::impl::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)
+ {
+ INVARIANT_CHECK;
+
+ assert((int)have_pieces.size() == m_info.num_pieces());
+
+ const int piece_size = static_cast<int>(m_info.piece_length());
+ const int last_piece_size = static_cast<int>(m_info.piece_size(
+ m_info.num_pieces() - 1));
+
+ assert((int)piece_data.size() >= last_piece_size);
+
+ // calculate a small digest, with the same
+ // size as the last piece. And a large digest
+ // which has the same size as a normal piece
+ hasher small_digest;
+ small_digest.update(&piece_data[0], last_piece_size);
+ hasher large_digest(small_digest);
+ assert(piece_size - last_piece_size >= 0);
+ if (piece_size - last_piece_size > 0)
+ {
+ large_digest.update(
+ &piece_data[last_piece_size]
+ , piece_size - last_piece_size);
+ }
+ sha1_hash large_hash = large_digest.final();
+ sha1_hash small_hash = small_digest.final();
+
+ typedef std::multimap<sha1_hash, int>::const_iterator map_iter;
+ map_iter begin1;
+ map_iter end1;
+ map_iter begin2;
+ map_iter end2;
+
+ // makes the lookups for the small digest and the large digest
+ boost::tie(begin1, end1) = hash_to_piece.equal_range(small_hash);
+ boost::tie(begin2, end2) = hash_to_piece.equal_range(large_hash);
+
+ // copy all potential piece indices into this vector
+ std::vector<int> matching_pieces;
+ for (map_iter i = begin1; i != end1; ++i)
+ matching_pieces.push_back(i->second);
+ for (map_iter i = begin2; i != end2; ++i)
+ matching_pieces.push_back(i->second);
+
+ // no piece matched the data in the slot
+ if (matching_pieces.empty())
+ return unassigned;
+
+ // ------------------------------------------
+ // CHECK IF THE PIECE IS IN ITS CORRECT PLACE
+ // ------------------------------------------
+
+ if (std::find(
+ matching_pieces.begin()
+ , matching_pieces.end()
+ , current_slot) != matching_pieces.end())
+ {
+ const int piece_index = current_slot;
+
+ if (have_pieces[piece_index])
+ {
+ // we have already found a piece with
+ // this index.
+ int other_slot = m_piece_to_slot[piece_index];
+ assert(other_slot >= 0);
+
+ // take one of the other matching pieces
+ // that hasn't already been assigned
+ int other_piece = -1;
+ for (std::vector<int>::iterator i = matching_pieces.begin();
+ i != matching_pieces.end(); ++i)
+ {
+ if (have_pieces[*i] || *i == piece_index) continue;
+ other_piece = *i;
+ break;
+ }
+ if (other_piece >= 0)
+ {
+ // replace the old slot with 'other_piece'
+ assert(have_pieces[other_piece] == false);
+ have_pieces[other_piece] = true;
+ m_slot_to_piece[other_slot] = other_piece;
+ m_piece_to_slot[other_piece] = other_slot;
+ ++num_pieces;
+ }
+ else
+ {
+ // this index is the only piece with this
+ // hash. The previous slot we found with
+ // this hash must be the same piece. Mark
+ // that piece as unassigned, since this slot
+ // is the correct place for the piece.
+ m_slot_to_piece[other_slot] = unassigned;
+ m_free_slots.push_back(other_slot);
+ }
+ assert(m_piece_to_slot[piece_index] != current_slot);
+ assert(m_piece_to_slot[piece_index] >= 0);
+ m_piece_to_slot[piece_index] = has_no_slot;
+#ifndef NDEBUG
+ // to make the assert happy, a few lines down
+ have_pieces[piece_index] = false;
+#endif
+ }
+ else
+ {
+ ++num_pieces;
+ }
+
+ assert(have_pieces[piece_index] == false);
+ assert(m_piece_to_slot[piece_index] == has_no_slot);
+ have_pieces[piece_index] = true;
+
+ return piece_index;
+ }
+
+ // find a matching piece that hasn't
+ // already been assigned
+ int free_piece = unassigned;
+ for (std::vector<int>::iterator i = matching_pieces.begin();
+ i != matching_pieces.end(); ++i)
+ {
+ if (have_pieces[*i]) continue;
+ free_piece = *i;
+ break;
+ }
+
+ if (free_piece >= 0)
+ {
+ assert(have_pieces[free_piece] == false);
+ assert(m_piece_to_slot[free_piece] == has_no_slot);
+ have_pieces[free_piece] = true;
+ ++num_pieces;
+
+ return free_piece;
+ }
+ else
+ {
+ assert(free_piece == unassigned);
+ return unassigned;
+ }
+ }
+
+ // check if the fastresume data is up to date
+ // if it is, use it and return true. If it
+ // isn't return false and the full check
+ // will be run
+ bool piece_manager::impl::check_fastresume(
+ aux::piece_checker_data& data
+ , std::vector<bool>& pieces
+ , int& num_pieces, bool compact_mode)
+ {
+ assert(m_info.piece_length() > 0);
+ // synchronization ------------------------------------------------------
+ boost::recursive_mutex::scoped_lock lock(m_mutex);
+ // ----------------------------------------------------------------------
+
+ INVARIANT_CHECK;
+
+ m_compact_mode = compact_mode;
+
+ // This will corrupt the storage
+ // use while debugging to find
+ // states that cannot be scanned
+ // by check_pieces.
+// m_storage.shuffle();
+
+ m_piece_to_slot.resize(m_info.num_pieces(), has_no_slot);
+ m_slot_to_piece.resize(m_info.num_pieces(), unallocated);
+ m_free_slots.clear();
+ m_unallocated_slots.clear();
+
+ pieces.clear();
+ pieces.resize(m_info.num_pieces(), false);
+ num_pieces = 0;
+
+ // if we have fast-resume info
+ // use it instead of doing the actual checking
+ if (!data.piece_map.empty()
+ && data.piece_map.size() <= m_slot_to_piece.size())
+ {
+ for (int i = 0; i < (int)data.piece_map.size(); ++i)
+ {
+ m_slot_to_piece[i] = data.piece_map[i];
+ if (data.piece_map[i] >= 0)
+ {
+ m_piece_to_slot[data.piece_map[i]] = i;
+ int found_piece = data.piece_map[i];
+
+ // if the piece is not in the unfinished list
+ // we have all of it
+ if (std::find_if(
+ data.unfinished_pieces.begin()
+ , data.unfinished_pieces.end()
+ , piece_picker::has_index(found_piece))
+ == data.unfinished_pieces.end())
+ {
+ ++num_pieces;
+ pieces[found_piece] = true;
+ }
+ }
+ else if (data.piece_map[i] == unassigned)
+ {
+ m_free_slots.push_back(i);
+ }
+ else
+ {
+ assert(data.piece_map[i] == unallocated);
+ m_unallocated_slots.push_back(i);
+ }
+ }
+
+ m_unallocated_slots.reserve(int(pieces.size() - data.piece_map.size()));
+ for (int i = (int)data.piece_map.size(); i < (int)pieces.size(); ++i)
+ {
+ m_unallocated_slots.push_back(i);
+ }
+
+ if (!m_compact_mode && !m_unallocated_slots.empty())
+ {
+ m_state = state_allocating;
+ return false;
+ }
+ else
+ {
+ m_state = state_finished;
+ return true;
+ }
+ }
+
+ m_state = state_create_files;
+ return false;
+ }
+
+ // performs the full check and full allocation
+ // (if necessary). returns true if finished and
+ // false if it should be called again
+ // the second return value is the progress the
+ // file check is at. 0 is nothing done, and 1
+ // is finished
+ std::pair<bool, float> piece_manager::impl::check_files(
+ std::vector<bool>& pieces, int& num_pieces)
+ {
+ assert(num_pieces == std::count(pieces.begin(), pieces.end(), true));
+
+ if (m_state == state_allocating)
+ {
+ if (m_compact_mode)
+ {
+ m_state = state_finished;
+ return std::make_pair(true, 1.f);
+ }
+
+ if (m_unallocated_slots.empty())
+ {
+ m_state = state_finished;
+ return std::make_pair(true, 1.f);
+ }
+
+ // if we're not in compact mode, make sure the
+ // pieces are spread out and placed at their
+ // final position.
+ assert(!m_unallocated_slots.empty());
+ allocate_slots(1);
+
+ return std::make_pair(false, 1.f - (float)m_unallocated_slots.size()
+ / (float)m_slot_to_piece.size());
+ }
+
+ if (m_state == state_create_files)
+ {
+ // first, create all missing directories
+ path last_path;
+ for (torrent_info::file_iterator file_iter = m_info.begin_files(),
+ end_iter = m_info.end_files(); file_iter != end_iter; ++file_iter)
+ {
+ path dir = (m_save_path / file_iter->path).branch_path();
+
+ // if the file is empty, just create it. But also make sure
+ // the directory exits.
+ if (dir == last_path
+ && file_iter->size == 0)
+ file(m_save_path / file_iter->path, file::out);
+
+ if (dir == last_path) continue;
+ last_path = dir;
+
+#if defined(_WIN32) && defined(UNICODE)
+ if (!exists_win(last_path))
+ create_directories_win(last_path);
+#else
+ if (!exists(last_path))
+ create_directories(last_path);
+#endif
+
+ if (file_iter->size == 0)
+ file(m_save_path / file_iter->path, file::out);
+ }
+ m_current_slot = 0;
+ m_state = state_full_check;
+ m_piece_data.resize(int(m_info.piece_length()));
+ return std::make_pair(false, 0.f);
+ }
+
+ assert(m_state == state_full_check);
+
+ // ------------------------
+ // DO THE FULL CHECK
+ // ------------------------
+
+ try
+ {
+
+ m_storage.read(
+ &m_piece_data[0]
+ , m_current_slot
+ , 0
+ , int(m_info.piece_size(m_current_slot)));
+
+ if (m_hash_to_piece.empty())
+ {
+ for (int i = 0; i < m_info.num_pieces(); ++i)
+ {
+ m_hash_to_piece.insert(std::make_pair(m_info.hash_for_piece(i), i));
+ }
+ }
+
+ int piece_index = identify_data(
+ m_piece_data
+ , m_current_slot
+ , pieces
+ , num_pieces
+ , m_hash_to_piece);
+
+ assert(num_pieces == std::count(pieces.begin(), pieces.end(), true));
+ assert(piece_index == unassigned || piece_index >= 0);
+
+ const bool this_should_move = piece_index >= 0 && m_slot_to_piece[piece_index] != unallocated;
+ const bool other_should_move = m_piece_to_slot[m_current_slot] != has_no_slot;
+
+ // check if this piece should be swapped with any other slot
+ // this section will ensure that the storage is correctly sorted
+ // libtorrent will never leave the storage in a state that
+ // requires this sorting, but other clients may.
+
+ // example of worst case:
+ // | m_current_slot = 5
+ // V
+ // +---+- - - +---+- - - +---+- -
+ // | x | | 5 | | 3 | <- piece data in slots
+ // +---+- - - +---+- - - +---+- -
+ // 3 y 5 <- slot index
+
+ // in this example, the data in the m_current_slot (5)
+ // is piece 3. It has to be moved into slot 3. The data
+ // in slot y (piece 5) should be moved into the m_current_slot.
+ // and the data in slot 3 (piece x) should be moved to slot y.
+
+ // there are three possible cases.
+ // 1. There's another piece that should be placed into this slot
+ // 2. This piece should be placed into another slot.
+ // 3. There's another piece that should be placed into this slot
+ // and this piece should be placed into another slot
+
+ // swap piece_index with this slot
+
+ // case 1
+ if (this_should_move && !other_should_move)
+ {
+ assert(piece_index != m_current_slot);
+
+ const int other_slot = piece_index;
+ assert(other_slot >= 0);
+ int other_piece = m_slot_to_piece[other_slot];
+
+ m_slot_to_piece[other_slot] = piece_index;
+ m_slot_to_piece[m_current_slot] = other_piece;
+ m_piece_to_slot[piece_index] = piece_index;
+ if (other_piece >= 0) m_piece_to_slot[other_piece] = m_current_slot;
+
+ if (other_piece == unassigned)
+ {
+ std::vector<int>::iterator i =
+ std::find(m_free_slots.begin(), m_free_slots.end(), other_slot);
+ assert(i != m_free_slots.end());
+ m_free_slots.erase(i);
+ m_free_slots.push_back(m_current_slot);
+ }
+
+ const int slot1_size = static_cast<int>(m_info.piece_size(piece_index));
+ const int slot2_size = other_piece >= 0 ? static_cast<int>(m_info.piece_size(other_piece)) : 0;
+ std::vector<char> buf1(slot1_size);
+ m_storage.read(&buf1[0], m_current_slot, 0, slot1_size);
+ if (slot2_size > 0)
+ {
+ std::vector<char> buf2(slot2_size);
+ m_storage.read(&buf2[0], piece_index, 0, slot2_size);
+ m_storage.write(&buf2[0], m_current_slot, 0, slot2_size);
+ }
+ m_storage.write(&buf1[0], piece_index, 0, slot1_size);
+ assert(m_slot_to_piece[m_current_slot] == unassigned
+ || m_piece_to_slot[m_slot_to_piece[m_current_slot]] == m_current_slot);
+ }
+ // case 2
+ else if (!this_should_move && other_should_move)
+ {
+ assert(piece_index != m_current_slot);
+
+ const int other_piece = m_current_slot;
+ const int other_slot = m_piece_to_slot[other_piece];
+ assert(other_slot >= 0);
+
+ m_slot_to_piece[m_current_slot] = other_piece;
+ m_slot_to_piece[other_slot] = piece_index;
+ m_piece_to_slot[other_piece] = m_current_slot;
+ if (piece_index >= 0) m_piece_to_slot[piece_index] = other_slot;
+
+ if (piece_index == unassigned)
+ {
+ m_free_slots.push_back(other_slot);
+ }
+
+ const int slot1_size = static_cast<int>(m_info.piece_size(other_piece));
+ const int slot2_size = piece_index >= 0 ? static_cast<int>(m_info.piece_size(piece_index)) : 0;
+ std::vector<char> buf1(slot1_size);
+ m_storage.read(&buf1[0], other_slot, 0, slot1_size);
+ if (slot2_size > 0)
+ {
+ std::vector<char> buf2(slot2_size);
+ m_storage.read(&buf2[0], m_current_slot, 0, slot2_size);
+ m_storage.write(&buf2[0], other_slot, 0, slot2_size);
+ }
+ m_storage.write(&buf1[0], m_current_slot, 0, slot1_size);
+ assert(m_slot_to_piece[m_current_slot] == unassigned
+ || m_piece_to_slot[m_slot_to_piece[m_current_slot]] == m_current_slot);
+ }
+ else if (this_should_move && other_should_move)
+ {
+ assert(piece_index != m_current_slot);
+ assert(piece_index >= 0);
+
+ const int piece1 = m_slot_to_piece[piece_index];
+ const int piece2 = m_current_slot;
+ const int slot1 = piece_index;
+ const int slot2 = m_piece_to_slot[piece2];
+
+ assert(slot1 >= 0);
+ assert(slot2 >= 0);
+ assert(piece2 >= 0);
+
+ if (slot1 == slot2)
+ {
+ // this means there are only two pieces involved in the swap
+ assert(piece1 >= 0);
+
+ // movement diagram:
+ // +-------------------------------+
+ // | |
+ // +--> slot1 --> m_current_slot --+
+
+ m_slot_to_piece[slot1] = piece_index;
+ m_slot_to_piece[m_current_slot] = piece1;
+
+ m_piece_to_slot[piece_index] = slot1;
+ m_piece_to_slot[piece1] = m_current_slot;
+
+ assert(piece1 == m_current_slot);
+ assert(piece_index == slot1);
+
+ const int slot1_size = static_cast<int>(m_info.piece_size(piece1));
+ const int slot3_size = static_cast<int>(m_info.piece_size(piece_index));
+ std::vector<char> buf1(static_cast<int>(slot1_size));
+ std::vector<char> buf2(static_cast<int>(slot3_size));
+
+ m_storage.read(&buf2[0], m_current_slot, 0, slot3_size);
+ m_storage.read(&buf1[0], slot1, 0, slot1_size);
+ m_storage.write(&buf1[0], m_current_slot, 0, slot1_size);
+ m_storage.write(&buf2[0], slot1, 0, slot3_size);
+
+ assert(m_slot_to_piece[m_current_slot] == unassigned
+ || m_piece_to_slot[m_slot_to_piece[m_current_slot]] == m_current_slot);
+ }
+ else
+ {
+ assert(slot1 != slot2);
+ assert(piece1 != piece2);
+
+ // movement diagram:
+ // +-----------------------------------------+
+ // | |
+ // +--> slot1 --> slot2 --> m_current_slot --+
+
+ m_slot_to_piece[slot1] = piece_index;
+ m_slot_to_piece[slot2] = piece1;
+ m_slot_to_piece[m_current_slot] = piece2;
+
+ m_piece_to_slot[piece_index] = slot1;
+ m_piece_to_slot[m_current_slot] = piece2;
+ if (piece1 >= 0) m_piece_to_slot[piece1] = slot2;
+
+ if (piece1 == unassigned)
+ {
+ std::vector<int>::iterator i =
+ std::find(m_free_slots.begin(), m_free_slots.end(), slot1);
+ assert(i != m_free_slots.end());
+ m_free_slots.erase(i);
+ m_free_slots.push_back(slot2);
+ }
+
+ const int slot1_size = piece1 >= 0 ? static_cast<int>(m_info.piece_size(piece1)) : 0;
+ const int slot2_size = static_cast<int>(m_info.piece_size(piece2));
+ const int slot3_size = static_cast<int>(m_info.piece_size(piece_index));
+
+ std::vector<char> buf1(static_cast<int>(m_info.piece_length()));
+ std::vector<char> buf2(static_cast<int>(m_info.piece_length()));
+
+ m_storage.read(&buf2[0], m_current_slot, 0, slot3_size);
+ m_storage.read(&buf1[0], slot2, 0, slot2_size);
+ m_storage.write(&buf1[0], m_current_slot, 0, slot2_size);
+ if (slot1_size > 0)
+ {
+ m_storage.read(&buf1[0], slot1, 0, slot1_size);
+ m_storage.write(&buf1[0], slot2, 0, slot1_size);
+ }
+ m_storage.write(&buf2[0], slot1, 0, slot3_size);
+ assert(m_slot_to_piece[m_current_slot] == unassigned
+ || m_piece_to_slot[m_slot_to_piece[m_current_slot]] == m_current_slot);
+ }
+ }
+ else
+ {
+ assert(m_piece_to_slot[m_current_slot] == has_no_slot || piece_index != m_current_slot);
+ assert(m_slot_to_piece[m_current_slot] == unallocated);
+ assert(piece_index == unassigned || m_piece_to_slot[piece_index] == has_no_slot);
+
+ // the slot was identified as piece 'piece_index'
+ if (piece_index != unassigned)
+ m_piece_to_slot[piece_index] = m_current_slot;
+ else
+ m_free_slots.push_back(m_current_slot);
+
+ m_slot_to_piece[m_current_slot] = piece_index;
+
+ assert(m_slot_to_piece[m_current_slot] == unassigned
+ || m_piece_to_slot[m_slot_to_piece[m_current_slot]] == m_current_slot);
+ }
+ }
+ catch (file_error&)
+ {
+ // find the file that failed, and skip all the blocks in that file
+ size_type file_offset = 0;
+ size_type current_offset = m_current_slot * m_info.piece_length();
+ for (torrent_info::file_iterator i = m_info.begin_files();
+ i != m_info.end_files(); ++i)
+ {
+ file_offset += i->size;
+ if (file_offset > current_offset) break;
+ }
+
+ assert(file_offset > current_offset);
+ int skip_blocks = static_cast<int>(
+ (file_offset - current_offset + m_info.piece_length() - 1)
+ / m_info.piece_length());
+
+ for (int i = m_current_slot; i < m_current_slot + skip_blocks; ++i)
+ {
+ assert(m_slot_to_piece[i] == unallocated);
+ m_unallocated_slots.push_back(i);
+ }
+
+ // current slot will increase by one at the end of the for-loop too
+ m_current_slot += skip_blocks - 1;
+ }
+ ++m_current_slot;
+
+ if (m_current_slot >= m_info.num_pieces())
+ {
+ assert(m_current_slot == m_info.num_pieces());
+
+ // clear the memory we've been using
+ std::vector<char>().swap(m_piece_data);
+ std::multimap<sha1_hash, int>().swap(m_hash_to_piece);
+ m_state = state_allocating;
+ assert(num_pieces == std::count(pieces.begin(), pieces.end(), true));
+ return std::make_pair(false, 1.f);
+ }
+
+ assert(num_pieces == std::count(pieces.begin(), pieces.end(), true));
+
+ return std::make_pair(false, (float)m_current_slot / m_info.num_pieces());
+ }
+
+ bool piece_manager::check_fastresume(
+ aux::piece_checker_data& d, std::vector<bool>& pieces
+ , int& num_pieces, bool compact_mode)
+ {
+ return m_pimpl->check_fastresume(d, pieces, num_pieces, compact_mode);
+ }
+
+ std::pair<bool, float> piece_manager::check_files(
+ std::vector<bool>& pieces
+ , int& num_pieces)
+ {
+ return m_pimpl->check_files(pieces, num_pieces);
+ }
+
+ int piece_manager::impl::allocate_slot_for_piece(int piece_index)
+ {
+ // synchronization ------------------------------------------------------
+ boost::recursive_mutex::scoped_lock lock(m_mutex);
+ // ----------------------------------------------------------------------
+
+ // INVARIANT_CHECK;
+
+ assert(piece_index >= 0);
+ assert(piece_index < (int)m_piece_to_slot.size());
+ assert(m_piece_to_slot.size() == m_slot_to_piece.size());
+
+ int slot_index = m_piece_to_slot[piece_index];
+
+ if (slot_index != has_no_slot)
+ {
+ assert(slot_index >= 0);
+ assert(slot_index < (int)m_slot_to_piece.size());
+ return slot_index;
+ }
+
+ if (m_free_slots.empty())
+ {
+ allocate_slots(1);
+ assert(!m_free_slots.empty());
+ }
+
+ std::vector<int>::iterator iter(
+ std::find(
+ m_free_slots.begin()
+ , m_free_slots.end()
+ , piece_index));
+
+ if (iter == m_free_slots.end())
+ {
+ assert(m_slot_to_piece[piece_index] != unassigned);
+ assert(!m_free_slots.empty());
+ iter = m_free_slots.end() - 1;
+
+ // special case to make sure we don't use the last slot
+ // when we shouldn't, since it's smaller than ordinary slots
+ if (*iter == m_info.num_pieces() - 1 && piece_index != *iter)
+ {
+ if (m_free_slots.size() == 1)
+ allocate_slots(1);
+ assert(m_free_slots.size() > 1);
+ // assumes that all allocated slots
+ // are put at the end of the free_slots vector
+ iter = m_free_slots.end() - 1;
+ }
+ }
+
+ slot_index = *iter;
+ m_free_slots.erase(iter);
+
+ assert(m_slot_to_piece[slot_index] == unassigned);
+
+ m_slot_to_piece[slot_index] = piece_index;
+ m_piece_to_slot[piece_index] = slot_index;
+
+ // there is another piece already assigned to
+ // the slot we are interested in, swap positions
+ if (slot_index != piece_index
+ && m_slot_to_piece[piece_index] >= 0)
+ {
+
+#if !defined(NDEBUG) && defined(TORRENT_STORAGE_DEBUG)
+ std::stringstream s;
+
+ s << "there is another piece at our slot, swapping..";
+
+ s << "\n piece_index: ";
+ s << piece_index;
+ s << "\n slot_index: ";
+ s << slot_index;
+ s << "\n piece at our slot: ";
+ s << m_slot_to_piece[piece_index];
+ s << "\n";
+
+ print_to_log(s.str());
+ debug_log();
+#endif
+
+ int piece_at_our_slot = m_slot_to_piece[piece_index];
+ assert(m_piece_to_slot[piece_at_our_slot] == piece_index);
+
+ std::swap(
+ m_slot_to_piece[piece_index]
+ , m_slot_to_piece[slot_index]);
+
+ std::swap(
+ m_piece_to_slot[piece_index]
+ , m_piece_to_slot[piece_at_our_slot]);
+
+ const int slot_size = static_cast<int>(m_info.piece_size(slot_index));
+ std::vector<char> buf(slot_size);
+ m_storage.read(&buf[0], piece_index, 0, slot_size);
+ m_storage.write(&buf[0], slot_index, 0, slot_size);
+
+ assert(m_slot_to_piece[piece_index] == piece_index);
+ assert(m_piece_to_slot[piece_index] == piece_index);
+
+ slot_index = piece_index;
+
+#if !defined(NDEBUG) && defined(TORRENT_STORAGE_DEBUG)
+ debug_log();
+#endif
+ }
+
+ assert(slot_index >= 0);
+ assert(slot_index < (int)m_slot_to_piece.size());
+ return slot_index;
+ }
+
+ namespace
+ {
+ // this is used to notify potential other
+ // threads that the allocation-function has exited
+ struct allocation_syncronization
+ {
+ allocation_syncronization(
+ bool& flag
+ , boost::condition& cond
+ , boost::mutex& monitor)
+ : m_flag(flag)
+ , m_cond(cond)
+ , m_monitor(monitor)
+ {
+ boost::mutex::scoped_lock lock(m_monitor);
+
+ while (m_flag)
+ m_cond.wait(lock);
+
+ m_flag = true;
+ }
+
+ ~allocation_syncronization()
+ {
+ boost::mutex::scoped_lock lock(m_monitor);
+ m_flag = false;
+ m_cond.notify_one();
+ }
+
+ bool& m_flag;
+ boost::condition& m_cond;
+ boost::mutex& m_monitor;
+ };
+
+ }
+
+ void piece_manager::impl::allocate_slots(int num_slots)
+ {
+ assert(num_slots > 0);
+
+ // this object will syncronize the allocation with
+ // potential other threads
+ allocation_syncronization sync_obj(
+ m_allocating
+ , m_allocating_condition
+ , m_allocating_monitor);
+
+ // synchronization ------------------------------------------------------
+ boost::recursive_mutex::scoped_lock lock(m_mutex);
+ // ----------------------------------------------------------------------
+
+ // INVARIANT_CHECK;
+
+ assert(!m_unallocated_slots.empty());
+
+ const int piece_size = static_cast<int>(m_info.piece_length());
+
+ std::vector<char>& buffer = m_scratch_buffer;
+ buffer.resize(piece_size);
+
+ for (int i = 0; i < num_slots && !m_unallocated_slots.empty(); ++i)
+ {
+ int pos = m_unallocated_slots.front();
+ // int piece_pos = pos;
+ bool write_back = false;
+
+ int new_free_slot = pos;
+ if (m_piece_to_slot[pos] != has_no_slot)
+ {
+ assert(m_piece_to_slot[pos] >= 0);
+ m_storage.read(&buffer[0], m_piece_to_slot[pos], 0, static_cast<int>(m_info.piece_size(pos)));
+ new_free_slot = m_piece_to_slot[pos];
+ m_slot_to_piece[pos] = pos;
+ m_piece_to_slot[pos] = pos;
+ write_back = true;
+ }
+ m_unallocated_slots.erase(m_unallocated_slots.begin());
+ m_slot_to_piece[new_free_slot] = unassigned;
+ m_free_slots.push_back(new_free_slot);
+
+ if (write_back || m_fill_mode)
+ m_storage.write(&buffer[0], pos, 0, static_cast<int>(m_info.piece_size(pos)));
+ }
+
+ assert(m_free_slots.size() > 0);
+ }
+
+ void piece_manager::allocate_slots(int num_slots)
+ {
+ m_pimpl->allocate_slots(num_slots);
+ }
+
+ path const& piece_manager::save_path() const
+ {
+ return m_pimpl->save_path();
+ }
+
+ bool piece_manager::move_storage(path const& save_path)
+ {
+ return m_pimpl->move_storage(save_path);
+ }
+
+#ifndef NDEBUG
+ void piece_manager::impl::check_invariant() const
+ {
+ // synchronization ------------------------------------------------------
+ boost::recursive_mutex::scoped_lock lock(m_mutex);
+ // ----------------------------------------------------------------------
+ if (m_piece_to_slot.empty()) return;
+
+ assert((int)m_piece_to_slot.size() == m_info.num_pieces());
+ assert((int)m_slot_to_piece.size() == m_info.num_pieces());
+
+ for (std::vector<int>::const_iterator i = m_free_slots.begin();
+ i != m_free_slots.end(); ++i)
+ {
+ assert(*i < (int)m_slot_to_piece.size());
+ assert(*i >= 0);
+ assert(m_slot_to_piece[*i] == unassigned);
+ assert(std::find(i+1, m_free_slots.end(), *i)
+ == m_free_slots.end());
+ }
+
+ for (std::vector<int>::const_iterator i = m_unallocated_slots.begin();
+ i != m_unallocated_slots.end(); ++i)
+ {
+ assert(*i < (int)m_slot_to_piece.size());
+ assert(*i >= 0);
+ assert(m_slot_to_piece[*i] == unallocated);
+ assert(std::find(i+1, m_unallocated_slots.end(), *i)
+ == m_unallocated_slots.end());
+ }
+
+ for (int i = 0; i < m_info.num_pieces(); ++i)
+ {
+ // Check domain of piece_to_slot's elements
+ if (m_piece_to_slot[i] != has_no_slot)
+ {
+ assert(m_piece_to_slot[i] >= 0);
+ assert(m_piece_to_slot[i] < (int)m_slot_to_piece.size());
+ }
+
+ // Check domain of slot_to_piece's elements
+ if (m_slot_to_piece[i] != unallocated
+ && m_slot_to_piece[i] != unassigned)
+ {
+ assert(m_slot_to_piece[i] >= 0);
+ assert(m_slot_to_piece[i] < (int)m_piece_to_slot.size());
+ }
+
+ // do more detailed checks on piece_to_slot
+ if (m_piece_to_slot[i] >= 0)
+ {
+ assert(m_slot_to_piece[m_piece_to_slot[i]] == i);
+ if (m_piece_to_slot[i] != i)
+ {
+ assert(m_slot_to_piece[i] == unallocated);
+ }
+ }
+ else
+ {
+ assert(m_piece_to_slot[i] == has_no_slot);
+ }
+
+ // do more detailed checks on slot_to_piece
+
+ if (m_slot_to_piece[i] >= 0)
+ {
+ assert(m_slot_to_piece[i] < (int)m_piece_to_slot.size());
+ assert(m_piece_to_slot[m_slot_to_piece[i]] == i);
+#ifdef TORRENT_STORAGE_DEBUG
+ assert(
+ std::find(
+ m_unallocated_slots.begin()
+ , m_unallocated_slots.end()
+ , i) == m_unallocated_slots.end()
+ );
+ assert(
+ std::find(
+ m_free_slots.begin()
+ , m_free_slots.end()
+ , i) == m_free_slots.end()
+ );
+#endif
+ }
+ else if (m_slot_to_piece[i] == unallocated)
+ {
+#ifdef TORRENT_STORAGE_DEBUG
+ assert(m_unallocated_slots.empty()
+ || (std::find(
+ m_unallocated_slots.begin()
+ , m_unallocated_slots.end()
+ , i) != m_unallocated_slots.end())
+ );
+#endif
+ }
+ else if (m_slot_to_piece[i] == unassigned)
+ {
+#ifdef TORRENT_STORAGE_DEBUG
+ assert(
+ std::find(
+ m_free_slots.begin()
+ , m_free_slots.end()
+ , i) != m_free_slots.end()
+ );
+#endif
+ }
+ else
+ {
+ assert(false && "m_slot_to_piece[i] is invalid");
+ }
+ }
+ }
+
+#ifdef TORRENT_STORAGE_DEBUG
+ void piece_manager::impl::debug_log() const
+ {
+ std::stringstream s;
+
+ s << "index\tslot\tpiece\n";
+
+ for (int i = 0; i < m_info.num_pieces(); ++i)
+ {
+ s << i << "\t" << m_slot_to_piece[i] << "\t";
+ s << m_piece_to_slot[i] << "\n";
+ }
+
+ s << "---------------------------------\n";
+
+ print_to_log(s.str());
+ }
+#endif
+#endif
+} // namespace libtorrent
+
diff --git a/library/test.py b/library/test.py
new file mode 100644
index 000000000..5c0532dcc
--- /dev/null
+++ b/library/test.py
@@ -0,0 +1,24 @@
+#/*
+#Copyright: A. Zakai ('Kripken') <kripkensteiner@gmail.com> http://6thsenseless.blogspot.com
+#
+#2006-15-9
+#
+#This code is licensed under the terms of the GNU General Public License (GPL),
+#version 2 or above; See /usr/share/common-licenses/GPL , or see
+#http://www.fsf.org/licensing/licenses/gpl.html
+#*/
+
+
+import torrent
+from time import sleep
+
+torrent.init()
+
+myTorrent = torrent.addTorrent("ubuntu.torrent")
+
+while True:
+ print "STATE:"
+ print torrent.getState(myTorrent)
+ print ""
+
+ sleep(1)
diff --git a/library/testit b/library/testit
new file mode 100755
index 000000000..dea5ec9d1
--- /dev/null
+++ b/library/testit
@@ -0,0 +1,5 @@
+echo ""
+echo "Run!"
+echo ""
+#LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH python test.py
+python test.py
diff --git a/library/torrent.cpp b/library/torrent.cpp
new file mode 100755
index 000000000..39936ffca
--- /dev/null
+++ b/library/torrent.cpp
@@ -0,0 +1,2186 @@
+/*
+
+Copyright (c) 2003, 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.
+
+*/
+#include <ctime>
+#include <iostream>
+#include <fstream>
+#include <iomanip>
+#include <iterator>
+#include <algorithm>
+#include <set>
+#include <cctype>
+#include <numeric>
+
+#ifdef _MSC_VER
+#pragma warning(push, 1)
+#endif
+
+#include <boost/lexical_cast.hpp>
+#include <boost/filesystem/convenience.hpp>
+#include <boost/bind.hpp>
+#include <boost/thread/mutex.hpp>
+
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+#include "libtorrent/torrent_handle.hpp"
+#include "libtorrent/session.hpp"
+#include "libtorrent/torrent_info.hpp"
+#include "libtorrent/tracker_manager.hpp"
+#include "libtorrent/bencode.hpp"
+#include "libtorrent/hasher.hpp"
+#include "libtorrent/entry.hpp"
+#include "libtorrent/peer.hpp"
+#include "libtorrent/bt_peer_connection.hpp"
+#include "libtorrent/web_peer_connection.hpp"
+#include "libtorrent/peer_id.hpp"
+#include "libtorrent/alert.hpp"
+#include "libtorrent/identify_client.hpp"
+#include "libtorrent/alert_types.hpp"
+#include "libtorrent/aux_/session_impl.hpp"
+
+using namespace libtorrent;
+using namespace boost::posix_time;
+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;
+
+// PROFILING CODE
+
+#ifdef TORRENT_PROFILE
+#include <boost/date_time/posix_time/ptime.hpp>
+
+namespace libtorrent
+{
+ namespace
+ {
+ using boost::posix_time::ptime;
+ using boost::posix_time::time_duration;
+ using boost::posix_time::microsec_clock;
+ std::vector<std::pair<ptime, std::string> > checkpoints;
+ }
+
+ void add_checkpoint(std::string const& str)
+ {
+ checkpoints.push_back(std::make_pair(microsec_clock::universal_time(), str));
+ }
+
+ void print_checkpoints()
+ {
+ for (std::vector<std::pair<ptime, std::string> >::iterator i
+ = checkpoints.begin(); i != checkpoints.end(); ++i)
+ {
+ ptime cur = i->first;
+ if (i + 1 != checkpoints.end())
+ {
+ time_duration diff = (i + 1)->first - cur;
+ std::cout << diff.total_microseconds() << " " << i->second << "\n";
+ }
+ else
+ {
+ std::cout << " " << i->second << "\n";
+ }
+ }
+ }
+}
+
+#endif
+
+namespace
+{
+
+ enum
+ {
+ // wait 60 seconds before retrying a failed tracker
+ tracker_retry_delay_min = 60
+ // when tracker_failed_max trackers
+ // has failed, wait 10 minutes instead
+ , tracker_retry_delay_max = 10 * 60
+ , tracker_failed_max = 5
+ };
+
+ int calculate_block_size(const torrent_info& i, int default_block_size)
+ {
+ if (default_block_size < 1024) default_block_size = 1024;
+
+ // if pieces are too small, adjust the block size
+ if (i.piece_length() < default_block_size)
+ {
+ return static_cast<int>(i.piece_length());
+ }
+
+ // if pieces are too large, adjust the block size
+ if (i.piece_length() / default_block_size > piece_picker::max_blocks_per_piece)
+ {
+ return static_cast<int>(i.piece_length() / piece_picker::max_blocks_per_piece);
+ }
+
+ // otherwise, go with the default
+ return default_block_size;
+ }
+
+ struct find_peer_by_ip
+ {
+ find_peer_by_ip(tcp::endpoint const& a, const torrent* t)
+ : ip(a)
+ , tor(t)
+ { assert(t != 0); }
+
+ bool operator()(const session_impl::connection_map::value_type& c) const
+ {
+ tcp::endpoint sender = c.first->remote_endpoint();
+ if (sender.address() != ip.address()) return false;
+ if (tor != c.second->associated_torrent().lock().get()) return false;
+ return true;
+ }
+
+ tcp::endpoint const& ip;
+ torrent const* tor;
+ };
+
+ struct peer_by_id
+ {
+ peer_by_id(const peer_id& i): pid(i) {}
+
+ bool operator()(const std::pair<tcp::endpoint, peer_connection*>& p) const
+ {
+ if (p.second->pid() != pid) return false;
+ // have a special case for all zeros. We can have any number
+ // of peers with that pid, since it's used to indicate no pid.
+ if (std::count(pid.begin(), pid.end(), 0) == 20) return false;
+ return true;
+ }
+
+ peer_id const& pid;
+ };
+}
+
+namespace libtorrent
+{
+ torrent::torrent(
+ session_impl& ses
+ , aux::checker_impl& checker
+ , torrent_info const& tf
+ , boost::filesystem::path const& save_path
+ , tcp::endpoint const& net_interface
+ , bool compact_mode
+ , int block_size
+ , session_settings const& s)
+ : m_torrent_file(tf)
+ , m_abort(false)
+ , m_paused(false)
+ , m_just_paused(false)
+ , m_event(tracker_request::started)
+ , m_block_size(0)
+ , m_storage(0)
+ , m_next_request(second_clock::universal_time())
+ , m_duration(1800)
+ , m_complete(-1)
+ , m_incomplete(-1)
+ , m_host_resolver(ses.m_selector)
+#ifndef TORRENT_DISABLE_DHT
+ , m_dht_announce_timer(ses.m_selector)
+#endif
+ , m_policy()
+ , m_ses(ses)
+ , m_checker(checker)
+ , m_picker(0)
+ , m_trackers(m_torrent_file.trackers())
+ , m_last_working_tracker(-1)
+ , m_currently_trying_tracker(0)
+ , m_failed_trackers(0)
+ , m_time_scaler(0)
+ , m_priority(.5)
+ , m_num_pieces(0)
+ , m_got_tracker_response(false)
+ , m_ratio(0.f)
+ , m_total_failed_bytes(0)
+ , m_total_redundant_bytes(0)
+ , m_net_interface(net_interface.address(), 0)
+ , m_upload_bandwidth_limit(std::numeric_limits<int>::max())
+ , m_download_bandwidth_limit(std::numeric_limits<int>::max())
+ , m_save_path(complete(save_path))
+ , m_compact_mode(compact_mode)
+ , m_metadata_progress(0)
+ , m_metadata_size(0)
+ , m_default_block_size(block_size)
+ , m_connections_initialized(true)
+ , m_settings(s)
+ {
+#ifndef NDEBUG
+ 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
+ // distributes resources, i.e. on average in 0.5 seconds
+ m_connections_quota.given = 100;
+ m_uploads_quota.max = std::numeric_limits<int>::max();
+ m_connections_quota.max = std::numeric_limits<int>::max();
+
+ m_dl_bandwidth_quota.min = 100;
+ m_dl_bandwidth_quota.max = resource_request::inf;
+
+ if (m_ses.m_download_rate == -1)
+ {
+ m_dl_bandwidth_quota.given = resource_request::inf;
+ }
+ else
+ {
+ m_dl_bandwidth_quota.given = 400;
+ }
+
+ m_ul_bandwidth_quota.min = 100;
+ m_ul_bandwidth_quota.max = resource_request::inf;
+
+ if (m_ses.m_upload_rate == -1)
+ {
+ m_ul_bandwidth_quota.given = resource_request::inf;
+ }
+ else
+ {
+ m_ul_bandwidth_quota.given = 400;
+ }
+
+ m_policy.reset(new policy(this));
+ init();
+
+#ifndef TORRENT_DISABLE_DHT
+ if (!tf.priv())
+ {
+ m_dht_announce_timer.expires_from_now(seconds(10));
+ m_dht_announce_timer.async_wait(bind(&torrent::on_dht_announce, this, _1));
+ }
+#endif
+ }
+
+ torrent::torrent(
+ session_impl& ses
+ , aux::checker_impl& checker
+ , char const* tracker_url
+ , sha1_hash const& info_hash
+ , boost::filesystem::path const& save_path
+ , tcp::endpoint const& net_interface
+ , bool compact_mode
+ , int block_size
+ , session_settings const& s)
+ : m_torrent_file(info_hash)
+ , m_abort(false)
+ , m_paused(false)
+ , m_just_paused(false)
+ , m_event(tracker_request::started)
+ , m_block_size(0)
+ , m_storage(0)
+ , m_next_request(second_clock::universal_time())
+ , m_duration(1800)
+ , m_complete(-1)
+ , m_incomplete(-1)
+ , m_host_resolver(ses.m_selector)
+#ifndef TORRENT_DISABLE_DHT
+ , m_dht_announce_timer(ses.m_selector)
+#endif
+ , m_policy()
+ , m_ses(ses)
+ , m_checker(checker)
+ , m_picker(0)
+ , m_last_working_tracker(-1)
+ , m_currently_trying_tracker(0)
+ , m_failed_trackers(0)
+ , m_time_scaler(0)
+ , m_priority(.5)
+ , m_num_pieces(0)
+ , m_got_tracker_response(false)
+ , m_ratio(0.f)
+ , m_total_failed_bytes(0)
+ , m_total_redundant_bytes(0)
+ , m_net_interface(net_interface.address(), 0)
+ , m_upload_bandwidth_limit(std::numeric_limits<int>::max())
+ , m_download_bandwidth_limit(std::numeric_limits<int>::max())
+ , m_save_path(complete(save_path))
+ , m_compact_mode(compact_mode)
+ , m_metadata_progress(0)
+ , m_metadata_size(0)
+ , m_default_block_size(block_size)
+ , m_connections_initialized(false)
+ , m_settings(s)
+ {
+#ifndef NDEBUG
+ 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
+ // distributes resources, i.e. on average in 0.5 seconds
+ m_connections_quota.given = 100;
+ m_uploads_quota.max = std::numeric_limits<int>::max();
+ m_connections_quota.max = std::numeric_limits<int>::max();
+
+ m_dl_bandwidth_quota.min = 100;
+ m_dl_bandwidth_quota.max = resource_request::inf;
+
+ if (m_ses.m_download_rate == -1)
+ {
+ m_dl_bandwidth_quota.given = resource_request::inf;
+ }
+ else
+ {
+ m_dl_bandwidth_quota.given = 400;
+ }
+
+ m_ul_bandwidth_quota.min = 100;
+ m_ul_bandwidth_quota.max = resource_request::inf;
+
+
+ if (m_ses.m_upload_rate == -1)
+ {
+ m_ul_bandwidth_quota.given = resource_request::inf;
+ }
+ else
+ {
+ m_ul_bandwidth_quota.given = 400;
+ }
+
+ m_trackers.push_back(announce_entry(tracker_url));
+ m_requested_metadata.resize(256, 0);
+
+ m_policy.reset(new policy(this));
+ m_torrent_file.add_tracker(tracker_url);
+#ifndef TORRENT_DISABLE_DHT
+ m_dht_announce_timer.expires_from_now(seconds(10));
+ m_dht_announce_timer.async_wait(bind(&torrent::on_dht_announce, this, _1));
+#endif
+ }
+
+ torrent::~torrent()
+ {
+ // The invariant can't be maintained here, since the torrent
+ // is being destructed, all weak references to it have been
+ // reset, which means that all its peers already have an
+ // invalidated torrent pointer (so it cannot be verified to be correct)
+
+ // i.e. the invariant can only be maintained if all connections have
+ // been closed by the time the torrent is destructed. And they are
+ // supposed to be closed. So we can still do the invariant check.
+
+ assert(m_connections.empty());
+
+ INVARIANT_CHECK;
+
+ if (m_ses.is_aborted())
+ m_abort = true;
+ if (!m_connections.empty())
+ disconnect_all();
+ }
+
+ 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_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)
+ , static_cast<int>((m_torrent_file.total_size()+m_block_size-1)/m_block_size)));
+
+ std::vector<std::string> const& url_seeds = m_torrent_file.url_seeds();
+ std::copy(url_seeds.begin(), url_seeds.end(), std::inserter(m_web_seeds
+ , m_web_seeds.begin()));
+ }
+
+ void torrent::use_interface(const char* net_interface)
+ {
+ INVARIANT_CHECK;
+
+ m_net_interface = tcp::endpoint(address::from_string(net_interface), 0);
+ }
+
+#ifndef TORRENT_DISABLE_DHT
+
+ void torrent::on_dht_announce_response_disp(boost::weak_ptr<libtorrent::torrent> t
+ , std::vector<tcp::endpoint> const& peers)
+ {
+ boost::shared_ptr<libtorrent::torrent> tor = t.lock();
+ if (!tor) return;
+ tor->on_dht_announce_response(peers);
+ }
+
+ void torrent::on_dht_announce(asio::error const& e)
+ {
+ if (e) return;
+ m_dht_announce_timer.expires_from_now(boost::posix_time::minutes(30));
+ m_dht_announce_timer.async_wait(bind(&torrent::on_dht_announce, this, _1));
+ if (!m_ses.m_dht) return;
+ // TODO: There should be a way to abort an announce operation on the dht.
+ // when the torrent is destructed
+ boost::weak_ptr<torrent> self(shared_from_this());
+ m_ses.m_dht->announce(m_torrent_file.info_hash()
+ , m_ses.m_listen_interface.port()
+ , bind(&torrent::on_dht_announce_response_disp, self, _1));
+ }
+
+ void torrent::on_dht_announce_response(std::vector<tcp::endpoint> const& peers)
+ {
+ std::for_each(peers.begin(), peers.end(), bind(
+ &policy::peer_from_tracker, boost::ref(m_policy), _1, peer_id(0)));
+ }
+
+#endif
+
+ // returns true if it is time for this torrent to make another
+ // tracker request
+ bool torrent::should_request()
+ {
+ INVARIANT_CHECK;
+
+ if (m_torrent_file.trackers().empty()) return false;
+
+ if (m_just_paused)
+ {
+ m_just_paused = false;
+ return true;
+ }
+ return !m_paused &&
+ m_next_request < second_clock::universal_time();
+ }
+
+ void torrent::tracker_warning(std::string const& msg)
+ {
+ INVARIANT_CHECK;
+
+ if (m_ses.m_alerts.should_post(alert::warning))
+ {
+ m_ses.m_alerts.post_alert(tracker_warning_alert(get_handle(), msg));
+ }
+ }
+
+ void torrent::tracker_response(
+ tracker_request const&
+ , std::vector<peer_entry>& peer_list
+ , int interval
+ , int complete
+ , int incomplete)
+ {
+ INVARIANT_CHECK;
+
+ session_impl::mutex_t::scoped_lock l(m_ses.m_mutex);
+
+ m_failed_trackers = 0;
+ // less than 5 minutes announce intervals
+ // are insane.
+ if (interval < 60 * 5) interval = 60 * 5;
+
+ m_last_working_tracker
+ = prioritize_tracker(m_currently_trying_tracker);
+ m_currently_trying_tracker = 0;
+
+ m_duration = interval;
+ m_next_request = second_clock::universal_time() + boost::posix_time::seconds(m_duration);
+
+ if (complete >= 0) m_complete = complete;
+ if (incomplete >= 0) m_incomplete = incomplete;
+
+ // connect to random peers from the list
+ std::random_shuffle(peer_list.begin(), peer_list.end());
+
+#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
+ std::stringstream s;
+ s << "TRACKER RESPONSE:\n"
+ "interval: " << m_duration << "\n"
+ "peers:\n";
+ for (std::vector<peer_entry>::const_iterator i = peer_list.begin();
+ i != peer_list.end(); ++i)
+ {
+ s << " " << std::setfill(' ') << std::setw(16) << i->ip
+ << " " << std::setw(5) << std::dec << i->port << " ";
+ if (!i->pid.is_all_zeros()) s << " " << i->pid << " " << identify_client(i->pid);
+ s << "\n";
+ }
+ debug_log(s.str());
+#endif
+ // for each of the peers we got from the tracker
+ for (std::vector<peer_entry>::iterator i = peer_list.begin();
+ i != peer_list.end(); ++i)
+ {
+ // don't make connections to ourself
+ if (i->pid == m_ses.get_peer_id())
+ continue;
+
+ tcp::endpoint a(address::from_string(i->ip), i->port);
+
+ if (m_ses.m_ip_filter.access(a.address()) & ip_filter::blocked)
+ {
+#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
+ debug_log("blocked ip from tracker: " + i->ip);
+#endif
+ continue;
+ }
+
+ m_policy->peer_from_tracker(a, i->pid);
+ }
+
+ if (m_ses.m_alerts.should_post(alert::info))
+ {
+ std::stringstream s;
+ s << "Got response from tracker: "
+ << m_trackers[m_last_working_tracker].url;
+ m_ses.m_alerts.post_alert(tracker_reply_alert(
+ get_handle(), s.str()));
+ }
+ m_got_tracker_response = true;
+ }
+
+ size_type torrent::bytes_left() const
+ {
+ // if we don't have the metadata yet, we
+ // cannot tell how big the torrent is.
+ if (!valid_metadata()) return -1;
+ return m_torrent_file.total_size()
+ - quantized_bytes_done();
+ }
+
+ size_type torrent::quantized_bytes_done() const
+ {
+ INVARIANT_CHECK;
+
+ if (!valid_metadata()) return 0;
+
+ assert(m_picker.get());
+
+ if (m_torrent_file.num_pieces() == 0)
+ return 0;
+ const int last_piece = m_torrent_file.num_pieces() - 1;
+
+ size_type total_done
+ = m_num_pieces * m_torrent_file.piece_length();
+
+ // if we have the last piece, we have to correct
+ // the amount we have, since the first calculation
+ // assumed all pieces were of equal size
+ if (m_have_pieces[last_piece])
+ {
+ int corr = m_torrent_file.piece_size(last_piece)
+ - m_torrent_file.piece_length();
+ total_done += corr;
+ }
+ return total_done;
+ }
+
+ // the first value is the total number of bytes downloaded
+ // the second value is the number of bytes of those that haven't
+ // been filtered as not wanted we have downloaded
+ tuple<size_type, size_type> torrent::bytes_done() const
+ {
+ INVARIANT_CHECK;
+
+ if (!valid_metadata()) return tuple<size_type, size_type>(0,0);
+
+ assert(m_picker.get());
+
+ if (m_torrent_file.num_pieces() == 0)
+ return tuple<size_type, size_type>(0,0);
+ const int last_piece = m_torrent_file.num_pieces() - 1;
+
+ size_type wanted_done = (m_num_pieces - m_picker->num_have_filtered())
+ * m_torrent_file.piece_length();
+
+ size_type total_done
+ = m_num_pieces * m_torrent_file.piece_length();
+
+ // if we have the last piece, we have to correct
+ // the amount we have, since the first calculation
+ // assumed all pieces were of equal size
+ if (m_have_pieces[last_piece])
+ {
+ int corr = m_torrent_file.piece_size(last_piece)
+ - m_torrent_file.piece_length();
+ total_done += corr;
+ if (!m_picker->is_filtered(last_piece))
+ wanted_done += corr;
+ }
+
+ const std::vector<piece_picker::downloading_piece>& dl_queue
+ = m_picker->get_download_queue();
+
+ const int blocks_per_piece = static_cast<int>(
+ m_torrent_file.piece_length() / m_block_size);
+
+ for (std::vector<piece_picker::downloading_piece>::const_iterator i =
+ dl_queue.begin(); i != dl_queue.end(); ++i)
+ {
+ int corr = 0;
+ assert(!m_have_pieces[i->index]);
+
+ for (int j = 0; j < blocks_per_piece; ++j)
+ {
+ corr += (i->finished_blocks[j]) * m_block_size;
+ }
+
+ // correction if this was the last piece
+ // and if we have the last block
+ if (i->index == last_piece
+ && i->finished_blocks[m_picker->blocks_in_last_piece()-1])
+ {
+ corr -= m_block_size;
+ corr += m_torrent_file.piece_size(last_piece) % m_block_size;
+ }
+ total_done += corr;
+ if (!m_picker->is_filtered(i->index))
+ wanted_done += corr;
+ }
+
+ std::map<piece_block, int> downloading_piece;
+ for (const_peer_iterator i = begin(); i != end(); ++i)
+ {
+ peer_connection* pc = i->second;
+ boost::optional<piece_block_progress> p
+ = pc->downloading_piece_progress();
+ if (p)
+ {
+ if (m_have_pieces[p->piece_index])
+ continue;
+
+ piece_block block(p->piece_index, p->block_index);
+ if (m_picker->is_finished(block))
+ continue;
+
+ std::map<piece_block, int>::iterator dp
+ = downloading_piece.find(block);
+ if (dp != downloading_piece.end())
+ {
+ if (dp->second < p->bytes_downloaded)
+ dp->second = p->bytes_downloaded;
+ }
+ else
+ {
+ downloading_piece[block] = p->bytes_downloaded;
+ }
+ assert(p->bytes_downloaded <= p->full_block_bytes);
+ }
+ }
+ for (std::map<piece_block, int>::iterator i = downloading_piece.begin();
+ i != downloading_piece.end(); ++i)
+ {
+ total_done += i->second;
+ if (!m_picker->is_filtered(i->first.piece_index))
+ wanted_done += i->second;
+ }
+ return make_tuple(total_done, wanted_done);
+ }
+
+ void torrent::piece_failed(int index)
+ {
+ INVARIANT_CHECK;
+
+ assert(m_storage.get());
+ assert(m_picker.get());
+ assert(index >= 0);
+ assert(index < m_torrent_file.num_pieces());
+
+ if (m_ses.m_alerts.should_post(alert::info))
+ {
+ std::stringstream s;
+ s << "hash for piece " << index << " failed";
+ m_ses.m_alerts.post_alert(hash_failed_alert(get_handle(), index, s.str()));
+ }
+ // increase the total amount of failed bytes
+ m_total_failed_bytes += m_torrent_file.piece_size(index);
+
+ std::vector<tcp::endpoint> downloaders;
+ m_picker->get_downloaders(downloaders, index);
+
+ // decrease the trust point of all peers that sent
+ // parts of this piece.
+ // first, build a set of all peers that participated
+ std::set<tcp::endpoint> peers;
+ std::copy(downloaders.begin(), downloaders.end(), std::inserter(peers, peers.begin()));
+
+ for (std::set<tcp::endpoint>::iterator i = peers.begin()
+ , end(peers.end()); i != end; ++i)
+ {
+ peer_iterator p = m_connections.find(*i);
+ if (p == m_connections.end()) continue;
+ p->second->received_invalid_data();
+
+ // either, we have received too many failed hashes
+ // or this was the only peer that sent us this piece.
+ // TODO: make this a changable setting
+ if (p->second->trust_points() <= -7 || peers.size() == 1)
+ {
+ // we don't trust this peer anymore
+ // ban it.
+ if (m_ses.m_alerts.should_post(alert::info))
+ {
+ m_ses.m_alerts.post_alert(peer_ban_alert(
+ p->first
+ , get_handle()
+ , "banning peer because of too many corrupt pieces"));
+ }
+ m_policy->ban_peer(*p->second);
+
+#if defined(TORRENT_VERBOSE_LOGGING)
+ (*p->second->m_logger) << "*** BANNING PEER 'too many corrupt pieces'\n";
+#endif
+ p->second->disconnect();
+ }
+ }
+
+ // we have to let the piece_picker know that
+ // this piece failed the check as it can restore it
+ // and mark it as being interesting for download
+ // TODO: do this more intelligently! and keep track
+ // of how much crap (data that failed hash-check) and
+ // how much redundant data we have downloaded
+ // if some clients has sent more than one piece
+ // start with redownloading the pieces that the client
+ // that has sent the least number of pieces
+ m_picker->restore_piece(index);
+ m_storage->mark_failed(index);
+
+ assert(m_have_pieces[index] == false);
+ }
+
+ void torrent::abort()
+ {
+ INVARIANT_CHECK;
+
+ m_abort = true;
+ // if the torrent is paused, it doesn't need
+ // to announce with even=stopped again.
+ if (!m_paused)
+ m_event = tracker_request::stopped;
+ // disconnect all peers and close all
+ // files belonging to the torrents
+ disconnect_all();
+ if (m_storage.get()) m_storage->release_files();
+ }
+
+ void torrent::announce_piece(int index)
+ {
+ INVARIANT_CHECK;
+
+ assert(m_picker.get());
+ assert(index >= 0);
+ assert(index < m_torrent_file.num_pieces());
+
+ std::vector<tcp::endpoint> downloaders;
+ m_picker->get_downloaders(downloaders, index);
+
+ // increase the trust point of all peers that sent
+ // parts of this piece.
+ std::set<tcp::endpoint> peers;
+ std::copy(downloaders.begin(), downloaders.end(), std::inserter(peers, peers.begin()));
+
+ for (std::set<tcp::endpoint>::iterator i = peers.begin()
+ , end(peers.end()); i != end; ++i)
+ {
+ peer_iterator p = m_connections.find(*i);
+ if (p == m_connections.end()) continue;
+ p->second->received_valid_data();
+ }
+
+ m_picker->we_have(index);
+ for (peer_iterator i = m_connections.begin(); i != m_connections.end(); ++i)
+ i->second->announce_piece(index);
+ }
+
+ std::string torrent::tracker_login() const
+ {
+ if (m_username.empty() && m_password.empty()) return "";
+ return m_username + ":" + m_password;
+ }
+
+ void torrent::filter_piece(int index, bool filter)
+ {
+ INVARIANT_CHECK;
+
+ // this call is only valid on torrents with metadata
+ assert(m_picker.get());
+ assert(index >= 0);
+ assert(index < m_torrent_file.num_pieces());
+
+ // TODO: update peer's interesting-bit
+
+ if (filter) m_picker->mark_as_filtered(index);
+ else m_picker->mark_as_unfiltered(index);
+ }
+
+ void torrent::filter_pieces(std::vector<bool> const& bitmask)
+ {
+ INVARIANT_CHECK;
+
+ // this call is only valid on torrents with metadata
+ assert(m_picker.get());
+
+ // TODO: update peer's interesting-bit
+
+ std::vector<int> state;
+ state.reserve(100);
+ int index = 0;
+ for (std::vector<bool>::const_iterator i = bitmask.begin()
+ , end(bitmask.end()); i != end; ++i, ++index)
+ {
+ if (m_picker->is_filtered(index) == *i) continue;
+ if (*i)
+ m_picker->mark_as_filtered(index);
+ else
+ state.push_back(index);
+ }
+
+ for (std::vector<int>::reverse_iterator i = state.rbegin();
+ i != state.rend(); ++i)
+ {
+ m_picker->mark_as_unfiltered(*i);
+ }
+ }
+
+ bool torrent::is_piece_filtered(int index) const
+ {
+ // this call is only valid on torrents with metadata
+ assert(m_picker.get());
+ assert(index >= 0);
+ assert(index < m_torrent_file.num_pieces());
+
+ return m_picker->is_filtered(index);
+ }
+
+ void torrent::filtered_pieces(std::vector<bool>& bitmask) const
+ {
+ INVARIANT_CHECK;
+
+ // this call is only valid on torrents with metadata
+ assert(m_picker.get());
+ m_picker->filtered_pieces(bitmask);
+ }
+
+ void torrent::filter_files(std::vector<bool> const& bitmask)
+ {
+ INVARIANT_CHECK;
+
+ // this call is only valid on torrents with metadata
+ if (!valid_metadata()) return;
+
+ // the bitmask need to have exactly one bit for every file
+ // in the torrent
+ assert((int)bitmask.size() == m_torrent_file.num_files());
+
+ size_type position = 0;
+
+ if (m_torrent_file.num_pieces())
+ {
+ int piece_length = m_torrent_file.piece_length();
+ // mark all pieces as filtered, then clear the bits for files
+ // that should be downloaded
+ std::vector<bool> piece_filter(m_torrent_file.num_pieces(), true);
+ for (int i = 0; i < (int)bitmask.size(); ++i)
+ {
+ size_type start = position;
+ position += m_torrent_file.file_at(i).size;
+ // is the file selected for download?
+ if (!bitmask[i])
+ {
+ // mark all pieces of the file as downloadable
+ int start_piece = int(start / piece_length);
+ int last_piece = int(position / piece_length);
+ // if one piece spans several files, we might
+ // come here several times with the same start_piece, end_piece
+ std::fill(piece_filter.begin() + start_piece, piece_filter.begin()
+ + last_piece + 1, false);
+ }
+ }
+ filter_pieces(piece_filter);
+ }
+ }
+
+ void torrent::replace_trackers(std::vector<announce_entry> const& urls)
+ {
+ assert(!urls.empty());
+ m_trackers = urls;
+ if (m_currently_trying_tracker >= (int)m_trackers.size())
+ m_currently_trying_tracker = (int)m_trackers.size()-1;
+ m_last_working_tracker = -1;
+ }
+
+ tracker_request torrent::generate_tracker_request()
+ {
+ INVARIANT_CHECK;
+
+ m_next_request
+ = second_clock::universal_time()
+ + boost::posix_time::seconds(tracker_retry_delay_max);
+
+ tracker_request req;
+ req.info_hash = m_torrent_file.info_hash();
+ req.pid = m_ses.get_peer_id();
+ req.downloaded = m_stat.total_payload_download();
+ req.uploaded = m_stat.total_payload_upload();
+ req.left = bytes_left();
+ if (req.left == -1) req.left = 16*1024;
+ req.event = m_event;
+
+ if (m_event != tracker_request::stopped)
+ m_event = tracker_request::none;
+ req.url = m_trackers[m_currently_trying_tracker].url;
+ assert(m_connections_quota.given > 0);
+ req.num_want = std::max(
+ (m_connections_quota.given
+ - m_policy->num_peers()), 10);
+ // if we are aborting. we don't want any new peers
+ if (req.event == tracker_request::stopped)
+ req.num_want = 0;
+
+ // default initialize, these should be set by caller
+ // before passing the request to the tracker_manager
+ req.listen_port = 0;
+ req.key = 0;
+
+ return req;
+ }
+
+ void torrent::remove_peer(peer_connection* p) try
+ {
+ INVARIANT_CHECK;
+
+ assert(p != 0);
+
+ peer_iterator i = m_connections.find(p->remote());
+ if (i == m_connections.end()) return;
+
+ if (ready_for_connections())
+ {
+ assert(p->associated_torrent().lock().get() == this);
+
+ std::vector<int> piece_list;
+ const std::vector<bool>& pieces = p->get_bitfield();
+
+ for (std::vector<bool>::const_iterator i = pieces.begin();
+ i != pieces.end(); ++i)
+ {
+ if (*i) piece_list.push_back(static_cast<int>(i - pieces.begin()));
+ }
+
+ for (std::vector<int>::reverse_iterator i = piece_list.rbegin();
+ i != piece_list.rend(); ++i)
+ {
+ peer_lost(*i);
+ }
+ }
+
+ m_policy->connection_closed(*p);
+ m_connections.erase(i);
+#ifndef NDEBUG
+ m_policy->check_invariant();
+#endif
+ }
+ catch (std::exception& e)
+ {
+#ifndef NDEBUG
+ std::string err = e.what();
+#endif
+ assert(false);
+ };
+
+ void torrent::connect_to_url_seed(std::string const& url)
+ {
+ INVARIANT_CHECK;
+
+#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
+ std::string now(to_simple_string(second_clock::universal_time()));
+ (*m_ses.m_logger) << now << " resolving: " << url << "\n";
+#endif
+
+ std::string protocol;
+ std::string hostname;
+ int port;
+ std::string path;
+ boost::tie(protocol, hostname, port, path)
+ = parse_url_components(url);
+
+ m_resolving_web_seeds.insert(url);
+ if (m_ses.settings().proxy_ip.empty())
+ {
+ tcp::resolver::query q(hostname, boost::lexical_cast<std::string>(port));
+ m_host_resolver.async_resolve(q, bind(&torrent::on_name_lookup
+ , shared_from_this(), _1, _2, url));
+ }
+ else
+ {
+ // use proxy
+ tcp::resolver::query q(m_ses.settings().proxy_ip
+ , boost::lexical_cast<std::string>(m_ses.settings().proxy_port));
+ m_host_resolver.async_resolve(q, bind(&torrent::on_name_lookup
+ , shared_from_this(), _1, _2, url));
+ }
+
+ }
+
+ void torrent::on_name_lookup(asio::error const& e, tcp::resolver::iterator host
+ , std::string url) try
+ {
+ session_impl::mutex_t::scoped_lock l(m_ses.m_mutex);
+
+ INVARIANT_CHECK;
+
+#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
+ std::string now(to_simple_string(second_clock::universal_time()));
+ (*m_ses.m_logger) << now << " completed resolve: " << url << "\n";
+#endif
+
+ std::set<std::string>::iterator i = m_resolving_web_seeds.find(url);
+ if (i != m_resolving_web_seeds.end()) m_resolving_web_seeds.erase(i);
+
+ if (e || host == tcp::resolver::iterator())
+ {
+ if (m_ses.m_alerts.should_post(alert::warning))
+ {
+ std::stringstream msg;
+ msg << "HTTP seed hostname lookup failed: " << e.what();
+ m_ses.m_alerts.post_alert(
+ url_seed_alert(url, msg.str()));
+ }
+#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
+ (*m_ses.m_logger) << " ** HOSTNAME LOOKUP FAILED!**: " << url << "\n";
+#endif
+
+ // the name lookup failed for the http host. Don't try
+ // this host again
+ remove_url_seed(url);
+ return;
+ }
+
+ if (m_ses.is_aborted()) return;
+
+ tcp::endpoint a(host->endpoint());
+
+ boost::shared_ptr<stream_socket> s(new stream_socket(m_ses.m_selector));
+ boost::intrusive_ptr<peer_connection> c(new web_peer_connection(
+ m_ses, shared_from_this(), s, a, url));
+#ifndef NDEBUG
+ c->m_in_constructor = false;
+#endif
+
+ try
+ {
+ m_ses.m_connection_queue.push_back(c);
+
+ assert(m_connections.find(a) == m_connections.end());
+
+#ifndef NDEBUG
+ m_policy->check_invariant();
+#endif
+ // add the newly connected peer to this torrent's peer list
+ m_connections.insert(
+ std::make_pair(a, boost::get_pointer(c)));
+
+#ifndef NDEBUG
+ m_policy->check_invariant();
+#endif
+
+ m_ses.process_connection_queue();
+ }
+ catch (std::exception& e)
+ {
+#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
+ (*m_ses.m_logger) << " ** HOSTNAME LOOKUP FAILED!**: " << e.what() << "\n";
+#endif
+
+ // TODO: post an error alert!
+ std::map<tcp::endpoint, peer_connection*>::iterator i = m_connections.find(a);
+ if (i != m_connections.end()) m_connections.erase(i);
+ m_ses.connection_failed(s, a, e.what());
+ c->disconnect();
+ }
+ }
+ catch (std::exception& exc)
+ {
+ assert(false);
+ };
+
+ peer_connection& torrent::connect_to_peer(const tcp::endpoint& a)
+ {
+ INVARIANT_CHECK;
+
+ if (m_connections.find(a) != m_connections.end())
+ throw protocol_error("already connected to peer");
+
+ boost::shared_ptr<stream_socket> s(new stream_socket(m_ses.m_selector));
+ boost::intrusive_ptr<peer_connection> c(new bt_peer_connection(
+ m_ses, shared_from_this(), s, a));
+#ifndef NDEBUG
+ c->m_in_constructor = false;
+#endif
+
+ try
+ {
+ m_ses.m_connection_queue.push_back(c);
+
+ assert(m_connections.find(a) == m_connections.end());
+
+#ifndef NDEBUG
+ m_policy->check_invariant();
+#endif
+ // add the newly connected peer to this torrent's peer list
+ m_connections.insert(
+ std::make_pair(a, boost::get_pointer(c)));
+
+#ifndef NDEBUG
+ m_policy->check_invariant();
+#endif
+
+ m_ses.process_connection_queue();
+ }
+ catch (std::exception& e)
+ {
+ // TODO: post an error alert!
+ std::map<tcp::endpoint, peer_connection*>::iterator i = m_connections.find(a);
+ if (i != m_connections.end()) m_connections.erase(i);
+ m_ses.connection_failed(s, a, e.what());
+ c->disconnect();
+ throw;
+ }
+ if (c->is_disconnecting()) throw protocol_error("failed to connect");
+ return *c;
+ }
+
+ void torrent::attach_peer(peer_connection* p)
+ {
+ INVARIANT_CHECK;
+
+ assert(p != 0);
+ assert(!p->is_local());
+
+ std::map<tcp::endpoint, peer_connection*>::iterator c
+ = m_connections.find(p->remote());
+ if (c != m_connections.end())
+ {
+ // we already have a peer_connection to this ip.
+ // It may currently be waiting for completing a
+ // connection attempt that might fail. So,
+ // prioritize this current connection since
+ // it has already succeeded.
+ if (!c->second->is_connecting())
+ {
+ throw protocol_error("already connected to peer");
+ }
+ c->second->disconnect();
+ }
+
+ if (m_ses.m_connections.find(p->get_socket())
+ == m_ses.m_connections.end())
+ {
+ throw protocol_error("peer is not properly constructed");
+ }
+
+ if (m_ses.is_aborted())
+ {
+ throw protocol_error("session is closing");
+ }
+
+ peer_iterator i = m_connections.insert(
+ std::make_pair(p->remote(), p)).first;
+
+ try
+ {
+ // if new_connection throws, we have to remove the
+ // it from the list.
+
+ m_policy->new_connection(*i->second);
+ }
+ catch (std::exception& e)
+ {
+ m_connections.erase(i);
+ throw;
+ }
+#ifndef NDEBUG
+ assert(p->remote() == p->get_socket()->remote_endpoint());
+#endif
+
+#ifndef NDEBUG
+ m_policy->check_invariant();
+#endif
+ }
+
+ void torrent::disconnect_all()
+ {
+ session_impl::mutex_t::scoped_lock l(m_ses.m_mutex);
+
+ INVARIANT_CHECK;
+
+ while (!m_connections.empty())
+ {
+ peer_connection& p = *m_connections.begin()->second;
+ assert(p.associated_torrent().lock().get() == this);
+
+#if defined(TORRENT_VERBOSE_LOGGING)
+ if (m_abort)
+ (*p.m_logger) << "*** CLOSING CONNECTION 'aborting'\n";
+ else
+ (*p.m_logger) << "*** CLOSING CONNECTION 'pausing'\n";
+#endif
+#ifndef NDEBUG
+ std::size_t size = m_connections.size();
+#endif
+ p.disconnect();
+ assert(m_connections.size() <= size);
+ }
+ }
+
+ // called when torrent is finished (all interested pieces downloaded)
+ void torrent::finished()
+ {
+ INVARIANT_CHECK;
+
+ if (alerts().should_post(alert::info))
+ {
+ alerts().post_alert(torrent_finished_alert(
+ get_handle()
+ , "torrent has finished downloading"));
+ }
+
+ // disconnect all seeds
+ std::vector<peer_connection*> seeds;
+ for (peer_iterator i = m_connections.begin();
+ i != m_connections.end(); ++i)
+ {
+ assert(i->second->associated_torrent().lock().get() == this);
+ if (i->second->is_seed())
+ {
+#if defined(TORRENT_VERBOSE_LOGGING)
+ (*i->second->m_logger) << "*** SEED, CLOSING CONNECTION\n";
+#endif
+ seeds.push_back(i->second);
+ }
+ }
+ std::for_each(seeds.begin(), seeds.end()
+ , bind(&peer_connection::disconnect, _1));
+
+ m_storage->release_files();
+ }
+
+ // called when torrent is complete (all pieces downloaded)
+ void torrent::completed()
+ {
+ INVARIANT_CHECK;
+
+/*
+ if (alerts().should_post(alert::info))
+ {
+ alerts().post_alert(torrent_complete_alert(
+ get_handle()
+ , "torrent is complete"));
+ }
+*/
+ // make the next tracker request
+ // be a completed-event
+ m_event = tracker_request::completed;
+ force_tracker_request();
+ }
+
+ // this will move the tracker with the given index
+ // to a prioritized position in the list (move it towards
+ // the begining) and return the new index to the tracker.
+ int torrent::prioritize_tracker(int index)
+ {
+ INVARIANT_CHECK;
+
+ assert(index >= 0);
+ if (index >= (int)m_trackers.size()) return (int)m_trackers.size()-1;
+
+ while (index > 0 && m_trackers[index].tier == m_trackers[index-1].tier)
+ {
+ std::swap(m_trackers[index].url, m_trackers[index-1].url);
+ --index;
+ }
+ return index;
+ }
+
+ void torrent::try_next_tracker()
+ {
+ INVARIANT_CHECK;
+
+ using namespace boost::posix_time;
+ ++m_currently_trying_tracker;
+
+ if ((unsigned)m_currently_trying_tracker >= m_trackers.size())
+ {
+ int delay = tracker_retry_delay_min
+ + std::min(m_failed_trackers, (int)tracker_failed_max)
+ * (tracker_retry_delay_max - tracker_retry_delay_min)
+ / tracker_failed_max;
+
+ ++m_failed_trackers;
+ // if we've looped the tracker list, wait a bit before retrying
+ m_currently_trying_tracker = 0;
+ m_next_request = second_clock::universal_time() + seconds(delay);
+ }
+ else
+ {
+ // don't delay before trying the next tracker
+ m_next_request = second_clock::universal_time();
+ }
+
+ }
+
+ bool torrent::check_fastresume(aux::piece_checker_data& data)
+ {
+ 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());
+ bool done = m_storage->check_fastresume(data, m_have_pieces, m_num_pieces
+ , m_compact_mode);
+#ifndef NDEBUG
+ m_initial_done = boost::get<0>(bytes_done());
+#endif
+ return done;
+ }
+
+ std::pair<bool, float> torrent::check_files()
+ {
+ INVARIANT_CHECK;
+
+ assert(m_storage.get());
+ std::pair<bool, float> progress = m_storage->check_files(m_have_pieces, m_num_pieces);
+
+#ifndef NDEBUG
+ m_initial_done = boost::get<0>(bytes_done());
+#endif
+ return progress;
+ }
+
+ void torrent::files_checked(std::vector<piece_picker::downloading_piece> const&
+ unfinished_pieces)
+ {
+ session_impl::mutex_t::scoped_lock l(m_ses.m_mutex);
+
+ INVARIANT_CHECK;
+
+ m_picker->files_checked(m_have_pieces, unfinished_pieces);
+ if (!m_connections_initialized)
+ {
+ m_connections_initialized = true;
+ // all peer connections have to initialize themselves now that the metadata
+ // is available
+ typedef std::map<tcp::endpoint, peer_connection*> conn_map;
+ for (conn_map::iterator i = m_connections.begin()
+ , end(m_connections.end()); i != end;)
+ {
+ try { i->second->init(); ++i;}
+ catch (std::exception& e)
+ {
+ // the connection failed, close it
+ conn_map::iterator j = i;
+ ++j;
+ m_ses.connection_failed(i->second->get_socket()
+ , i->first, e.what());
+ i = j;
+ }
+ }
+ }
+#ifndef NDEBUG
+ m_initial_done = boost::get<0>(bytes_done());
+#endif
+ }
+
+ alert_manager& torrent::alerts() const
+ {
+ return m_ses.m_alerts;
+ }
+
+ boost::filesystem::path torrent::save_path() const
+ {
+ return m_save_path;
+ }
+
+ bool torrent::move_storage(boost::filesystem::path const& save_path)
+ {
+ INVARIANT_CHECK;
+
+ bool ret = true;
+ if (m_storage.get())
+ {
+ ret = m_storage->move_storage(save_path);
+ m_save_path = m_storage->save_path();
+ }
+ else
+ {
+ m_save_path = save_path;
+ }
+ return ret;
+ }
+
+ piece_manager& torrent::filesystem()
+ {
+ INVARIANT_CHECK;
+
+ assert(m_storage.get());
+ return *m_storage;
+ }
+
+
+ torrent_handle torrent::get_handle() const
+ {
+ INVARIANT_CHECK;
+
+ return torrent_handle(&m_ses, 0, m_torrent_file.info_hash());
+ }
+
+ session_settings const& torrent::settings() const
+ {
+ INVARIANT_CHECK;
+
+ return m_ses.settings();
+ }
+
+#ifndef NDEBUG
+ void torrent::check_invariant() const
+ {
+// size_type download = m_stat.total_payload_download();
+// size_type done = boost::get<0>(bytes_done());
+// assert(download >= done - m_initial_done);
+ for (const_peer_iterator i = begin(); i != end(); ++i)
+ {
+ peer_connection const& p = *i->second;
+ torrent* associated_torrent = p.associated_torrent().lock().get();
+ if (associated_torrent != this)
+ assert(false);
+ }
+
+// This check is very expensive.
+// assert(m_num_pieces
+// == std::count(m_have_pieces.begin(), m_have_pieces.end(), true));
+ assert(m_priority >= 0.f && m_priority < 1.f);
+ assert(!valid_metadata() || m_block_size > 0);
+ assert(!valid_metadata() || (m_torrent_file.piece_length() % m_block_size) == 0);
+ }
+#endif
+
+ void torrent::set_sequenced_download_threshold(int threshold)
+ {
+ if (valid_metadata())
+ picker().set_sequenced_download_threshold(threshold);
+ }
+
+
+ void torrent::set_max_uploads(int limit)
+ {
+ assert(limit >= -1);
+ if (limit == -1) limit = std::numeric_limits<int>::max();
+ m_uploads_quota.max = std::max(m_uploads_quota.min, limit);
+ }
+
+ void torrent::set_max_connections(int limit)
+ {
+ assert(limit >= -1);
+ if (limit == -1) limit = std::numeric_limits<int>::max();
+ m_connections_quota.max = std::max(m_connections_quota.min, limit);
+ }
+
+ void torrent::set_peer_upload_limit(tcp::endpoint ip, int limit)
+ {
+ assert(limit >= -1);
+ peer_connection* p = connection_for(ip);
+ if (p == 0) return;
+ p->set_upload_limit(limit);
+ }
+
+ void torrent::set_peer_download_limit(tcp::endpoint ip, int limit)
+ {
+ assert(limit >= -1);
+ peer_connection* p = connection_for(ip);
+ if (p == 0) return;
+ p->set_download_limit(limit);
+ }
+
+ void torrent::set_upload_limit(int limit)
+ {
+ assert(limit >= -1);
+ if (limit == -1) limit = std::numeric_limits<int>::max();
+ if (limit < num_peers() * 10) limit = num_peers() * 10;
+ m_upload_bandwidth_limit = limit;
+ }
+
+ void torrent::set_download_limit(int limit)
+ {
+ assert(limit >= -1);
+ if (limit == -1) limit = std::numeric_limits<int>::max();
+ if (limit < num_peers() * 10) limit = num_peers() * 10;
+ m_download_bandwidth_limit = limit;
+ }
+
+ void torrent::pause()
+ {
+ INVARIANT_CHECK;
+
+ if (m_paused) return;
+ disconnect_all();
+ m_paused = true;
+ // tell the tracker that we stopped
+ m_event = tracker_request::stopped;
+ 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();
+ }
+
+ void torrent::resume()
+ {
+ INVARIANT_CHECK;
+
+ if (!m_paused) return;
+ m_paused = false;
+
+ // tell the tracker that we're back
+ m_event = tracker_request::started;
+ force_tracker_request();
+
+ // make pulse be called as soon as possible
+ m_time_scaler = 0;
+ }
+
+ void torrent::second_tick(stat& accumulator, float tick_interval)
+ {
+ INVARIANT_CHECK;
+
+ m_connections_quota.used = (int)m_connections.size();
+ m_uploads_quota.used = m_policy->num_uploads();
+
+ m_ul_bandwidth_quota.used = 0;
+ m_ul_bandwidth_quota.max = 0;
+ m_ul_bandwidth_quota.min = 0;
+
+ m_dl_bandwidth_quota.used = 0;
+ m_dl_bandwidth_quota.min = 0;
+ m_dl_bandwidth_quota.max = 0;
+
+ if (m_paused)
+ {
+ // let the stats fade out to 0
+ m_stat.second_tick(tick_interval);
+ return;
+ }
+
+ // ---- WEB SEEDS ----
+
+ // if we're a seed, we don't need to connect to any web-seed
+ if (!is_seed() && !m_web_seeds.empty())
+ {
+ // keep trying web-seeds if there are any
+ // first find out which web seeds we are connected to
+ std::set<std::string> web_seeds;
+ for (peer_iterator i = m_connections.begin();
+ i != m_connections.end(); ++i)
+ {
+ web_peer_connection* p
+ = dynamic_cast<web_peer_connection*>(i->second);
+ if (!p) continue;
+ web_seeds.insert(p->url());
+ }
+
+ for (std::set<std::string>::iterator i = m_resolving_web_seeds.begin()
+ , end(m_resolving_web_seeds.end()); i != end; ++i)
+ web_seeds.insert(web_seeds.begin(), *i);
+
+ // from the list of available web seeds, subtract the ones we are
+ // already connected to.
+ std::vector<std::string> not_connected_web_seeds;
+ std::set_difference(m_web_seeds.begin(), m_web_seeds.end(), web_seeds.begin()
+ , web_seeds.end(), std::back_inserter(not_connected_web_seeds));
+
+ // connect to all of those that we aren't connected to
+ std::for_each(not_connected_web_seeds.begin(), not_connected_web_seeds.end()
+ , bind(&torrent::connect_to_url_seed, this, _1));
+ }
+
+ for (peer_iterator i = m_connections.begin();
+ i != m_connections.end(); ++i)
+ {
+ peer_connection* p = i->second;
+ m_stat += p->statistics();
+ // updates the peer connection's ul/dl bandwidth
+ // resource requests
+ p->second_tick(tick_interval);
+
+ m_ul_bandwidth_quota.used += p->m_ul_bandwidth_quota.used;
+ m_ul_bandwidth_quota.min += p->m_ul_bandwidth_quota.min;
+ m_dl_bandwidth_quota.used += p->m_dl_bandwidth_quota.used;
+ m_dl_bandwidth_quota.min += p->m_dl_bandwidth_quota.min;
+
+ m_ul_bandwidth_quota.max = saturated_add(
+ m_ul_bandwidth_quota.max
+ , p->m_ul_bandwidth_quota.max);
+
+ m_dl_bandwidth_quota.max = saturated_add(
+ m_dl_bandwidth_quota.max
+ , p->m_dl_bandwidth_quota.max);
+ }
+
+ m_ul_bandwidth_quota.max
+ = std::min(m_ul_bandwidth_quota.max, m_upload_bandwidth_limit);
+
+ if (m_upload_bandwidth_limit == resource_request::inf)
+ m_ul_bandwidth_quota.max = resource_request::inf;
+
+ m_dl_bandwidth_quota.max
+ = std::min(m_dl_bandwidth_quota.max, m_download_bandwidth_limit);
+
+ if (m_download_bandwidth_limit == resource_request::inf)
+ m_dl_bandwidth_quota.max = resource_request::inf;
+
+ accumulator += m_stat;
+ m_stat.second_tick(tick_interval);
+ }
+
+ void torrent::distribute_resources()
+ {
+ INVARIANT_CHECK;
+
+ m_time_scaler--;
+ if (m_time_scaler <= 0)
+ {
+ m_time_scaler = 10;
+ m_policy->pulse();
+ }
+
+ assert(m_ul_bandwidth_quota.given >= 0);
+ assert(m_dl_bandwidth_quota.given >= 0);
+
+ // distribute allowed upload among the peers
+ allocate_resources(m_ul_bandwidth_quota.given
+ , m_connections
+ , &peer_connection::m_ul_bandwidth_quota);
+
+ // distribute allowed download among the peers
+ allocate_resources(m_dl_bandwidth_quota.given
+ , m_connections
+ , &peer_connection::m_dl_bandwidth_quota);
+
+ using boost::bind;
+
+ // tell all peers to reset their used quota. This is
+ // a new second and they can again use up their quota
+
+ for (std::map<tcp::endpoint, peer_connection*>::iterator i
+ = m_connections.begin(); i != m_connections.end(); ++i)
+ {
+ i->second->reset_upload_quota();
+ assert(i->second->m_dl_bandwidth_quota.used
+ <= i->second->m_dl_bandwidth_quota.given);
+ }
+ }
+
+ bool torrent::verify_piece(int piece_index)
+ {
+ INVARIANT_CHECK;
+
+ assert(m_storage.get());
+ 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);
+
+ hasher h;
+ h.update(&buffer[0], size);
+ sha1_hash digest = h.final();
+
+ if (m_torrent_file.hash_for_piece(piece_index) != digest)
+ return false;
+
+ if (!m_have_pieces[piece_index])
+ m_num_pieces++;
+ m_have_pieces[piece_index] = true;
+
+ assert(std::accumulate(m_have_pieces.begin(), m_have_pieces.end(), 0)
+ == m_num_pieces);
+ return true;
+ }
+
+ const tcp::endpoint& torrent::current_tracker() const
+ {
+ return m_tracker_address;
+ }
+
+ bool torrent::is_allocating() const
+ { return m_storage.get() && m_storage->is_allocating(); }
+
+ std::vector<char> const& torrent::metadata() const
+ {
+ INVARIANT_CHECK;
+
+ if (m_metadata.empty())
+ {
+ bencode(std::back_inserter(m_metadata)
+ , m_torrent_file.create_info_metadata());
+
+ assert(hasher(&m_metadata[0], m_metadata.size()).final()
+ == m_torrent_file.info_hash());
+ }
+ assert(!m_metadata.empty());
+ return m_metadata;
+ }
+
+ void torrent::file_progress(std::vector<float>& fp) const
+ {
+ assert(valid_metadata());
+
+ fp.clear();
+ fp.resize(m_torrent_file.num_files(), 0.f);
+
+ for (int i = 0; i < m_torrent_file.num_files(); ++i)
+ {
+ peer_request ret = m_torrent_file.map_file(i, 0, 0);
+ size_type size = m_torrent_file.file_at(i).size;
+
+// zero sized files are considered
+// 100% done all the time
+ if (size == 0)
+ {
+ fp[i] = 1.f;
+ continue;
+ }
+
+ size_type done = 0;
+ while (size > 0)
+ {
+ size_type bytes_step = std::min(m_torrent_file.piece_size(ret.piece)
+ - ret.start, size);
+ if (m_have_pieces[ret.piece]) done += bytes_step;
+ ++ret.piece;
+ ret.start = 0;
+ size -= bytes_step;
+ }
+ assert(size == 0);
+
+ fp[i] = static_cast<float>(done) / m_torrent_file.file_at(i).size;
+ }
+ }
+
+ torrent_status torrent::status() const
+ {
+ INVARIANT_CHECK;
+
+ assert(std::accumulate(
+ m_have_pieces.begin()
+ , m_have_pieces.end()
+ , 0) == m_num_pieces);
+
+ 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))));
+
+ st.num_complete = m_complete;
+ st.num_incomplete = m_incomplete;
+ st.paused = m_paused;
+ boost::tie(st.total_done, st.total_wanted_done) = bytes_done();
+
+ // payload transfer
+ st.total_payload_download = m_stat.total_payload_download();
+ st.total_payload_upload = m_stat.total_payload_upload();
+
+ // total transfer
+ st.total_download = m_stat.total_payload_download()
+ + m_stat.total_protocol_download();
+ st.total_upload = m_stat.total_payload_upload()
+ + m_stat.total_protocol_upload();
+
+ // failed bytes
+ st.total_failed_bytes = m_total_failed_bytes;
+ st.total_redundant_bytes = m_total_redundant_bytes;
+
+ // transfer rate
+ st.download_rate = m_stat.download_rate();
+ st.upload_rate = m_stat.upload_rate();
+ st.download_payload_rate = m_stat.download_payload_rate();
+ st.upload_payload_rate = m_stat.upload_payload_rate();
+
+ st.next_announce = next_announce()
+ - second_clock::universal_time();
+ if (st.next_announce.is_negative()) st.next_announce
+ = boost::posix_time::seconds(0);
+ st.announce_interval = boost::posix_time::seconds(m_duration);
+
+ if (m_last_working_tracker >= 0)
+ {
+ st.current_tracker
+ = m_trackers[m_last_working_tracker].url;
+ }
+
+ // if we don't have any metadata, stop here
+
+ if (!valid_metadata())
+ {
+ if (m_got_tracker_response == false)
+ st.state = torrent_status::connecting_to_tracker;
+ else
+ st.state = torrent_status::downloading_metadata;
+
+ if (m_metadata_size == 0) st.progress = 0.f;
+ else st.progress = std::min(1.f, m_metadata_progress / (float)m_metadata_size);
+
+ st.block_size = 0;
+
+ return st;
+ }
+
+ st.block_size = block_size();
+
+ // fill in status that depends on metadata
+
+ st.total_wanted = m_torrent_file.total_size();
+
+ if (m_picker.get() && (m_picker->num_filtered() > 0
+ || m_picker->num_have_filtered() > 0))
+ {
+ int filtered_pieces = m_picker->num_filtered()
+ + m_picker->num_have_filtered();
+ int last_piece_index = m_torrent_file.num_pieces() - 1;
+ if (m_picker->is_filtered(last_piece_index))
+ {
+ st.total_wanted -= m_torrent_file.piece_size(last_piece_index);
+ --filtered_pieces;
+ }
+
+ st.total_wanted -= filtered_pieces * m_torrent_file.piece_length();
+ }
+
+ assert(st.total_wanted >= st.total_wanted_done);
+
+ if (st.total_wanted == 0) st.progress = 1.f;
+ else st.progress = st.total_wanted_done
+ / static_cast<double>(st.total_wanted);
+
+ st.pieces = &m_have_pieces;
+ st.num_pieces = m_num_pieces;
+
+ if (m_got_tracker_response == false)
+ st.state = torrent_status::connecting_to_tracker;
+ else if (m_num_pieces == (int)m_have_pieces.size())
+ st.state = torrent_status::seeding;
+ else if (st.total_wanted_done == st.total_wanted)
+ st.state = torrent_status::finished;
+ else
+ st.state = torrent_status::downloading;
+
+ st.num_seeds = num_seeds();
+ st.distributed_copies = m_picker->distributed_copies();
+ return st;
+ }
+
+ int torrent::num_seeds() const
+ {
+ 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)));
+ }
+
+ int div_round_up(int numerator, int denominator)
+ {
+ return (numerator + denominator - 1) / denominator;
+ }
+
+ std::pair<int, int> req_to_offset(std::pair<int, int> req, int total_size)
+ {
+ assert(req.first >= 0);
+ assert(req.second > 0);
+ assert(req.second <= 256);
+ assert(req.first + req.second <= 256);
+
+ int start = div_round_up(req.first * total_size, 256);
+ int size = div_round_up((req.first + req.second) * total_size, 256) - start;
+ return std::make_pair(start, size);
+ }
+
+ std::pair<int, int> offset_to_req(std::pair<int, int> offset, int total_size)
+ {
+ int start = offset.first * 256 / total_size;
+ int size = (offset.first + offset.second) * 256 / total_size - start;
+
+ std::pair<int, int> ret(start, size);
+
+ assert(start >= 0);
+ assert(size > 0);
+ assert(start <= 256);
+ assert(start + size <= 256);
+
+ // assert the identity of this function
+#ifndef NDEBUG
+ std::pair<int, int> identity = req_to_offset(ret, total_size);
+ assert(offset == identity);
+#endif
+ return ret;
+ }
+
+ bool torrent::received_metadata(char const* buf, int size, int offset, int total_size)
+ {
+ INVARIANT_CHECK;
+
+ if (valid_metadata()) return false;
+
+ if ((int)m_metadata.size() < total_size)
+ m_metadata.resize(total_size);
+
+ std::copy(
+ buf
+ , buf + size
+ , &m_metadata[offset]);
+
+ if (m_have_metadata.empty())
+ m_have_metadata.resize(256, false);
+
+ std::pair<int, int> req = offset_to_req(std::make_pair(offset, size)
+ , total_size);
+
+ assert(req.first + req.second <= (int)m_have_metadata.size());
+
+ std::fill(
+ m_have_metadata.begin() + req.first
+ , m_have_metadata.begin() + req.first + req.second
+ , true);
+
+ bool have_all = std::count(
+ m_have_metadata.begin()
+ , m_have_metadata.end()
+ , true) == 256;
+
+ if (!have_all) return false;
+
+ hasher h;
+ h.update(&m_metadata[0], (int)m_metadata.size());
+ sha1_hash info_hash = h.final();
+
+ if (info_hash != m_torrent_file.info_hash())
+ {
+ std::fill(
+ m_have_metadata.begin()
+ , m_have_metadata.begin() + req.first + req.second
+ , false);
+ m_metadata_progress = 0;
+ m_metadata_size = 0;
+ if (m_ses.m_alerts.should_post(alert::info))
+ {
+ m_ses.m_alerts.post_alert(metadata_failed_alert(
+ get_handle(), "invalid metadata received from swarm"));
+ }
+
+ return false;
+ }
+
+ entry metadata = bdecode(m_metadata.begin(), m_metadata.end());
+ m_torrent_file.parse_info_section(metadata);
+
+ {
+ boost::mutex::scoped_lock(m_checker.m_mutex);
+
+ boost::shared_ptr<aux::piece_checker_data> d(
+ new aux::piece_checker_data);
+ d->torrent_ptr = shared_from_this();
+ d->save_path = m_save_path;
+ d->info_hash = m_torrent_file.info_hash();
+ // add the torrent to the queue to be checked
+ m_checker.m_torrents.push_back(d);
+ typedef session_impl::torrent_map torrent_map;
+ torrent_map::iterator i = m_ses.m_torrents.find(
+ m_torrent_file.info_hash());
+ assert(i != m_ses.m_torrents.end());
+ m_ses.m_torrents.erase(i);
+ // and notify the thread that it got another
+ // job in its queue
+ m_checker.m_cond.notify_one();
+ }
+ if (m_ses.m_alerts.should_post(alert::info))
+ {
+ m_ses.m_alerts.post_alert(metadata_received_alert(
+ get_handle(), "metadata successfully received from swarm"));
+ }
+
+ // clear the storage for the bitfield
+ std::vector<bool>().swap(m_have_metadata);
+ std::vector<int>().swap(m_requested_metadata);
+
+ return true;
+ }
+
+ std::pair<int, int> torrent::metadata_request()
+ {
+ INVARIANT_CHECK;
+
+ // count the number of peers that supports the
+ // extension and that has metadata
+ int peers = 0;
+ typedef std::map<tcp::endpoint, peer_connection*> conn_map;
+ for (conn_map::iterator i = m_connections.begin()
+ , end(m_connections.end()); i != end; ++i)
+ {
+ bt_peer_connection* c = dynamic_cast<bt_peer_connection*>(i->second);
+ if (c == 0) continue;
+ if (!c->supports_extension(
+ extended_metadata_message))
+ continue;
+ if (!c->has_metadata())
+ continue;
+ ++peers;
+ }
+
+ // the number of blocks to request
+ int num_blocks = 256 / (peers + 1);
+ if (num_blocks < 1) num_blocks = 1;
+ assert(num_blocks <= 128);
+
+ int min_element = std::numeric_limits<int>::max();
+ int best_index = 0;
+ for (int i = 0; i < 256 - num_blocks + 1; ++i)
+ {
+ int min = *std::min_element(m_requested_metadata.begin() + i
+ , m_requested_metadata.begin() + i + num_blocks);
+ min += std::accumulate(m_requested_metadata.begin() + i
+ , m_requested_metadata.begin() + i + num_blocks, (int)0);
+
+ if (min_element > min)
+ {
+ best_index = i;
+ min_element = min;
+ }
+ }
+
+ std::pair<int, int> ret(best_index, num_blocks);
+ for (int i = ret.first; i < ret.first + ret.second; ++i)
+ m_requested_metadata[i]++;
+
+ assert(ret.first >= 0);
+ assert(ret.second > 0);
+ assert(ret.second <= 256);
+ assert(ret.first + ret.second <= 256);
+
+ return ret;
+ }
+
+ void torrent::cancel_metadata_request(std::pair<int, int> req)
+ {
+ INVARIANT_CHECK;
+
+ for (int i = req.first; i < req.first + req.second; ++i)
+ {
+ assert(m_requested_metadata[i] > 0);
+ if (m_requested_metadata[i] > 0)
+ --m_requested_metadata[i];
+ }
+ }
+
+ void torrent::tracker_request_timed_out(
+ tracker_request const&)
+ {
+ session_impl::mutex_t::scoped_lock l(m_ses.m_mutex);
+ INVARIANT_CHECK;
+
+#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
+ debug_log("*** tracker timed out");
+#endif
+
+ if (m_ses.m_alerts.should_post(alert::warning))
+ {
+ std::stringstream s;
+ s << "tracker: \""
+ << m_trackers[m_currently_trying_tracker].url
+ << "\" timed out";
+ m_ses.m_alerts.post_alert(tracker_alert(get_handle()
+ , m_failed_trackers + 1, 0, s.str()));
+ }
+ try_next_tracker();
+ }
+
+ // TODO: with some response codes, we should just consider
+ // the tracker as a failure and not retry
+ // it anymore
+ void torrent::tracker_request_error(tracker_request const&
+ , int response_code, const std::string& str)
+ {
+ INVARIANT_CHECK;
+
+ session_impl::mutex_t::scoped_lock l(m_ses.m_mutex);
+#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
+ debug_log(std::string("*** tracker error: ") + str);
+#endif
+ if (m_ses.m_alerts.should_post(alert::warning))
+ {
+ std::stringstream s;
+ s << "tracker: \""
+ << m_trackers[m_currently_trying_tracker].url
+ << "\" " << str;
+ m_ses.m_alerts.post_alert(tracker_alert(get_handle()
+ , m_failed_trackers + 1, response_code, s.str()));
+ }
+
+ try_next_tracker();
+ }
+
+
+#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
+ void torrent::debug_log(const std::string& line)
+ {
+ (*m_ses.m_logger) << line << "\n";
+ }
+#endif
+
+ void torrent::metadata_progress(int total_size, int received)
+ {
+ m_metadata_progress += received;
+ m_metadata_size = total_size;
+ }
+
+}
+
diff --git a/library/torrent_handle.cpp b/library/torrent_handle.cpp
new file mode 100755
index 000000000..b8fe6f415
--- /dev/null
+++ b/library/torrent_handle.cpp
@@ -0,0 +1,729 @@
+/*
+
+Copyright (c) 2003, 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.
+
+*/
+
+#include <ctime>
+#include <iostream>
+#include <fstream>
+#include <iomanip>
+#include <iterator>
+#include <algorithm>
+#include <set>
+#include <cctype>
+#include <algorithm>
+
+#ifdef _MSC_VER
+#pragma warning(push, 1)
+#endif
+
+#include <boost/lexical_cast.hpp>
+#include <boost/filesystem/convenience.hpp>
+#include <boost/optional.hpp>
+#include <boost/bind.hpp>
+
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+#include "libtorrent/peer_id.hpp"
+#include "libtorrent/bt_peer_connection.hpp"
+#include "libtorrent/torrent_info.hpp"
+#include "libtorrent/tracker_manager.hpp"
+#include "libtorrent/bencode.hpp"
+#include "libtorrent/hasher.hpp"
+#include "libtorrent/entry.hpp"
+#include "libtorrent/session.hpp"
+#include "libtorrent/aux_/session_impl.hpp"
+#include "libtorrent/invariant_check.hpp"
+
+#if defined(_MSC_VER) && _MSC_VER < 1300
+namespace std
+{
+ using ::srand;
+ using ::isalnum;
+};
+#endif
+
+using boost::bind;
+using boost::mutex;
+using libtorrent::aux::session_impl;
+
+namespace libtorrent
+{
+ namespace
+ {
+ void throw_invalid_handle()
+ {
+ throw invalid_handle();
+ }
+
+ template<class Ret, class F>
+ Ret call_member(
+ session_impl* ses
+ , aux::checker_impl* chk
+ , sha1_hash const& hash
+ , F f)
+ {
+ if (ses == 0) throw_invalid_handle();
+
+ if (chk)
+ {
+ mutex::scoped_lock l(chk->m_mutex);
+ aux::piece_checker_data* d = chk->find_torrent(hash);
+ if (d != 0) return f(*d->torrent_ptr);
+ }
+
+ {
+ session_impl::mutex_t::scoped_lock l(ses->m_mutex);
+ boost::shared_ptr<torrent> t = ses->find_torrent(hash).lock();
+ if (t) return f(*t);
+ }
+
+ throw invalid_handle();
+ }
+ }
+
+#ifndef NDEBUG
+
+ void torrent_handle::check_invariant() const
+ {
+ assert((m_ses == 0 && m_chk == 0) || (m_ses != 0));
+ }
+
+#endif
+
+ void torrent_handle::set_max_uploads(int max_uploads) const
+ {
+ INVARIANT_CHECK;
+
+ assert(max_uploads >= 2 || max_uploads == -1);
+
+ call_member<void>(m_ses, m_chk, m_info_hash
+ , bind(&torrent::set_max_uploads, _1, max_uploads));
+ }
+
+ void torrent_handle::use_interface(const char* net_interface) const
+ {
+ INVARIANT_CHECK;
+
+ call_member<void>(m_ses, m_chk, m_info_hash
+ , bind(&torrent::use_interface, _1, net_interface));
+ }
+
+ void torrent_handle::set_max_connections(int max_connections) const
+ {
+ INVARIANT_CHECK;
+
+ assert(max_connections >= 2 || max_connections == -1);
+
+ call_member<void>(m_ses, m_chk, m_info_hash
+ , bind(&torrent::set_max_connections, _1, max_connections));
+ }
+
+ void torrent_handle::set_peer_upload_limit(tcp::endpoint ip, int limit) const
+ {
+ INVARIANT_CHECK;
+ assert(limit >= -1);
+
+ call_member<void>(m_ses, m_chk, m_info_hash
+ , bind(&torrent::set_peer_upload_limit, _1, ip, limit));
+ }
+
+ void torrent_handle::set_peer_download_limit(tcp::endpoint ip, int limit) const
+ {
+ INVARIANT_CHECK;
+ assert(limit >= -1);
+
+ call_member<void>(m_ses, m_chk, m_info_hash
+ , bind(&torrent::set_peer_download_limit, _1, ip, limit));
+ }
+
+ void torrent_handle::set_upload_limit(int limit) const
+ {
+ INVARIANT_CHECK;
+
+ assert(limit >= -1);
+
+ call_member<void>(m_ses, m_chk, m_info_hash
+ , bind(&torrent::set_upload_limit, _1, limit));
+ }
+
+ void torrent_handle::set_download_limit(int limit) const
+ {
+ INVARIANT_CHECK;
+
+ assert(limit >= -1);
+
+ call_member<void>(m_ses, m_chk, m_info_hash
+ , bind(&torrent::set_download_limit, _1, limit));
+ }
+
+ bool torrent_handle::move_storage(
+ boost::filesystem::path const& save_path) const
+ {
+ INVARIANT_CHECK;
+
+ return call_member<bool>(m_ses, m_chk, m_info_hash
+ , bind(&torrent::move_storage, _1, save_path));
+ }
+
+ bool torrent_handle::has_metadata() const
+ {
+ INVARIANT_CHECK;
+
+ return call_member<bool>(m_ses, m_chk, m_info_hash
+ , bind(&torrent::valid_metadata, _1));
+ }
+
+ bool torrent_handle::is_seed() const
+ {
+ INVARIANT_CHECK;
+
+ return call_member<bool>(m_ses, m_chk, m_info_hash
+ , bind(&torrent::is_seed, _1));
+ }
+
+ bool torrent_handle::is_paused() const
+ {
+ INVARIANT_CHECK;
+
+ return call_member<bool>(m_ses, m_chk, m_info_hash
+ , bind(&torrent::is_paused, _1));
+ }
+
+ void torrent_handle::pause() const
+ {
+ INVARIANT_CHECK;
+
+ call_member<void>(m_ses, m_chk, m_info_hash
+ , bind(&torrent::pause, _1));
+ }
+
+ void torrent_handle::resume() const
+ {
+ INVARIANT_CHECK;
+
+ call_member<void>(m_ses, m_chk, m_info_hash
+ , bind(&torrent::resume, _1));
+ }
+
+ void torrent_handle::set_tracker_login(std::string const& name
+ , std::string const& password) const
+ {
+ INVARIANT_CHECK;
+
+ call_member<void>(m_ses, m_chk, m_info_hash
+ , bind(&torrent::set_tracker_login, _1, name, password));
+ }
+
+ void torrent_handle::file_progress(std::vector<float>& progress)
+ {
+ INVARIANT_CHECK;
+
+ if (m_ses == 0) throw_invalid_handle();
+
+ if (m_chk)
+ {
+ mutex::scoped_lock l(m_chk->m_mutex);
+
+ aux::piece_checker_data* d = m_chk->find_torrent(m_info_hash);
+ if (d != 0)
+ {
+ if (!d->processing)
+ {
+ torrent_info const& info = d->torrent_ptr->torrent_file();
+ progress.clear();
+ progress.resize(info.num_files(), 0.f);
+ return;
+ }
+ d->torrent_ptr->file_progress(progress);
+ return;
+ }
+ }
+
+ {
+ session_impl::mutex_t::scoped_lock l(m_ses->m_mutex);
+ boost::shared_ptr<torrent> t = m_ses->find_torrent(m_info_hash).lock();
+ if (t) return t->file_progress(progress);
+ }
+
+ throw_invalid_handle();
+ }
+
+ torrent_status torrent_handle::status() const
+ {
+ INVARIANT_CHECK;
+
+ if (m_ses == 0) throw_invalid_handle();
+
+ if (m_chk)
+ {
+ mutex::scoped_lock l(m_chk->m_mutex);
+
+ aux::piece_checker_data* d = m_chk->find_torrent(m_info_hash);
+ if (d != 0)
+ {
+ torrent_status st;
+
+ if (d->processing)
+ {
+ if (d->torrent_ptr->is_allocating())
+ st.state = torrent_status::allocating;
+ else
+ st.state = torrent_status::checking_files;
+ }
+ else
+ st.state = torrent_status::queued_for_checking;
+ st.progress = d->progress;
+ st.paused = d->torrent_ptr->is_paused();
+ return st;
+ }
+ }
+
+ {
+ session_impl::mutex_t::scoped_lock l(m_ses->m_mutex);
+ boost::shared_ptr<torrent> t = m_ses->find_torrent(m_info_hash).lock();
+ if (t) return t->status();
+ }
+
+ throw_invalid_handle();
+ return torrent_status();
+ }
+
+ void torrent_handle::set_sequenced_download_threshold(int threshold) const
+ {
+ INVARIANT_CHECK;
+ call_member<void>(m_ses, m_chk, m_info_hash
+ , bind(&torrent::set_sequenced_download_threshold, _1, threshold));
+ }
+
+ void torrent_handle::filter_piece(int index, bool filter) const
+ {
+ INVARIANT_CHECK;
+ call_member<void>(m_ses, m_chk, m_info_hash
+ , bind(&torrent::filter_piece, _1, index, filter));
+ }
+
+ void torrent_handle::filter_pieces(std::vector<bool> const& pieces) const
+ {
+ INVARIANT_CHECK;
+ call_member<void>(m_ses, m_chk, m_info_hash
+ , bind(&torrent::filter_pieces, _1, pieces));
+ }
+
+ bool torrent_handle::is_piece_filtered(int index) const
+ {
+ INVARIANT_CHECK;
+ return call_member<bool>(m_ses, m_chk, m_info_hash
+ , bind(&torrent::is_piece_filtered, _1, index));
+ }
+
+ std::vector<bool> torrent_handle::filtered_pieces() const
+ {
+ INVARIANT_CHECK;
+ std::vector<bool> ret;
+ call_member<void>(m_ses, m_chk, m_info_hash
+ , bind(&torrent::filtered_pieces, _1, boost::ref(ret)));
+ return ret;
+ }
+
+ void torrent_handle::filter_files(std::vector<bool> const& files) const
+ {
+ INVARIANT_CHECK;
+ call_member<void>(m_ses, m_chk, m_info_hash
+ , bind(&torrent::filter_files, _1, files));
+ }
+
+ std::vector<announce_entry> const& torrent_handle::trackers() const
+ {
+ INVARIANT_CHECK;
+
+ return call_member<std::vector<announce_entry> const&>(m_ses
+ , m_chk, m_info_hash, bind(&torrent::trackers, _1));
+ }
+
+ void torrent_handle::add_url_seed(std::string const& url)
+ {
+ INVARIANT_CHECK;
+
+ return call_member<void>(m_ses, m_chk, m_info_hash
+ , bind(&torrent::add_url_seed, _1, url));
+ }
+
+ void torrent_handle::replace_trackers(
+ std::vector<announce_entry> const& urls) const
+ {
+ INVARIANT_CHECK;
+
+ call_member<void>(m_ses, m_chk, m_info_hash
+ , bind(&torrent::replace_trackers, _1, urls));
+ }
+
+ const torrent_info& torrent_handle::get_torrent_info() const
+ {
+ INVARIANT_CHECK;
+
+ if (!has_metadata()) throw_invalid_handle();
+ return call_member<torrent_info const&>(m_ses, m_chk, m_info_hash
+ , bind(&torrent::torrent_file, _1));
+ }
+
+ bool torrent_handle::is_valid() const
+ {
+ INVARIANT_CHECK;
+
+ if (m_ses == 0) return false;
+
+ if (m_chk)
+ {
+ mutex::scoped_lock l(m_chk->m_mutex);
+ aux::piece_checker_data* d = m_chk->find_torrent(m_info_hash);
+ if (d != 0) return true;
+ }
+
+ {
+ session_impl::mutex_t::scoped_lock l(m_ses->m_mutex);
+ boost::weak_ptr<torrent> t = m_ses->find_torrent(m_info_hash);
+ if (!t.expired()) return true;
+ }
+
+ return false;
+ }
+
+ entry torrent_handle::write_resume_data() const
+ {
+ INVARIANT_CHECK;
+
+ std::vector<int> piece_index;
+ if (m_ses == 0) return entry();
+
+ session_impl::mutex_t::scoped_lock l(m_ses->m_mutex);
+ boost::shared_ptr<torrent> t = m_ses->find_torrent(m_info_hash).lock();
+ if (!t) return entry();
+
+ if (!t->valid_metadata()) return entry();
+
+ t->filesystem().export_piece_map(piece_index);
+
+ entry ret(entry::dictionary_t);
+
+ ret["file-format"] = "libtorrent resume file";
+ ret["file-version"] = 1;
+
+ const sha1_hash& info_hash = t->torrent_file().info_hash();
+ ret["info-hash"] = std::string((char*)info_hash.begin(), (char*)info_hash.end());
+
+ ret["slots"] = entry(entry::list_t);
+ entry::list_type& slots = ret["slots"].list();
+ std::copy(piece_index.begin(), piece_index.end(), std::back_inserter(slots));
+
+ const piece_picker& p = t->picker();
+
+ const std::vector<piece_picker::downloading_piece>& q
+ = p.get_download_queue();
+
+ // blocks per piece
+ int num_blocks_per_piece =
+ static_cast<int>(t->torrent_file().piece_length()) / t->block_size();
+ ret["blocks per piece"] = num_blocks_per_piece;
+
+ // unfinished pieces
+ ret["unfinished"] = entry::list_type();
+ entry::list_type& up = ret["unfinished"].list();
+
+ // info for each unfinished piece
+ for (std::vector<piece_picker::downloading_piece>::const_iterator i
+ = q.begin(); i != q.end(); ++i)
+ {
+ if (i->finished_blocks.count() == 0) continue;
+
+ entry piece_struct(entry::dictionary_t);
+
+ // the unfinished piece's index
+ piece_struct["piece"] = i->index;
+
+ std::string bitmask;
+ const int num_bitmask_bytes
+ = std::max(num_blocks_per_piece / 8, 1);
+
+ for (int j = 0; j < num_bitmask_bytes; ++j)
+ {
+ unsigned char v = 0;
+ for (int k = 0; k < 8; ++k)
+ v |= i->finished_blocks[j*8+k]?(1 << k):0;
+ bitmask.insert(bitmask.end(), v);
+ }
+ piece_struct["bitmask"] = bitmask;
+
+ assert(t->filesystem().slot_for_piece(i->index) >= 0);
+ unsigned long adler
+ = t->filesystem().piece_crc(
+ t->filesystem().slot_for_piece(i->index)
+ , t->block_size()
+ , i->finished_blocks);
+
+ piece_struct["adler32"] = adler;
+
+ // push the struct onto the unfinished-piece list
+ up.push_back(piece_struct);
+ }
+
+ // write local peers
+
+ ret["peers"] = entry::list_type();
+ entry::list_type& peer_list = ret["peers"].list();
+
+ policy& pol = t->get_policy();
+
+ for (policy::iterator i = pol.begin_peer()
+ , end(pol.end_peer()); i != end; ++i)
+ {
+ // we cannot save remote connection
+ // since we don't know their listen port
+ // unless they gave us their listen port
+ // through the extension handshake
+ // so, if the peer is not connectable (i.e. we
+ // don't know its listen port) or if it has
+ // been banned, don't save it.
+ if (i->type == policy::peer::not_connectable
+ || i->banned) continue;
+
+ tcp::endpoint ip = i->ip;
+ entry peer(entry::dictionary_t);
+ peer["ip"] = ip.address().to_string();
+ peer["port"] = ip.port();
+ peer_list.push_back(peer);
+ }
+
+ std::vector<std::pair<size_type, std::time_t> > file_sizes
+ = get_filesizes(t->torrent_file(), t->save_path());
+
+ ret["file sizes"] = entry::list_type();
+ entry::list_type& fl = ret["file sizes"].list();
+ for (std::vector<std::pair<size_type, std::time_t> >::iterator i
+ = file_sizes.begin(), end(file_sizes.end()); i != end; ++i)
+ {
+ entry::list_type p;
+ p.push_back(entry(i->first));
+ p.push_back(entry(i->second));
+ fl.push_back(entry(p));
+ }
+
+ return ret;
+ }
+
+
+ boost::filesystem::path torrent_handle::save_path() const
+ {
+ INVARIANT_CHECK;
+
+ return call_member<boost::filesystem::path>(m_ses, m_chk, m_info_hash
+ , bind(&torrent::save_path, _1));
+ }
+
+ std::vector<char> const& torrent_handle::metadata() const
+ {
+ INVARIANT_CHECK;
+
+ return call_member<std::vector<char> const&>(m_ses, m_chk, m_info_hash
+ , bind(&torrent::metadata, _1));
+ }
+
+ void torrent_handle::connect_peer(tcp::endpoint const& adr) const
+ {
+ INVARIANT_CHECK;
+
+ if (m_ses == 0) throw_invalid_handle();
+
+ session_impl::mutex_t::scoped_lock l(m_ses->m_mutex);
+ boost::shared_ptr<torrent> t = m_ses->find_torrent(m_info_hash).lock();
+
+ if (!t)
+ {
+ // the torrent is being checked. Add the peer to its
+ // peer list. The entries in there will be connected
+ // once the checking is complete.
+ mutex::scoped_lock l2(m_chk->m_mutex);
+
+ aux::piece_checker_data* d = m_chk->find_torrent(m_info_hash);
+ if (d == 0) throw_invalid_handle();
+ d->peers.push_back(adr);
+ return;
+ }
+
+ peer_id id;
+ std::fill(id.begin(), id.end(), 0);
+ t->get_policy().peer_from_tracker(adr, id);
+ }
+
+ void torrent_handle::force_reannounce(
+ boost::posix_time::time_duration duration) const
+ {
+ INVARIANT_CHECK;
+
+ if (m_ses == 0) throw_invalid_handle();
+
+ session_impl::mutex_t::scoped_lock l(m_ses->m_mutex);
+ boost::shared_ptr<torrent> t = m_ses->find_torrent(m_info_hash).lock();
+ if (!t) throw_invalid_handle();
+
+ using boost::posix_time::second_clock;
+ t->force_tracker_request(second_clock::universal_time()
+ + duration);
+ }
+
+ void torrent_handle::force_reannounce() const
+ {
+ INVARIANT_CHECK;
+
+ if (m_ses == 0) throw_invalid_handle();
+
+ session_impl::mutex_t::scoped_lock l(m_ses->m_mutex);
+ boost::shared_ptr<torrent> t = m_ses->find_torrent(m_info_hash).lock();
+ if (!t) throw_invalid_handle();
+
+ t->force_tracker_request();
+ }
+
+ void torrent_handle::set_ratio(float ratio) const
+ {
+ INVARIANT_CHECK;
+
+ assert(ratio >= 0.f);
+
+ if (ratio < 1.f && ratio > 0.f)
+ ratio = 1.f;
+
+ call_member<void>(m_ses, m_chk, m_info_hash
+ , bind(&torrent::set_ratio, _1, ratio));
+ }
+
+ void torrent_handle::get_peer_info(std::vector<peer_info>& v) const
+ {
+ INVARIANT_CHECK;
+
+ v.clear();
+ if (m_ses == 0) throw_invalid_handle();
+
+ session_impl::mutex_t::scoped_lock l(m_ses->m_mutex);
+
+ boost::shared_ptr<const torrent> t = m_ses->find_torrent(m_info_hash).lock();
+ if (!t) return;
+
+ for (torrent::const_peer_iterator i = t->begin();
+ i != t->end(); ++i)
+ {
+ peer_connection* peer = i->second;
+
+ // peers that haven't finished the handshake should
+ // not be included in this list
+ if (peer->associated_torrent().expired()) continue;
+
+ v.push_back(peer_info());
+ peer_info& p = v.back();
+
+ peer->get_peer_info(p);
+ }
+ }
+
+ bool torrent_handle::send_chat_message(tcp::endpoint ip, std::string message) const
+ {
+ if (m_ses == 0) throw_invalid_handle();
+
+ session_impl::mutex_t::scoped_lock l(m_ses->m_mutex);
+ boost::shared_ptr<torrent> t = m_ses->find_torrent(m_info_hash).lock();
+ if (!t) return false;
+
+ for (torrent::const_peer_iterator i = t->begin();
+ i != t->end(); ++i)
+ {
+ peer_connection* peer = i->second;
+
+ // peers that haven't finished the handshake should
+ // not be included in this list
+ if (peer->associated_torrent().expired()) continue;
+
+ tcp::endpoint sender = peer->get_socket()->remote_endpoint();
+ // loop until we find the required ip tcp::endpoint
+ if (ip != sender) continue;
+
+ bt_peer_connection* p = dynamic_cast<bt_peer_connection*>(peer);
+ if (!p) return false;
+
+ // peers that don's support chat message extension
+ // should not be included either
+ if (!p->supports_extension(extended_chat_message))
+ return false;
+
+ // send the message
+ p->write_chat_message(message);
+ return true;
+ }
+ return false;
+ }
+
+ void torrent_handle::get_download_queue(std::vector<partial_piece_info>& queue) const
+ {
+ INVARIANT_CHECK;
+
+ if (m_ses == 0) throw_invalid_handle();
+
+ session_impl::mutex_t::scoped_lock l(m_ses->m_mutex);
+ boost::shared_ptr<torrent> t = m_ses->find_torrent(m_info_hash).lock();
+
+ queue.clear();
+ if (!t) return;
+ if (!t->valid_metadata()) return;
+
+ const piece_picker& p = t->picker();
+
+ const std::vector<piece_picker::downloading_piece>& q
+ = p.get_download_queue();
+
+ for (std::vector<piece_picker::downloading_piece>::const_iterator i
+ = q.begin(); i != q.end(); ++i)
+ {
+ partial_piece_info pi;
+ pi.finished_blocks = i->finished_blocks;
+ pi.requested_blocks = i->requested_blocks;
+ for (int j = 0; j < partial_piece_info::max_blocks_per_piece; ++j)
+ {
+ pi.peer[j] = i->info[j].peer;
+ pi.num_downloads[j] = i->info[j].num_downloads;
+ }
+ pi.piece_index = i->index;
+ pi.blocks_in_piece = p.blocks_in_piece(i->index);
+ queue.push_back(pi);
+ }
+ }
+
+}
+
diff --git a/library/torrent_info.cpp b/library/torrent_info.cpp
new file mode 100755
index 000000000..6b008369b
--- /dev/null
+++ b/library/torrent_info.cpp
@@ -0,0 +1,833 @@
+/*
+
+Copyright (c) 2003, 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.
+
+*/
+
+#include <ctime>
+#include <iostream>
+#include <fstream>
+#include <iomanip>
+#include <iterator>
+#include <algorithm>
+#include <set>
+
+#ifdef _MSC_VER
+#pragma warning(push, 1)
+#endif
+
+#include <boost/lexical_cast.hpp>
+#include <boost/date_time/gregorian/gregorian_types.hpp>
+#include <boost/filesystem/path.hpp>
+#include <boost/next_prior.hpp>
+#include <boost/bind.hpp>
+
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+#include "libtorrent/torrent_info.hpp"
+#include "libtorrent/bencode.hpp"
+#include "libtorrent/hasher.hpp"
+#include "libtorrent/entry.hpp"
+
+using namespace libtorrent;
+using namespace boost::filesystem;
+
+namespace
+{
+ void convert_to_utf8(std::string& str, unsigned char chr)
+ {
+ str += 0xc0 | ((chr & 0xff) >> 6);
+ str += 0x80 | (chr & 0x3f);
+ }
+
+ void verify_encoding(file_entry& target)
+ {
+ std::string tmp_path;
+ std::string file_path = target.path.string();
+ bool valid_encoding = true;
+ for (std::string::iterator i = file_path.begin()
+ , end(file_path.end()); i != end; ++i)
+ {
+ // valid ascii-character
+ if ((*i & 0x80) == 0)
+ {
+ tmp_path += *i;
+ continue;
+ }
+
+ if (std::distance(i, end) < 2)
+ {
+ convert_to_utf8(tmp_path, *i);
+ valid_encoding = false;
+ continue;
+ }
+
+ // valid 2-byte utf-8 character
+ if ((i[0] & 0xe0) == 0xc0
+ && (i[1] & 0xc0) == 0x80)
+ {
+ tmp_path += i[0];
+ tmp_path += i[1];
+ i += 1;
+ continue;
+ }
+
+ if (std::distance(i, end) < 3)
+ {
+ convert_to_utf8(tmp_path, *i);
+ valid_encoding = false;
+ continue;
+ }
+
+ // valid 3-byte utf-8 character
+ if ((i[0] & 0xf0) == 0xe0
+ && (i[1] & 0xc0) == 0x80
+ && (i[2] & 0xc0) == 0x80)
+ {
+ tmp_path += i[0];
+ tmp_path += i[1];
+ tmp_path += i[2];
+ i += 2;
+ continue;
+ }
+
+ if (std::distance(i, end) < 4)
+ {
+ convert_to_utf8(tmp_path, *i);
+ valid_encoding = false;
+ continue;
+ }
+
+ // valid 4-byte utf-8 character
+ if ((i[0] & 0xf0) == 0xe0
+ && (i[1] & 0xc0) == 0x80
+ && (i[2] & 0xc0) == 0x80
+ && (i[3] & 0xc0) == 0x80)
+ {
+ tmp_path += i[0];
+ tmp_path += i[1];
+ tmp_path += i[2];
+ tmp_path += i[3];
+ i += 3;
+ continue;
+ }
+
+ convert_to_utf8(tmp_path, *i);
+ valid_encoding = false;
+ }
+ // the encoding was not valid utf-8
+ // save the original encoding and replace the
+ // commonly used path with the correctly
+ // encoded string
+ if (!valid_encoding)
+ {
+ target.orig_path.reset(new path(target.path));
+ target.path = tmp_path;
+ }
+ }
+
+ void extract_single_file(const entry& dict, file_entry& target
+ , std::string const& root_dir)
+ {
+ target.size = dict["length"].integer();
+ target.path = root_dir;
+
+
+ // prefer the name.utf-8
+ // because if it exists, it is more
+ // likely to be correctly encoded
+
+ const entry::list_type* list = 0;
+ if (entry const* p = dict.find_key("path.utf-8"))
+ {
+ list = &p->list();
+ }
+ else
+ {
+ list = &dict["path"].list();
+ }
+
+ for (entry::list_type::const_iterator i = list->begin();
+ i != list->end(); ++i)
+ {
+ if (i->string() != "..")
+ target.path /= i->string();
+ }
+ verify_encoding(target);
+ if (target.path.is_complete()) throw std::runtime_error("torrent contains "
+ "a file with an absolute path: '"
+ + target.path.native_file_string() + "'");
+ }
+
+ void extract_files(const entry::list_type& list, std::vector<file_entry>& target
+ , std::string const& root_dir)
+ {
+ size_type offset = 0;
+ for (entry::list_type::const_iterator i = list.begin(); i != list.end(); ++i)
+ {
+ target.push_back(file_entry());
+ extract_single_file(*i, target.back(), root_dir);
+ target.back().offset = offset;
+ offset += target.back().size;
+ }
+ }
+
+ void remove_dir(path& p)
+ {
+ assert(p.begin() != p.end());
+ path tmp;
+ for (path::iterator i = boost::next(p.begin()); i != p.end(); ++i)
+ tmp /= *i;
+ p = tmp;
+ }
+}
+
+namespace libtorrent
+{
+
+ using namespace boost::gregorian;
+ using namespace boost::posix_time;
+
+ // standard constructor that parses a torrent file
+ torrent_info::torrent_info(const entry& torrent_file)
+ : m_creation_date(date(not_a_date_time))
+ , m_multifile(false)
+ , m_private(false)
+ , m_extra_info(entry::dictionary_t)
+ {
+ try
+ {
+ read_torrent_info(torrent_file);
+ }
+ catch(type_error&)
+ {
+ throw invalid_torrent_file();
+ }
+ }
+
+ // constructor used for creating new torrents
+ // will not contain any hashes, comments, creation date
+ // just the necessary to use it with piece manager
+ // used for torrents with no metadata
+ torrent_info::torrent_info(sha1_hash const& info_hash)
+ : m_piece_length(256 * 1024)
+ , m_total_size(0)
+ , m_info_hash(info_hash)
+ , m_name()
+ , m_creation_date(second_clock::universal_time())
+ , m_multifile(false)
+ , m_extra_info(entry::dictionary_t)
+ {
+ }
+
+ torrent_info::torrent_info()
+ : m_piece_length(256 * 1024)
+ , m_total_size(0)
+ , m_info_hash(0)
+ , m_name()
+ , m_creation_date(second_clock::universal_time())
+ , m_multifile(false)
+ , m_extra_info(entry::dictionary_t)
+ {
+ }
+
+ torrent_info::~torrent_info()
+ {}
+
+ void torrent_info::set_piece_size(int size)
+ {
+ // make sure the size is an even power of 2
+#ifndef NDEBUG
+ for (int i = 0; i < 32; ++i)
+ {
+ if (size & (1 << i))
+ {
+ assert((size & ~(1 << i)) == 0);
+ break;
+ }
+ }
+#endif
+ m_piece_length = size;
+
+
+ int num_pieces = static_cast<int>(
+ (m_total_size + m_piece_length - 1) / m_piece_length);
+ int old_num_pieces = static_cast<int>(m_piece_hash.size());
+
+ m_piece_hash.resize(num_pieces);
+ for (int i = old_num_pieces; i < num_pieces; ++i)
+ {
+ m_piece_hash[i].clear();
+ }
+ }
+
+ void torrent_info::parse_info_section(entry const& info)
+ {
+ // encode the info-field in order to calculate it's sha1-hash
+ std::vector<char> buf;
+ bencode(std::back_inserter(buf), info);
+ hasher h;
+ h.update(&buf[0], (int)buf.size());
+ m_info_hash = h.final();
+
+ // extract piece length
+ m_piece_length = (int)info["piece length"].integer();
+ if (m_piece_length <= 0) throw std::runtime_error("invalid torrent. piece length <= 0");
+
+ // extract file name (or the directory name if it's a multifile libtorrent)
+ if (entry const* e = info.find_key("name.utf-8"))
+ { m_name = e->string(); }
+ else
+ { m_name = info["name"].string(); }
+
+ 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(
+ "torrent contains name with directories: '" + m_name + "'");
+
+ // extract file list
+ entry const* i = info.find_key("files");
+ if (i == 0)
+ {
+ // if there's no list of files, there has to be a length
+ // field.
+ file_entry e;
+ e.path = m_name;
+ e.offset = 0;
+ e.size = info["length"].integer();
+ m_files.push_back(e);
+ }
+ else
+ {
+ extract_files(i->list(), m_files, m_name);
+ m_multifile = true;
+ }
+
+ // calculate total size of all pieces
+ m_total_size = 0;
+ for (std::vector<file_entry>::iterator i = m_files.begin(); i != m_files.end(); ++i)
+ m_total_size += i->size;
+
+ // extract sha-1 hashes for all pieces
+ // we want this division to round upwards, that's why we have the
+ // extra addition
+
+ int num_pieces = static_cast<int>((m_total_size + m_piece_length - 1) / m_piece_length);
+ m_piece_hash.resize(num_pieces);
+ const std::string& hash_string = info["pieces"].string();
+
+ if ((int)hash_string.length() != num_pieces * 20)
+ throw invalid_torrent_file();
+
+ for (int i = 0; i < num_pieces; ++i)
+ std::copy(
+ hash_string.begin() + i*20
+ , hash_string.begin() + (i+1)*20
+ , m_piece_hash[i].begin());
+
+ for (entry::dictionary_type::const_iterator i = info.dict().begin()
+ , end(info.dict().end()); i != end; ++i)
+ {
+ if (i->first == "pieces"
+ || i->first == "piece length"
+ || i->first == "length")
+ continue;
+ m_extra_info[i->first] = i->second;
+ }
+
+ if (entry const* priv = info.find_key("private"))
+ {
+ if (priv->type() != entry::int_t
+ || priv->integer() != 0)
+ {
+ // this key exists and it's not 0.
+ // consider the torrent private
+ m_private = true;
+ }
+ }
+
+#ifndef NDEBUG
+ std::vector<char> info_section_buf;
+ entry gen_info_section = create_info_metadata();
+ bencode(std::back_inserter(info_section_buf), gen_info_section);
+ assert(hasher(&info_section_buf[0], info_section_buf.size()).final()
+ == m_info_hash);
+#endif
+ }
+
+ // extracts information from a libtorrent file and fills in the structures in
+ // the torrent object
+ void torrent_info::read_torrent_info(const entry& torrent_file)
+ {
+ // extract the url of the tracker
+ if (entry const* i = torrent_file.find_key("announce-list"))
+ {
+ const entry::list_type& l = i->list();
+ for (entry::list_type::const_iterator j = l.begin(); j != l.end(); ++j)
+ {
+ const entry::list_type& ll = j->list();
+ for (entry::list_type::const_iterator k = ll.begin(); k != ll.end(); ++k)
+ {
+ announce_entry e(k->string());
+ e.tier = (int)std::distance(l.begin(), j);
+ m_urls.push_back(e);
+ }
+ }
+
+ if (m_urls.size() == 0)
+ {
+ // the announce-list is empty
+ // fall back to look for announce
+ m_urls.push_back(announce_entry(
+ torrent_file["announce"].string()));
+ }
+ // shuffle each tier
+ std::vector<announce_entry>::iterator start = m_urls.begin();
+ std::vector<announce_entry>::iterator stop;
+ int current_tier = m_urls.front().tier;
+ for (stop = m_urls.begin(); stop != m_urls.end(); ++stop)
+ {
+ if (stop->tier != current_tier)
+ {
+ std::random_shuffle(start, stop);
+ start = stop;
+ current_tier = stop->tier;
+ }
+ }
+ std::random_shuffle(start, stop);
+ }
+ else if (entry const* i = torrent_file.find_key("announce"))
+ {
+ m_urls.push_back(announce_entry(i->string()));
+ }
+
+ if (entry const* i = torrent_file.find_key("nodes"))
+ {
+ entry::list_type const& list = i->list();
+ for (entry::list_type::const_iterator i(list.begin())
+ , end(list.end()); i != end; ++i)
+ {
+ if (i->type() != entry::list_t) continue;
+ entry::list_type const& l = i->list();
+ entry::list_type::const_iterator iter = l.begin();
+ if (l.size() < 1) continue;
+ std::string const& hostname = iter->string();
+ ++iter;
+ int port = 6881;
+ if (l.end() != iter) port = iter->integer();
+ m_nodes.push_back(std::make_pair(hostname, port));
+ }
+ }
+
+ // extract creation date
+ try
+ {
+ m_creation_date = ptime(date(1970, Jan, 1))
+ + seconds(long(torrent_file["creation date"].integer()));
+ }
+ catch (type_error) {}
+
+ // if there are any url-seeds, extract them
+ try
+ {
+ entry const& url_seeds = torrent_file["url-list"];
+ if (url_seeds.type() == entry::string_t)
+ {
+ m_url_seeds.push_back(url_seeds.string());
+ }
+ else if (url_seeds.type() == entry::list_t)
+ {
+ entry::list_type const& l = url_seeds.list();
+ for (entry::list_type::const_iterator i = l.begin();
+ i != l.end(); ++i)
+ {
+ m_url_seeds.push_back(i->string());
+ }
+ }
+ }
+ catch (type_error&) {}
+
+ // extract comment
+ if (entry const* e = torrent_file.find_key("comment.utf-8"))
+ { m_comment = e->string(); }
+ else if (entry const* e = torrent_file.find_key("comment"))
+ { m_comment = e->string(); }
+
+ if (entry const* e = torrent_file.find_key("created by.utf-8"))
+ { m_created_by = e->string(); }
+ else if (entry const* e = torrent_file.find_key("created by"))
+ { m_created_by = e->string(); }
+
+ parse_info_section(torrent_file["info"]);
+ }
+
+ boost::optional<ptime>
+ torrent_info::creation_date() const
+ {
+ if (m_creation_date != ptime(date(not_a_date_time)))
+ {
+ return boost::optional<ptime>(m_creation_date);
+ }
+ return boost::optional<ptime>();
+ }
+
+ void torrent_info::add_tracker(std::string const& url, int tier)
+ {
+ announce_entry e(url);
+ e.tier = tier;
+ m_urls.push_back(e);
+
+ using boost::bind;
+ std::sort(m_urls.begin(), m_urls.end(), boost::bind<bool>(std::less<int>()
+ , bind(&announce_entry::tier, _1), bind(&announce_entry::tier, _2)));
+ }
+
+ void torrent_info::add_file(boost::filesystem::path file, size_type size)
+ {
+ assert(file.begin() != file.end());
+
+ if (!file.has_branch_path())
+ {
+ // you have already added at least one file with a
+ // path to the file (branch_path), which means that
+ // all the other files need to be in the same top
+ // directory as the first file.
+ assert(m_files.empty());
+ assert(!m_multifile);
+ m_name = file.string();
+ }
+ else
+ {
+#ifndef NDEBUG
+ if (!m_files.empty())
+ assert(m_name == *file.begin());
+#endif
+ m_multifile = true;
+ m_name = *file.begin();
+ }
+
+ file_entry e;
+ e.path = file;
+ e.size = size;
+ m_files.push_back(e);
+
+ m_total_size += size;
+
+ int num_pieces = static_cast<int>(
+ (m_total_size + m_piece_length - 1) / m_piece_length);
+ int old_num_pieces = static_cast<int>(m_piece_hash.size());
+
+ m_piece_hash.resize(num_pieces);
+ for (std::vector<sha1_hash>::iterator i = m_piece_hash.begin() + old_num_pieces;
+ i != m_piece_hash.end(); ++i)
+ {
+ i->clear();
+ }
+
+ }
+
+ void torrent_info::add_url_seed(std::string const& url)
+ {
+ m_url_seeds.push_back(url);
+ }
+
+ void torrent_info::set_comment(char const* str)
+ {
+ m_comment = str;
+ }
+
+ void torrent_info::set_creator(char const* str)
+ {
+ m_created_by = str;
+ }
+
+ entry torrent_info::create_info_metadata() const
+ {
+ namespace fs = boost::filesystem;
+
+ // you have to add files to the torrent first
+ assert(!m_files.empty());
+
+ entry info(m_extra_info);
+
+ if (!info.find_key("name"))
+ info["name"] = m_name;
+
+ if (!m_multifile)
+ {
+ info["length"] = m_files.front().size;
+ }
+ else
+ {
+ if (!info.find_key("files"))
+ {
+ entry& files = info["files"];
+ files = entry(entry::list_t);
+
+ for (std::vector<file_entry>::const_iterator i = m_files.begin();
+ i != m_files.end(); ++i)
+ {
+ files.list().push_back(entry(entry::dictionary_t));
+ entry& file_e = files.list().back();
+ file_e["length"] = i->size;
+ entry& path_e = file_e["path"];
+ path_e = entry(entry::list_t);
+
+ fs::path const* file_path;
+ if (i->orig_path) file_path = &(*i->orig_path);
+ else file_path = &i->path;
+ assert(file_path->has_branch_path());
+ assert(*file_path->begin() == m_name);
+
+ for (fs::path::iterator j = boost::next(file_path->begin());
+ j != file_path->end(); ++j)
+ {
+ path_e.list().push_back(entry(*j));
+ }
+ }
+ }
+ }
+
+ info["piece length"] = piece_length();
+ entry& pieces = info["pieces"];
+ pieces = entry(entry::string_t);
+
+ std::string& p = pieces.string();
+
+ for (std::vector<sha1_hash>::const_iterator i = m_piece_hash.begin();
+ i != m_piece_hash.end(); ++i)
+ {
+ p.append((char*)i->begin(), (char*)i->end());
+ }
+
+ return info;
+ }
+
+ entry torrent_info::create_torrent() const
+ {
+ assert(m_piece_length > 0);
+
+ using namespace boost::gregorian;
+ using namespace boost::posix_time;
+
+ namespace fs = boost::filesystem;
+
+ entry dict(entry::dictionary_t);
+
+ if ((m_urls.empty() && m_nodes.empty()) || m_files.empty())
+ {
+ // TODO: throw something here
+ // throw
+ return entry();
+ }
+
+ if (m_private) dict["private"] = 1;
+
+ if (!m_urls.empty())
+ dict["announce"] = m_urls.front().url;
+
+ if (!m_nodes.empty())
+ {
+ entry& nodes = dict["nodes"];
+ nodes = entry(entry::list_t);
+ entry::list_type& nodes_list = nodes.list();
+ for (nodes_t::const_iterator i = m_nodes.begin()
+ , end(m_nodes.end()); i != end; ++i)
+ {
+ entry::list_type node;
+ node.push_back(entry(i->first));
+ node.push_back(entry(i->second));
+ nodes_list.push_back(entry(node));
+ }
+ }
+
+ if (m_urls.size() > 1)
+ {
+ entry trackers(entry::list_t);
+ entry tier(entry::list_t);
+ int current_tier = m_urls.front().tier;
+ for (std::vector<announce_entry>::const_iterator i = m_urls.begin();
+ i != m_urls.end(); ++i)
+ {
+ if (i->tier != current_tier)
+ {
+ current_tier = i->tier;
+ trackers.list().push_back(tier);
+ tier.list().clear();
+ }
+ tier.list().push_back(entry(i->url));
+ }
+ trackers.list().push_back(tier);
+ dict["announce-list"] = trackers;
+ }
+
+ if (!m_comment.empty())
+ dict["comment"] = m_comment;
+
+ dict["creation date"] =
+ (m_creation_date - ptime(date(1970, Jan, 1))).total_seconds();
+
+ if (!m_created_by.empty())
+ dict["created by"] = m_created_by;
+
+ if (!m_url_seeds.empty())
+ {
+ if (m_url_seeds.size() == 1)
+ {
+ dict["url-list"] = m_url_seeds.front();
+ }
+ else
+ {
+ entry& list = dict["url-list"];
+ list = entry(entry::list_t);
+ for (std::vector<std::string>::const_iterator i
+ = m_url_seeds.begin(); i != m_url_seeds.end(); ++i)
+ {
+ list.list().push_back(entry(*i));
+ }
+ }
+ }
+
+ dict["info"] = create_info_metadata();
+
+ entry const& info_section = dict["info"];
+ std::vector<char> buf;
+ bencode(std::back_inserter(buf), info_section);
+ m_info_hash = hasher(&buf[0], buf.size()).final();
+
+ return dict;
+ }
+
+ void torrent_info::set_hash(int index, const sha1_hash& h)
+ {
+ assert(index >= 0);
+ assert(index < (int)m_piece_hash.size());
+ m_piece_hash[index] = h;
+ }
+
+ void torrent_info::convert_file_names()
+ {
+ assert(false);
+ }
+
+ void torrent_info::print(std::ostream& os) const
+ {
+ os << "trackers:\n";
+ for (std::vector<announce_entry>::const_iterator i = trackers().begin();
+ i != trackers().end(); ++i)
+ {
+ os << i->tier << ": " << i->url << "\n";
+ }
+ if (!m_comment.empty())
+ os << "comment: " << m_comment << "\n";
+ if (m_creation_date != ptime(date(not_a_date_time)))
+ os << "creation date: " << to_simple_string(m_creation_date) << "\n";
+ os << "private: " << (m_private?"yes":"no") << "\n";
+ os << "number of pieces: " << num_pieces() << "\n";
+ os << "piece length: " << piece_length() << "\n";
+ os << "files:\n";
+ for (file_iterator i = begin_files(); i != end_files(); ++i)
+ os << " " << std::setw(11) << i->size << " " << i->path.string() << "\n";
+ }
+
+ size_type torrent_info::piece_size(int index) const
+ {
+ assert(index >= 0 && index < num_pieces());
+ if (index == num_pieces()-1)
+ {
+ size_type size = total_size()
+ - (num_pieces() - 1) * piece_length();
+ assert(size > 0);
+ assert(size <= piece_length());
+ return size;
+ }
+ else
+ return piece_length();
+ }
+
+ void torrent_info::add_node(std::pair<std::string, int> const& node)
+ {
+ m_nodes.push_back(node);
+ }
+
+ std::vector<file_slice> torrent_info::map_block(int piece, size_type offset
+ , int size) const
+ {
+ assert(num_files() > 0);
+ std::vector<file_slice> ret;
+
+ size_type start = piece * (size_type)m_piece_length + offset;
+ assert(start + size <= m_total_size);
+
+ // find the file iterator and file offset
+ // TODO: make a vector that can map piece -> file index in O(1)
+ size_type file_offset = start;
+ std::vector<file_entry>::const_iterator file_iter;
+
+ int counter = 0;
+ for (file_iter = begin_files();; ++counter, ++file_iter)
+ {
+ assert(file_iter != end_files());
+ if (file_offset < file_iter->size)
+ {
+ file_slice f;
+ f.file_index = counter;
+ f.offset = file_offset;
+ f.size = (std::min)(file_iter->size - file_offset, (size_type)size);
+ size -= f.size;
+ file_offset += f.size;
+ ret.push_back(f);
+ }
+
+ assert(size >= 0);
+ if (size <= 0) break;
+
+ file_offset -= file_iter->size;
+ }
+ return ret;
+ }
+
+ peer_request torrent_info::map_file(int file_index, size_type file_offset
+ , int size) const
+ {
+ assert(file_index < (int)m_files.size());
+ assert(file_index >= 0);
+ size_type offset = file_offset + m_files[file_index].offset;
+
+ peer_request ret;
+ ret.piece = offset / piece_length();
+ ret.start = offset - ret.piece * piece_length();
+ ret.length = size;
+ return ret;
+ }
+
+}
diff --git a/library/tracker_manager.cpp b/library/tracker_manager.cpp
new file mode 100755
index 000000000..20e234c1f
--- /dev/null
+++ b/library/tracker_manager.cpp
@@ -0,0 +1,569 @@
+/*
+
+Copyright (c) 2003, 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.
+
+*/
+
+#include <vector>
+#include <iostream>
+#include <cctype>
+#include <iomanip>
+#include <sstream>
+
+#include "zlib.h"
+
+#include <boost/bind.hpp>
+
+#include "libtorrent/tracker_manager.hpp"
+#include "libtorrent/http_tracker_connection.hpp"
+#include "libtorrent/udp_tracker_connection.hpp"
+#include "libtorrent/entry.hpp"
+#include "libtorrent/bencode.hpp"
+#include "libtorrent/torrent.hpp"
+
+using namespace libtorrent;
+using boost::tuples::make_tuple;
+using boost::tuples::tuple;
+using boost::bind;
+
+namespace
+{
+ enum
+ {
+ minimum_tracker_response_length = 3,
+ http_buffer_size = 2048
+ };
+
+
+ enum
+ {
+ FTEXT = 0x01,
+ FHCRC = 0x02,
+ FEXTRA = 0x04,
+ FNAME = 0x08,
+ FCOMMENT = 0x10,
+ FRESERVED = 0xe0,
+
+ GZIP_MAGIC0 = 0x1f,
+ GZIP_MAGIC1 = 0x8b
+ };
+
+}
+
+namespace libtorrent
+{
+ using boost::posix_time::second_clock;
+ using boost::posix_time::seconds;
+ using boost::posix_time::ptime;
+ using boost::posix_time::time_duration;
+
+ // returns -1 if gzip header is invalid or the header size in bytes
+ int gzip_header(const char* buf, int size)
+ {
+ assert(buf != 0);
+ assert(size > 0);
+
+ const unsigned char* buffer = reinterpret_cast<const unsigned char*>(buf);
+ const int total_size = size;
+
+ // The zip header cannot be shorter than 10 bytes
+ if (size < 10) return -1;
+
+ // check the magic header of gzip
+ if ((buffer[0] != GZIP_MAGIC0) || (buffer[1] != GZIP_MAGIC1)) return -1;
+
+ int method = buffer[2];
+ int flags = buffer[3];
+
+ // check for reserved flag and make sure it's compressed with the correct metod
+ if (method != Z_DEFLATED || (flags & FRESERVED) != 0) return -1;
+
+ // skip time, xflags, OS code
+ size -= 10;
+ buffer += 10;
+
+ if (flags & FEXTRA)
+ {
+ int extra_len;
+
+ if (size < 2) return -1;
+
+ extra_len = (buffer[1] << 8) | buffer[0];
+
+ if (size < (extra_len+2)) return -1;
+ size -= (extra_len + 2);
+ buffer += (extra_len + 2);
+ }
+
+ if (flags & FNAME)
+ {
+ while (size && *buffer)
+ {
+ --size;
+ ++buffer;
+ }
+ if (!size || *buffer) return -1;
+
+ --size;
+ ++buffer;
+ }
+
+ if (flags & FCOMMENT)
+ {
+ while (size && *buffer)
+ {
+ --size;
+ ++buffer;
+ }
+ if (!size || *buffer) return -1;
+
+ --size;
+ ++buffer;
+ }
+
+ if (flags & FHCRC)
+ {
+ if (size < 2) return -1;
+
+ size -= 2;
+ buffer += 2;
+ }
+
+ return total_size - size;
+ }
+
+ bool inflate_gzip(
+ std::vector<char>& buffer
+ , tracker_request const& req
+ , request_callback* requester
+ , int maximum_tracker_response_length)
+ {
+ assert(maximum_tracker_response_length > 0);
+
+ int header_len = gzip_header(&buffer[0], (int)buffer.size());
+ if (header_len < 0)
+ {
+ requester->tracker_request_error(req, 200, "invalid gzip header in tracker response");
+ return true;
+ }
+
+ // start off wth one kilobyte and grow
+ // if needed
+ std::vector<char> inflate_buffer(1024);
+
+ // initialize the zlib-stream
+ z_stream str;
+
+ // subtract 8 from the end of the buffer since that's CRC32 and input size
+ // and those belong to the gzip file
+ str.avail_in = (int)buffer.size() - header_len - 8;
+ str.next_in = reinterpret_cast<Bytef*>(&buffer[header_len]);
+ str.next_out = reinterpret_cast<Bytef*>(&inflate_buffer[0]);
+ str.avail_out = (int)inflate_buffer.size();
+ str.zalloc = Z_NULL;
+ str.zfree = Z_NULL;
+ str.opaque = 0;
+ // -15 is really important. It will make inflate() not look for a zlib header
+ // and just deflate the buffer
+ if (inflateInit2(&str, -15) != Z_OK)
+ {
+ requester->tracker_request_error(req, 200, "gzip out of memory");
+ return true;
+ }
+
+ // inflate and grow inflate_buffer as needed
+ int ret = inflate(&str, Z_SYNC_FLUSH);
+ while (ret == Z_OK)
+ {
+ if (str.avail_out == 0)
+ {
+ if (inflate_buffer.size() >= (unsigned)maximum_tracker_response_length)
+ {
+ inflateEnd(&str);
+ requester->tracker_request_error(req, 200
+ , "tracker response too large");
+ return true;
+ }
+ int new_size = (int)inflate_buffer.size() * 2;
+ if (new_size > maximum_tracker_response_length) new_size = maximum_tracker_response_length;
+ int old_size = (int)inflate_buffer.size();
+
+ inflate_buffer.resize(new_size);
+ str.next_out = reinterpret_cast<Bytef*>(&inflate_buffer[old_size]);
+ str.avail_out = new_size - old_size;
+ }
+
+ ret = inflate(&str, Z_SYNC_FLUSH);
+ }
+
+ inflate_buffer.resize(inflate_buffer.size() - str.avail_out);
+ inflateEnd(&str);
+
+ if (ret != Z_STREAM_END)
+ {
+ requester->tracker_request_error(req, 200, "gzip error");
+ return true;
+ }
+
+ // commit the resulting buffer
+ std::swap(buffer, inflate_buffer);
+ return false;
+ }
+
+ std::string base64encode(const std::string& s)
+ {
+ static const char base64_table[] =
+ {
+ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
+ 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
+ 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
+ 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
+ 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
+ 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
+ 'w', 'x', 'y', 'z', '0', '1', '2', '3',
+ '4', '5', '6', '7', '8', '9', '+', '/'
+ };
+
+ unsigned char inbuf[3];
+ unsigned char outbuf[4];
+
+ std::string ret;
+ for (std::string::const_iterator i = s.begin(); i != s.end();)
+ {
+ // available input is 1,2 or 3 bytes
+ // since we read 3 bytes at a time at most
+ int available_input = std::min(3, (int)std::distance(i, s.end()));
+
+ // clear input buffer
+ std::fill(inbuf, inbuf+3, 0);
+
+ // read a chunk of input into inbuf
+ for (int j = 0; j < available_input; ++j)
+ {
+ inbuf[j] = *i;
+ ++i;
+ }
+
+ // encode inbuf to outbuf
+ outbuf[0] = (inbuf[0] & 0xfc) >> 2;
+ outbuf[1] = ((inbuf[0] & 0x03) << 4) | ((inbuf [1] & 0xf0) >> 4);
+ outbuf[2] = ((inbuf[1] & 0x0f) << 2) | ((inbuf [2] & 0xc0) >> 6);
+ outbuf[3] = inbuf[2] & 0x3f;
+
+ // write output
+ for (int j = 0; j < available_input+1; ++j)
+ {
+ ret += base64_table[outbuf[j]];
+ }
+
+ // write pad
+ for (int j = 0; j < 3 - available_input; ++j)
+ {
+ ret += '=';
+ }
+ }
+ 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(demuxer& d)
+ : m_demuxer(d)
+ , m_start_time(second_clock::universal_time())
+ , m_read_time(second_clock::universal_time())
+ , m_timeout(d)
+ , m_completion_timeout(0)
+ , m_read_timeout(0)
+ , m_refs(0)
+ {}
+
+ void timeout_handler::set_timeout(int completion_timeout, int read_timeout)
+ {
+ m_completion_timeout = completion_timeout;
+ m_read_timeout = read_timeout;
+ m_start_time = second_clock::universal_time();
+ m_read_time = second_clock::universal_time();
+
+ m_timeout.expires_at(std::min(
+ m_read_time + seconds(m_read_timeout)
+ , m_start_time + seconds(m_completion_timeout)));
+ m_timeout.async_wait(bind(&timeout_handler::timeout_callback, self(), _1));
+ }
+
+ void timeout_handler::restart_read_timeout()
+ {
+ m_read_time = second_clock::universal_time();
+ }
+
+ void timeout_handler::cancel()
+ {
+ m_completion_timeout = 0;
+ m_timeout.cancel();
+ }
+
+ void timeout_handler::timeout_callback(asio::error const& error) try
+ {
+ if (error) return;
+ if (m_completion_timeout == 0) return;
+
+ ptime now(second_clock::universal_time());
+ time_duration receive_timeout = now - m_read_time;
+ time_duration completion_timeout = now - m_start_time;
+
+ if (m_read_timeout
+ < receive_timeout.total_seconds()
+ || m_completion_timeout
+ < completion_timeout.total_seconds())
+ {
+ on_timeout();
+ return;
+ }
+
+ m_timeout.expires_at(std::min(
+ m_read_time + seconds(m_read_timeout)
+ , m_start_time + seconds(m_completion_timeout)));
+ m_timeout.async_wait(bind(&timeout_handler::timeout_callback, self(), _1));
+ }
+ catch (std::exception& e)
+ {
+ assert(false);
+ }
+
+ tracker_connection::tracker_connection(
+ tracker_manager& man
+ , tracker_request req
+ , demuxer& d
+ , boost::weak_ptr<request_callback> r)
+ : timeout_handler(d)
+ , m_requester(r)
+ , m_man(man)
+ , m_req(req)
+ {}
+
+ request_callback& tracker_connection::requester()
+ {
+ boost::shared_ptr<request_callback> r = m_requester.lock();
+ assert(r);
+ return *r;
+ }
+
+ void tracker_connection::fail(int code, char const* msg)
+ {
+ if (has_requester()) requester().tracker_request_error(
+ m_req, code, msg);
+ close();
+ }
+
+ void tracker_connection::fail_timeout()
+ {
+ if (has_requester()) requester().tracker_request_timed_out(m_req);
+ close();
+ }
+
+ void tracker_connection::close()
+ {
+ cancel();
+ m_man.remove_request(this);
+ }
+
+ void tracker_manager::remove_request(tracker_connection const* c)
+ {
+ mutex_t::scoped_lock l(m_mutex);
+
+ tracker_connections_t::iterator i = std::find(m_connections.begin()
+ , m_connections.end(), boost::intrusive_ptr<const tracker_connection>(c));
+ if (i == m_connections.end()) return;
+
+ m_connections.erase(i);
+ }
+
+ tuple<std::string, std::string, int, std::string>
+ parse_url_components(std::string url)
+ {
+ std::string hostname; // hostname only
+ std::string protocol; // should be http
+ int port = 80;
+
+ // PARSE URL
+ std::string::iterator start = url.begin();
+ // remove white spaces in front of the url
+ while (start != url.end() && (*start == ' ' || *start == '\t'))
+ ++start;
+ std::string::iterator end
+ = std::find(url.begin(), url.end(), ':');
+ protocol = std::string(start, end);
+
+ if (end == url.end()) throw std::runtime_error("invalid url");
+ ++end;
+ if (end == url.end()) throw std::runtime_error("invalid url");
+ if (*end != '/') throw std::runtime_error("invalid url");
+ ++end;
+ if (end == url.end()) throw std::runtime_error("invalid url");
+ if (*end != '/') throw std::runtime_error("invalid url");
+ ++end;
+ start = end;
+
+ end = std::find(start, url.end(), '/');
+ std::string::iterator port_pos
+ = std::find(start, url.end(), ':');
+
+ if (port_pos < end)
+ {
+ hostname.assign(start, port_pos);
+ ++port_pos;
+ try
+ {
+ port = boost::lexical_cast<int>(std::string(port_pos, end));
+ }
+ catch(boost::bad_lexical_cast&)
+ {
+ throw std::runtime_error("invalid url: \"" + url
+ + "\", port number expected");
+ }
+ }
+ else
+ {
+ hostname.assign(start, end);
+ }
+
+ start = end;
+ return make_tuple(protocol, hostname, port
+ , std::string(start, url.end()));
+ }
+
+ void tracker_manager::queue_request(
+ demuxer& d
+ , tracker_request req
+ , std::string const& auth
+ , boost::weak_ptr<request_callback> c)
+ {
+ mutex_t::scoped_lock l(m_mutex);
+ assert(req.num_want >= 0);
+ if (req.event == tracker_request::stopped)
+ req.num_want = 0;
+
+ try
+ {
+ std::string protocol;
+ std::string hostname;
+ int port;
+ std::string request_string;
+
+ boost::tie(protocol, hostname, port, request_string)
+ = parse_url_components(req.url);
+
+ boost::intrusive_ptr<tracker_connection> con;
+
+ if (protocol == "http")
+ {
+ con = new http_tracker_connection(
+ d
+ , *this
+ , req
+ , hostname
+ , port
+ , request_string
+ , c
+ , m_settings
+ , auth);
+ }
+ else if (protocol == "udp")
+ {
+ con = new udp_tracker_connection(
+ d
+ , *this
+ , req
+ , hostname
+ , port
+ , c
+ , m_settings);
+ }
+ else
+ {
+ throw std::runtime_error("unkown protocol in tracker url");
+ }
+
+ m_connections.push_back(con);
+
+ if (con->has_requester()) con->requester().m_manager = this;
+ }
+ catch (std::exception& e)
+ {
+ if (boost::shared_ptr<request_callback> r = c.lock())
+ r->tracker_request_error(req, -1, e.what());
+ }
+ }
+
+ void tracker_manager::abort_all_requests()
+ {
+ // removes all connections from m_connections
+ // except those with a requester == 0 (since those are
+ // 'event=stopped'-requests)
+ mutex_t::scoped_lock l(m_mutex);
+
+ tracker_connections_t keep_connections;
+
+ for (tracker_connections_t::const_iterator i =
+ m_connections.begin(); i != m_connections.end(); ++i)
+ {
+ tracker_request const& req = (*i)->tracker_req();
+ if (req.event == tracker_request::stopped)
+ keep_connections.push_back(*i);
+ }
+
+ std::swap(m_connections, keep_connections);
+ }
+
+ bool tracker_manager::empty() const
+ {
+ mutex_t::scoped_lock l(m_mutex);
+ return m_connections.empty();
+ }
+
+}
diff --git a/library/udp_tracker_connection.cpp b/library/udp_tracker_connection.cpp
new file mode 100755
index 000000000..2a1e97d1a
--- /dev/null
+++ b/library/udp_tracker_connection.cpp
@@ -0,0 +1,522 @@
+/*
+
+Copyright (c) 2003, 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.
+
+*/
+
+#include <vector>
+#include <iostream>
+#include <cctype>
+#include <iomanip>
+#include <sstream>
+
+#include "zlib.h"
+
+#ifdef _MSC_VER
+#pragma warning(push, 1)
+#endif
+
+#include <boost/bind.hpp>
+#include <boost/lexical_cast.hpp>
+
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+#include "libtorrent/tracker_manager.hpp"
+#include "libtorrent/udp_tracker_connection.hpp"
+#include "libtorrent/io.hpp"
+
+namespace
+{
+ enum
+ {
+ udp_connection_retries = 4,
+ udp_announce_retries = 15,
+ udp_connect_timeout = 15,
+ udp_announce_timeout = 10,
+ udp_buffer_size = 2048
+ };
+}
+
+using namespace boost::posix_time;
+using boost::bind;
+using boost::lexical_cast;
+
+namespace libtorrent
+{
+
+ udp_tracker_connection::udp_tracker_connection(
+ demuxer& d
+ , tracker_manager& man
+ , tracker_request const& req
+ , std::string const& hostname
+ , unsigned short port
+ , boost::weak_ptr<request_callback> c
+ , session_settings const& stn)
+ : tracker_connection(man, req, d, c)
+ , m_man(man)
+ , m_name_lookup(d)
+ , m_port(port)
+ , m_transaction_id(0)
+ , m_connection_id(0)
+ , m_settings(stn)
+ , m_attempts(0)
+ {
+ m_socket.reset(new datagram_socket(d));
+ tcp::resolver::query q(hostname, "0");
+ m_name_lookup.async_resolve(q
+ , boost::bind(&udp_tracker_connection::name_lookup, self(), _1, _2));
+ set_timeout(m_settings.tracker_completion_timeout
+ , m_settings.tracker_receive_timeout);
+ }
+
+ void udp_tracker_connection::name_lookup(asio::error const& error
+ , tcp::resolver::iterator i) try
+ {
+ if (error == asio::error::operation_aborted) return;
+ if (!m_socket) return; // the operation was aborted
+ if (error || i == tcp::resolver::iterator())
+ {
+ fail(-1, error.what());
+ return;
+ }
+
+#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
+ if (has_requester()) requester().debug_log("udp tracker name lookup successful");
+#endif
+ restart_read_timeout();
+ m_target = udp::endpoint(i->endpoint().address(), m_port);
+ if (has_requester()) requester().m_tracker_address
+ = tcp::endpoint(i->endpoint().address(), m_port);
+ m_socket->connect(m_target);
+ send_udp_connect();
+ }
+ catch (std::exception& e)
+ {
+ fail(-1, e.what());
+ };
+
+ void udp_tracker_connection::on_timeout()
+ {
+ m_socket.reset();
+ m_name_lookup.cancel();
+ fail_timeout();
+ }
+
+ void udp_tracker_connection::send_udp_connect()
+ {
+#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
+ if (has_requester())
+ {
+ requester().debug_log("==> UDP_TRACKER_CONNECT ["
+ + lexical_cast<std::string>(tracker_req().info_hash) + "]");
+ }
+#endif
+ if (!m_socket) return; // the operation was aborted
+
+ char send_buf[16];
+ char* ptr = send_buf;
+
+ if (m_transaction_id == 0)
+ m_transaction_id = rand() ^ (rand() << 16);
+
+ // connection_id
+ detail::write_uint32(0x417, ptr);
+ detail::write_uint32(0x27101980, ptr);
+ // action (connect)
+ detail::write_int32(action_connect, ptr);
+ // transaction_id
+ detail::write_int32(m_transaction_id, ptr);
+
+ m_socket->send(asio::buffer((void*)send_buf, 16), 0);
+ ++m_attempts;
+ m_buffer.resize(udp_buffer_size);
+ m_socket->async_receive_from(asio::buffer(m_buffer), m_sender
+ , boost::bind(&udp_tracker_connection::connect_response, self(), _1, _2));
+ }
+
+ void udp_tracker_connection::connect_response(asio::error const& error
+ , std::size_t bytes_transferred) try
+ {
+ if (error == asio::error::operation_aborted) return;
+ if (!m_socket) return; // the operation was aborted
+ if (error)
+ {
+ fail(-1, error.what());
+ return;
+ }
+
+ if (m_target != m_sender)
+ {
+ // this packet was not received from the tracker
+ m_socket->async_receive_from(asio::buffer(m_buffer), m_sender
+ , boost::bind(&udp_tracker_connection::connect_response, self(), _1, _2));
+ return;
+ }
+
+ if (bytes_transferred >= udp_buffer_size)
+ {
+ fail(-1, "udp response too big");
+ return;
+ }
+
+ if (bytes_transferred < 8)
+ {
+ fail(-1, "got a message with size < 8");
+ return;
+ }
+
+ restart_read_timeout();
+
+ const char* ptr = &m_buffer[0];
+ int action = detail::read_int32(ptr);
+ int transaction = detail::read_int32(ptr);
+
+ if (action == action_error)
+ {
+ fail(-1, std::string(ptr, bytes_transferred - 8).c_str());
+ return;
+ }
+
+ if (action != action_connect)
+ {
+ fail(-1, "invalid action in connect reply");
+ return;
+ }
+
+ if (m_transaction_id != transaction)
+ {
+ fail(-1, "incorrect transaction id");
+ return;
+ }
+
+ if (bytes_transferred < 16)
+ {
+ fail(-1, "udp_tracker_connection: "
+ "got a message with size < 16");
+ return;
+ }
+ // reset transaction
+ m_transaction_id = 0;
+ m_attempts = 0;
+ m_connection_id = detail::read_int64(ptr);
+
+#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
+ if (has_requester())
+ {
+ requester().debug_log("<== UDP_TRACKER_CONNECT_RESPONSE ["
+ + lexical_cast<std::string>(m_connection_id) + "]");
+ }
+#endif
+
+ if (tracker_req().kind == tracker_request::announce_request)
+ send_udp_announce();
+ else if (tracker_req().kind == tracker_request::scrape_request)
+ send_udp_scrape();
+ }
+ catch (std::exception& e)
+ {
+ fail(-1, e.what());
+ }
+
+ void udp_tracker_connection::send_udp_announce()
+ {
+ if (m_transaction_id == 0)
+ m_transaction_id = rand() ^ (rand() << 16);
+
+ if (!m_socket) return; // the operation was aborted
+
+ std::vector<char> buf;
+ std::back_insert_iterator<std::vector<char> > out(buf);
+
+ tracker_request const& req = tracker_req();
+
+ // connection_id
+ detail::write_int64(m_connection_id, out);
+ // action (announce)
+ detail::write_int32(action_announce, out);
+ // transaction_id
+ detail::write_int32(m_transaction_id, out);
+ // info_hash
+ std::copy(req.info_hash.begin(), req.info_hash.end(), out);
+ // peer_id
+ std::copy(req.pid.begin(), req.pid.end(), out);
+ // downloaded
+ detail::write_int64(req.downloaded, out);
+ // left
+ detail::write_int64(req.left, out);
+ // uploaded
+ detail::write_int64(req.uploaded, out);
+ // event
+ detail::write_int32(req.event, out);
+ // ip address
+ detail::write_int32(0, out);
+ // key
+ detail::write_int32(req.key, out);
+ // num_want
+ detail::write_int32(req.num_want, out);
+ // port
+ detail::write_uint16(req.listen_port, out);
+ // extensions
+ detail::write_uint16(0, out);
+
+#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
+ if (has_requester())
+ {
+ requester().debug_log("==> UDP_TRACKER_ANNOUNCE ["
+ + lexical_cast<std::string>(req.info_hash) + "]");
+ }
+#endif
+
+ m_socket->send(asio::buffer(buf), 0);
+ ++m_attempts;
+
+ m_socket->async_receive_from(asio::buffer(m_buffer), m_sender
+ , bind(&udp_tracker_connection::announce_response, self(), _1, _2));
+ }
+
+ void udp_tracker_connection::send_udp_scrape()
+ {
+ if (m_transaction_id == 0)
+ m_transaction_id = rand() ^ (rand() << 16);
+
+ if (!m_socket) return; // the operation was aborted
+
+ std::vector<char> buf;
+ std::back_insert_iterator<std::vector<char> > out(buf);
+
+ // connection_id
+ detail::write_int64(m_connection_id, out);
+ // action (scrape)
+ detail::write_int32(action_scrape, out);
+ // transaction_id
+ detail::write_int32(m_transaction_id, out);
+ // info_hash
+ std::copy(tracker_req().info_hash.begin(), tracker_req().info_hash.end(), out);
+
+ m_socket->send(asio::buffer(&buf[0], buf.size()), 0);
+ ++m_attempts;
+
+ m_socket->async_receive_from(asio::buffer(m_buffer), m_sender
+ , bind(&udp_tracker_connection::scrape_response, self(), _1, _2));
+ }
+
+ void udp_tracker_connection::announce_response(asio::error const& error
+ , std::size_t bytes_transferred) try
+ {
+ if (error == asio::error::operation_aborted) return;
+ if (!m_socket) return; // the operation was aborted
+ if (error)
+ {
+ fail(-1, error.what());
+ return;
+ }
+
+ if (m_target != m_sender)
+ {
+ // this packet was not received from the tracker
+ m_socket->async_receive_from(asio::buffer(m_buffer), m_sender
+ , bind(&udp_tracker_connection::connect_response, self(), _1, _2));
+ return;
+ }
+
+ if (bytes_transferred >= udp_buffer_size)
+ {
+ fail(-1, "udp response too big");
+ return;
+ }
+
+ if (bytes_transferred < 8)
+ {
+ fail(-1, "got a message with size < 8");
+ return;
+ }
+
+ restart_read_timeout();
+ char* buf = &m_buffer[0];
+ int action = detail::read_int32(buf);
+ int transaction = detail::read_int32(buf);
+
+ if (transaction != m_transaction_id)
+ {
+ fail(-1, "incorrect transaction id");
+ return;
+ }
+
+ if (action == action_error)
+ {
+ fail(-1, std::string(buf, bytes_transferred - 8).c_str());
+ return;
+ }
+
+ if (action != action_announce)
+ {
+ fail(-1, "invalid action in announce response");
+ return;
+ }
+
+ if (bytes_transferred < 20)
+ {
+ fail(-1, "got a message with size < 20");
+ return;
+ }
+
+ int interval = detail::read_int32(buf);
+ int incomplete = detail::read_int32(buf);
+ int complete = detail::read_int32(buf);
+ int num_peers = (bytes_transferred - 20) / 6;
+ if ((bytes_transferred - 20) % 6 != 0)
+ {
+ fail(-1, "invalid udp tracker response length");
+ return;
+ }
+
+#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
+ if (has_requester())
+ {
+ requester().debug_log("<== UDP_TRACKER_ANNOUNCE_RESPONSE");
+ }
+#endif
+
+ if (!has_requester())
+ {
+ m_man.remove_request(this);
+ return;
+ }
+
+ std::vector<peer_entry> peer_list;
+ for (int i = 0; i < num_peers; ++i)
+ {
+ peer_entry e;
+ std::stringstream s;
+ s << (int)detail::read_uint8(buf) << ".";
+ s << (int)detail::read_uint8(buf) << ".";
+ s << (int)detail::read_uint8(buf) << ".";
+ s << (int)detail::read_uint8(buf);
+ e.ip = s.str();
+ e.port = detail::read_uint16(buf);
+ e.pid.clear();
+ peer_list.push_back(e);
+ }
+
+ requester().tracker_response(tracker_req(), peer_list, interval
+ , complete, incomplete);
+
+ m_man.remove_request(this);
+ return;
+ }
+ catch (std::exception& e)
+ {
+ fail(-1, e.what());
+ }; // msvc 7.1 seems to require this
+
+ void udp_tracker_connection::scrape_response(asio::error const& error
+ , std::size_t bytes_transferred) try
+ {
+ if (error == asio::error::operation_aborted) return;
+ if (!m_socket) return; // the operation was aborted
+ if (error)
+ {
+ fail(-1, error.what());
+ return;
+ }
+
+ if (m_target != m_sender)
+ {
+ // this packet was not received from the tracker
+ m_socket->async_receive_from(asio::buffer(m_buffer), m_sender
+ , bind(&udp_tracker_connection::connect_response, self(), _1, _2));
+ return;
+ }
+
+ if (bytes_transferred >= udp_buffer_size)
+ {
+ fail(-1, "udp response too big");
+ return;
+ }
+
+ if (bytes_transferred < 8)
+ {
+ fail(-1, "got a message with size < 8");
+ return;
+ }
+
+ restart_read_timeout();
+ char* buf = &m_buffer[0];
+ int action = detail::read_int32(buf);
+ int transaction = detail::read_int32(buf);
+
+ if (transaction != m_transaction_id)
+ {
+ fail(-1, "incorrect transaction id");
+ return;
+ }
+
+ if (action == action_error)
+ {
+ fail(-1, std::string(buf, bytes_transferred - 8).c_str());
+ return;
+ }
+
+ if (action != action_scrape)
+ {
+ fail(-1, "invalid action in announce response");
+ return;
+ }
+
+ if (bytes_transferred < 20)
+ {
+ fail(-1, "got a message with size < 20");
+ return;
+ }
+
+ int complete = detail::read_int32(buf);
+ /*int downloaded = */detail::read_int32(buf);
+ int incomplete = detail::read_int32(buf);
+
+ if (!has_requester())
+ {
+ m_man.remove_request(this);
+ return;
+ }
+
+ std::vector<peer_entry> peer_list;
+ requester().tracker_response(tracker_req(), peer_list, 0
+ , complete, incomplete);
+
+ m_man.remove_request(this);
+ }
+ catch (std::exception& e)
+ {
+ fail(-1, e.what());
+ }
+
+}
+
diff --git a/library/web_peer_connection.cpp b/library/web_peer_connection.cpp
new file mode 100755
index 000000000..5be9610f4
--- /dev/null
+++ b/library/web_peer_connection.cpp
@@ -0,0 +1,455 @@
+/*
+
+Copyright (c) 2003, 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.
+
+*/
+
+#include <vector>
+#include <iostream>
+#include <iomanip>
+#include <limits>
+#include <boost/bind.hpp>
+#include <sstream>
+
+#include "libtorrent/web_peer_connection.hpp"
+#include "libtorrent/session.hpp"
+#include "libtorrent/identify_client.hpp"
+#include "libtorrent/entry.hpp"
+#include "libtorrent/bencode.hpp"
+#include "libtorrent/alert_types.hpp"
+#include "libtorrent/invariant_check.hpp"
+#include "libtorrent/io.hpp"
+#include "libtorrent/version.hpp"
+#include "libtorrent/aux_/session_impl.hpp"
+
+using namespace boost::posix_time;
+using boost::bind;
+using boost::shared_ptr;
+using libtorrent::aux::session_impl;
+
+namespace libtorrent
+{
+ web_peer_connection::web_peer_connection(
+ session_impl& ses
+ , boost::weak_ptr<torrent> t
+ , boost::shared_ptr<stream_socket> s
+ , tcp::endpoint const& remote
+ , std::string const& url)
+ : peer_connection(ses, t, s, remote)
+ , m_url(url)
+ , m_first_request(true)
+ {
+ INVARIANT_CHECK;
+
+ m_max_out_request_queue = ses.settings().urlseed_pipeline_size;
+
+ // since this is a web seed, change the timeout
+ // according to the settings.
+ set_timeout(ses.settings().urlseed_timeout);
+#ifdef TORRENT_VERBOSE_LOGGING
+ (*m_logger) << "*** web_peer_connection\n";
+#endif
+
+ std::string protocol;
+ boost::tie(protocol, m_host, m_port, m_path)
+ = parse_url_components(url);
+
+ m_server_string = "URL seed @ ";
+ m_server_string += m_host;
+ }
+
+ web_peer_connection::~web_peer_connection()
+ {}
+
+ boost::optional<piece_block_progress>
+ web_peer_connection::downloading_piece_progress() const
+ {
+ if (!m_parser.header_finished() || m_requests.empty())
+ return boost::optional<piece_block_progress>();
+
+ boost::shared_ptr<torrent> t = associated_torrent().lock();
+ assert(t);
+
+ int body_start = m_parser.body_start();
+ buffer::const_interval recv_buffer = receive_buffer();
+ assert(body_start <= recv_buffer.left());
+ piece_block_progress ret;
+
+ ret.piece_index = m_requests.front().piece;
+ ret.block_index = m_requests.front().start / t->block_size();
+ ret.bytes_downloaded = recv_buffer.left() - body_start;
+ ret.full_block_bytes = m_requests.front().length;
+ return ret;
+ }
+
+ void web_peer_connection::on_connected()
+ {
+ boost::shared_ptr<torrent> t = associated_torrent().lock();
+ assert(t);
+
+ // this is always a seed
+ incoming_bitfield(std::vector<bool>(
+ t->torrent_file().num_pieces(), true));
+ // it is always possible to request pieces
+ incoming_unchoke();
+
+ reset_recv_buffer(512*1024+1024);
+ }
+
+ void web_peer_connection::write_request(peer_request const& r)
+ {
+ INVARIANT_CHECK;
+
+ boost::shared_ptr<torrent> t = associated_torrent().lock();
+ assert(t);
+
+ assert(t->valid_metadata());
+
+ bool single_file_request = false;
+ if (!m_path.empty() && m_path[m_path.size() - 1] != '/')
+ single_file_request = true;
+
+ torrent_info const& info = t->torrent_file();
+
+ std::string request;
+
+ m_requests.push_back(r);
+
+ bool using_proxy = false;
+ if (!m_ses.settings().proxy_ip.empty())
+ using_proxy = true;
+
+ if (single_file_request)
+ {
+ request += "GET ";
+ if (using_proxy) request += m_url;
+ else request += escape_path(m_path.c_str(), m_path.length());
+ request += " HTTP/1.1\r\n";
+ request += "Host: ";
+ request += m_host;
+ if (m_first_request)
+ {
+ request += "\r\nUser-Agent: ";
+ request += m_ses.settings().user_agent;
+ }
+ if (using_proxy && !m_ses.settings().proxy_login.empty())
+ {
+ request += "\r\nProxy-Authorization: Basic ";
+ request += base64encode(m_ses.settings().proxy_login + ":"
+ + m_ses.settings().proxy_password);
+ }
+ if (using_proxy)
+ {
+ request += "\r\nProxy-Connection: keep-alive";
+ }
+ request += "\r\nRange: bytes=";
+ request += boost::lexical_cast<std::string>(r.piece
+ * info.piece_length() + r.start);
+ request += "-";
+ request += boost::lexical_cast<std::string>(r.piece
+ * info.piece_length() + r.start + r.length - 1);
+ if (m_first_request || using_proxy)
+ request += "\r\nConnection: keep-alive";
+ request += "\r\n\r\n";
+ m_first_request = false;
+ m_file_requests.push_back(0);
+ }
+ else
+ {
+ std::vector<file_slice> files = info.map_block(r.piece, r.start
+ , r.length);
+
+ for (std::vector<file_slice>::iterator i = files.begin();
+ i != files.end(); ++i)
+ {
+ file_slice const& f = *i;
+
+ request += "GET ";
+ if (using_proxy)
+ {
+ request += m_url;
+ std::string path = info.file_at(f.file_index).path.string();
+ request += escape_path(path.c_str(), path.length());
+ }
+ else
+ {
+ std::string path = m_path;
+ path += info.file_at(f.file_index).path.string();
+ request += escape_path(path.c_str(), path.length());
+ }
+ request += " HTTP/1.1\r\n";
+ request += "Host: ";
+ request += m_host;
+ if (m_first_request)
+ {
+ request += "\r\nUser-Agent: ";
+ request += m_ses.settings().user_agent;
+ }
+ if (using_proxy && !m_ses.settings().proxy_login.empty())
+ {
+ request += "\r\nProxy-Authorization: Basic ";
+ request += base64encode(m_ses.settings().proxy_login + ":"
+ + m_ses.settings().proxy_password);
+ }
+ if (using_proxy)
+ {
+ request += "\r\nProxy-Connection: keep-alive";
+ }
+ request += "\r\nRange: bytes=";
+ request += boost::lexical_cast<std::string>(f.offset);
+ request += "-";
+ request += boost::lexical_cast<std::string>(f.offset + f.size - 1);
+ if (m_first_request || using_proxy)
+ request += "\r\nConnection: keep-alive";
+ request += "\r\n\r\n";
+ m_first_request = false;
+ m_file_requests.push_back(f.file_index);
+ }
+ }
+
+ send_buffer(request.c_str(), request.c_str() + request.size());
+ }
+
+ // --------------------------
+ // RECEIVE DATA
+ // --------------------------
+
+ // throws exception when the client should be disconnected
+ void web_peer_connection::on_receive(const asio::error& error
+ , std::size_t bytes_transferred)
+ {
+ INVARIANT_CHECK;
+
+ if (error)
+ {
+ return;
+ }
+
+ boost::shared_ptr<torrent> t = associated_torrent().lock();
+ assert(t);
+
+ incoming_piece_fragment();
+
+ for (;;)
+ {
+ buffer::const_interval recv_buffer = receive_buffer();
+ int payload;
+ int protocol;
+ boost::tie(payload, protocol) = m_parser.incoming(recv_buffer);
+ m_statistics.received_bytes(payload, protocol);
+
+ if (m_parser.status_code() != 206 && m_parser.status_code() != -1)
+ {
+ // we should not try this server again.
+ t->remove_url_seed(m_url);
+ if (m_parser.status_code() == 404)
+ throw std::runtime_error("File not found on server");
+ throw std::runtime_error("HTTP server does not support byte range requests");
+ }
+
+ if (!m_parser.finished()) break;
+
+ std::string server_version = m_parser.header<std::string>("Server");
+ if (!server_version.empty())
+ {
+ m_server_string = "URL seed @ ";
+ m_server_string += m_host;
+ m_server_string += " (";
+ m_server_string += server_version;
+ m_server_string += ")";
+ }
+
+ std::stringstream range_str(m_parser.header<std::string>("Content-Range"));
+ size_type range_start;
+ size_type range_end;
+ char dummy;
+ std::string bytes;
+ range_str >> bytes >> range_start >> dummy >> range_end;
+ if (!range_str)
+ {
+ // we should not try this server again.
+ t->remove_url_seed(m_url);
+ throw std::runtime_error("invalid range in HTTP response: " + range_str.str());
+ }
+ // the http range is inclusive
+ range_end++;
+
+ torrent_info const& info = t->torrent_file();
+
+ if (m_requests.empty() || m_file_requests.empty())
+ throw std::runtime_error("unexpected HTTP response");
+
+ int file_index = m_file_requests.front();
+ m_file_requests.pop_front();
+
+ peer_request r = info.map_file(file_index, range_start
+ , range_end - range_start);
+
+ buffer::const_interval http_body = m_parser.get_body();
+
+ if (r == m_requests.front())
+ {
+ m_requests.pop_front();
+ incoming_piece(r, http_body.begin);
+ cut_receive_buffer(http_body.end - recv_buffer.begin, 512*1024+1024);
+ return;
+ }
+
+ if (!m_piece.empty())
+ {
+ // this is not the first partial request we get
+ if (m_intermediate_piece.start + m_intermediate_piece.length != r.start
+ || m_intermediate_piece.piece != r.piece)
+ {
+ throw std::runtime_error("invalid range in HTTP response");
+ }
+ }
+ else
+ {
+ // this is the first part of a partial request
+ if (r.start != m_requests.front().start
+ || r.piece != m_requests.front().piece)
+ {
+ throw std::runtime_error("invalid range in HTTP response");
+ }
+ m_intermediate_piece.piece = r.piece;
+ m_intermediate_piece.start = r.start;
+ m_intermediate_piece.length = 0;
+ }
+
+ m_piece.reserve(info.piece_length());
+ std::copy(http_body.begin, http_body.end, back_inserter(m_piece));
+ m_intermediate_piece.length += r.length;
+ if (m_intermediate_piece.length == m_requests.front().length)
+ {
+ assert(m_requests.front() == m_intermediate_piece);
+ assert(int(m_piece.size()) == m_intermediate_piece.length);
+ m_requests.pop_front();
+ incoming_piece(m_intermediate_piece, &m_piece[0]);
+ m_piece.clear();
+ }
+ else if (m_intermediate_piece.length > m_requests.front().length)
+ {
+ throw std::runtime_error("too large HTTP response body");
+ }
+
+ cut_receive_buffer(http_body.end - recv_buffer.begin, 512*1024+1024);
+ }
+ }
+
+ // --------------------------
+ // SEND DATA
+ // --------------------------
+
+ void web_peer_connection::get_peer_info(peer_info& p) const
+ {
+ assert(!associated_torrent().expired());
+
+ p.down_speed = statistics().download_rate();
+ p.up_speed = statistics().upload_rate();
+ p.payload_down_speed = statistics().download_payload_rate();
+ p.payload_up_speed = statistics().upload_payload_rate();
+ p.pid = pid();
+ p.ip = remote();
+
+ p.total_download = statistics().total_payload_download();
+ p.total_upload = statistics().total_payload_upload();
+
+ if (m_ul_bandwidth_quota.given == std::numeric_limits<int>::max())
+ p.upload_limit = -1;
+ else
+ p.upload_limit = m_ul_bandwidth_quota.given;
+
+ if (m_dl_bandwidth_quota.given == std::numeric_limits<int>::max())
+ p.download_limit = -1;
+ else
+ p.download_limit = m_dl_bandwidth_quota.given;
+
+ p.load_balancing = total_free_upload();
+
+ p.download_queue_length = (int)download_queue().size();
+ p.upload_queue_length = (int)upload_queue().size();
+
+ if (boost::optional<piece_block_progress> ret = downloading_piece_progress())
+ {
+ p.downloading_piece_index = ret->piece_index;
+ p.downloading_block_index = ret->block_index;
+ p.downloading_progress = ret->bytes_downloaded;
+ p.downloading_total = ret->full_block_bytes;
+ }
+ else
+ {
+ p.downloading_piece_index = -1;
+ p.downloading_block_index = -1;
+ p.downloading_progress = 0;
+ p.downloading_total = 0;
+ }
+
+ p.flags = 0;
+ if (is_interesting()) p.flags |= peer_info::interesting;
+ if (is_choked()) p.flags |= peer_info::choked;
+ if (is_peer_interested()) p.flags |= peer_info::remote_interested;
+ if (has_peer_choked()) p.flags |= peer_info::remote_choked;
+ if (is_local()) p.flags |= peer_info::local_connection;
+ if (!is_connecting() && m_server_string.empty())
+ p.flags |= peer_info::handshake;
+ if (is_connecting() && !is_queued()) p.flags |= peer_info::connecting;
+ if (is_queued()) p.flags |= peer_info::queued;
+
+ p.pieces = get_bitfield();
+ p.seed = is_seed();
+
+ p.client = m_server_string;
+ p.connection_type = peer_info::web_seed;
+ }
+
+ // throws exception when the client should be disconnected
+ void web_peer_connection::on_sent(asio::error const& error
+ , std::size_t bytes_transferred)
+ {
+ INVARIANT_CHECK;
+
+ if (error) return;
+ m_statistics.sent_bytes(0, bytes_transferred);
+ }
+
+
+#ifndef NDEBUG
+ void web_peer_connection::check_invariant() const
+ {
+/*
+ assert(m_num_pieces == std::count(
+ m_have_piece.begin()
+ , m_have_piece.end()
+ , true));
+*/ }
+#endif
+
+}
+